From 83f9007401c36bd3775bc8c6ba0a4bba77df52d1 Mon Sep 17 00:00:00 2001 From: Ladd Hoffman Date: Wed, 11 Jan 2023 23:04:12 -0600 Subject: [PATCH] Prevent reputation from decreasing below zero --- forum-network/notes/rep.md | 50 ++++++++++++++++++++ forum-network/src/classes/forum.js | 47 +++++++++--------- forum-network/src/classes/validation-pool.js | 5 +- forum-network/src/index.html | 17 +++++-- forum-network/src/params.js | 2 +- forum-network/src/tests/forum.html | 4 +- 6 files changed, 94 insertions(+), 31 deletions(-) create mode 100644 forum-network/notes/rep.md diff --git a/forum-network/notes/rep.md b/forum-network/notes/rep.md new file mode 100644 index 0000000..8e5f445 --- /dev/null +++ b/forum-network/notes/rep.md @@ -0,0 +1,50 @@ +Reputation Tokens + +Minting + +Suppose it's possible to mint a reputation token. +Say we have a contract that keeps track of all the reputation tokens. +Suppose the reputation contract implements ERC720 (NFT). +Assume a validation pool always corresponds to a post. +A single token could be minted for each validation pool. +That token could be subdivided so that each winning voter gets some. +Perhaps voters get tokens that are specifically identifiable as governance reputation tokens. +Then the main token can be awarded to the post author. +Each token should carry a specific value. +The forum will update the value of the token for the post author as well as posts affected by its chain of references. + +Then, when participants wish to stake reputation (for voting or for availability), +they must specify the amount and the token address which carries that reputation. +The token should probably then lock that reputation, preventing it from being staked concurrently for another purpose. + +Perhaps our interface can allow staking reputation from multiple tokens at the same time. +And/or we can provide a mechanism for consolidating tokens. + +Or maybe, if reputation is staked via a given token, then the reputation awards should go to that same token. +In that case, when should new tokens be minted? + +Maybe a token should be minted for each validation pool, but not subdivided. +Voter rewards can just add value to the existing tokens from which reputation was staked. + +Maybe a new token should only be minted if the author did not provide a token from which to stake reputation on their own post. +This supports the case of a new author earning their first reputation. +In that case the author may need to pay a fee to buy in to the DAO. +Or perhaps they can be sponsored by one or more existing reputation token holders. +Existing reputation holders could grant some reputation to a new member. +Perhaps there could be a contract that allows sponsoring a new member, such that whatever fee is given, +that fee will automatically be repaid from the new member's earnings, before the new member starts receiving their share of earnings. +This could be a multi-party action, or could just be a generic operation that can be performed multiple times. + +However, this effectively allows buying reputation, which goes against the core concept of reputation as evidence of performance. + +It could make more sense for new members to submit some sort of petition, i.e. to make a post. + +Maybe rather than submitting fees, existing members can grant some of their own reputation to a new member, and receive some sort of compensation if the new member does well. + +So far the only workable model seems to be the idea that a new member must submit a post along with a fee, in order to be considered, and if the post is approved, they gain their first reputation. +The amount of this fee can be up to the applicant, and/or can be regulated by soft protocols within the DAO. + +If this is the only scenario in which new rep tokens are minted, and from then on their value is updated as a result of each validation pool, +then we probably want each token to store information about the history of its value. +At a minimum this can be a list where each item includes the identifier of the corresponding validation pool, and the resulting increment/decrement of token value. +Each validation pool can then keep a record of the reputation staked by each voter, and the identifier of the corresponding post. diff --git a/forum-network/src/classes/forum.js b/forum-network/src/classes/forum.js index dedaded..0020392 100644 --- a/forum-network/src/classes/forum.js +++ b/forum-network/src/classes/forum.js @@ -83,44 +83,47 @@ export class Forum extends Actor { const post = this.getPost(postId); post.setStatus('Validated'); - const rewards = await this.propagateValue(pool, post, initialValue); + + // Compute rewards + const rewards = new Map(); + await this.propagateValue(rewards, pool, post, initialValue); // Apply computed rewards for (const [id, value] of rewards) { bench.reputations.addTokens(id, value); } } - async propagateValue(fromActor, post, increment, depth = 0) { + async propagateValue(rewards, fromActor, post, increment, depth = 0) { if (params.referenceChainLimit >= 0 && depth > params.referenceChainLimit) { return []; } this.actions.propagateValue.log(fromActor, post, `(${increment})`); - // Apply leaching value - const adjustedIncrement = increment * (1 - params.leachingValue * post.totalCitationWeight); - - const rewards = new Map(); - const addReward = (id, value) => rewards.set(id, (rewards.get(id) ?? 0) + value); - const addRewards = (r) => { - for (const [id, value] of r) { - addReward(id, value); - } - }; - - // Increment the value of the post - await this.setPostValue(post, post.value + adjustedIncrement); - - // Award reputation to post author - console.log(`reward for post author ${post.authorPublicKey}`, adjustedIncrement); - addReward(post.authorPublicKey, adjustedIncrement); - // Recursively distribute reputation to citations, according to weights + let downstreamRefund = 0; for (const { postId: citedPostId, weight } of post.citations) { const citedPost = this.getPost(citedPostId); - addRewards(await this.propagateValue(post, citedPost, weight * increment, depth + 1)); + downstreamRefund += await this.propagateValue(rewards, post, citedPost, weight * increment, depth + 1); } - return rewards; + // Apply leaching value + const adjustedIncrement = increment * (1 - params.leachingValue * post.totalCitationWeight) + downstreamRefund; + + // Prevent value from decreasing below zero + const rawNewValue = post.value + adjustedIncrement; + const newValue = Math.max(0, rawNewValue); + const upstreamRefund = rawNewValue < 0 ? rawNewValue : 0; + const appliedIncrement = newValue - post.value; + + // Increment the value of the post + await this.setPostValue(post, newValue); + + // Award reputation to post author + console.log(`reward for post author ${post.authorPublicKey}`, appliedIncrement); + + rewards.set(post.authorPublicKey, appliedIncrement); + + return upstreamRefund; } } diff --git a/forum-network/src/classes/validation-pool.js b/forum-network/src/classes/validation-pool.js index ff13f39..63cb33f 100644 --- a/forum-network/src/classes/validation-pool.js +++ b/forum-network/src/classes/validation-pool.js @@ -224,7 +224,7 @@ export class ValidationPool extends Actor { const tokensForWinners = getTotalStaked(!votePasses); const winningVotes = this.listVotes(({ position, isSystemVote }) => position === votePasses && !isSystemVote); - // Reward the winning voters, in proportion to their stakes + // Compute rewards for the winning voters, in proportion to their stakes const rewards = new Map(); for (const [signingPublicKey, { stake }] of winningVotes) { const { reputationPublicKey } = this.voters.get(signingPublicKey); @@ -233,6 +233,7 @@ export class ValidationPool extends Actor { } const authorReputationPublicKey = this.voters.get(this.authorSigningPublicKey).reputationPublicKey; + // Distribute awards to voters other than the author for (const [id, value] of rewards) { if (id !== authorReputationPublicKey) { @@ -241,6 +242,8 @@ export class ValidationPool extends Actor { } } + // TODO: revoke staked reputation from losing voters + if (votePasses) { // Distribute awards to author via the forum const tokensForAuthor = this.tokensMinted * params.stakeForAuthor + rewards.get(authorReputationPublicKey); diff --git a/forum-network/src/index.html b/forum-network/src/index.html index 96eafef..faafb53 100644 --- a/forum-network/src/index.html +++ b/forum-network/src/index.html @@ -5,15 +5,22 @@

Tests

+

Primary

+ +

Secondary

+ +

Tertiary

diff --git a/forum-network/src/params.js b/forum-network/src/params.js index 9f7b871..a9877b7 100644 --- a/forum-network/src/params.js +++ b/forum-network/src/params.js @@ -21,7 +21,7 @@ const params = { /* Forum parameters */ initialPostValue: () => 1, // q1 revaluationLimit: 1, // q2 - maxPropagationDepth: 3, // q3 + referenceChainLimit: 3, // q3 leachingValue: 1, // q4 }; diff --git a/forum-network/src/tests/forum.html b/forum-network/src/tests/forum.html index 8fa9048..176e9c1 100644 --- a/forum-network/src/tests/forum.html +++ b/forum-network/src/tests/forum.html @@ -122,9 +122,9 @@ forum, new PostContent({ hello: "y'all" }) .setTitle("Post 3") - .addCitation(postId2, 0.5), + .addCitation(postId2, -0.5), { - fee: 10, + fee: 100, duration: 1000, tokenLossRatio: 1, }