Merge branch 'dev' into 'main'

Move params to validation pool

See merge request dao-governance-framework/science-publishing-dao!6
This commit is contained in:
Ladd Hoffman 2023-04-23 01:25:39 +00:00
commit ce4f78aa97
9 changed files with 129 additions and 77 deletions

View File

@ -1 +1,12 @@
Each DAO needs to allocate some of its incoming fees to incentivize development.
---
Well, the above is not exactly correct. The incentive for development is to earn reputation.
However what is true is that a DAO may need to leverage some reputation toward governance-related actions.
For example gradually changing the weight of some posts bit by bit.
This can be accomplished by a work smart contract that allocates a fixed percentage of minted reputation in the desired way.
If no reputation is needed for such initiatives at a given time, it can be burned instead, to preserve fairness.
---

View File

@ -0,0 +1,39 @@
We are trying to build the reputation mechanisms for the X-prize project.
This project has the loosely stated goal of building tools for communities, but there is an expectation that this will use "MVPR."
My thinking is that the following basic features are needed of reputation:
- Bootstrap initial members
- Members can onboard new members
- Members can perform actions that affect each other's reputation
- Members can vote to prioritize content
- Public can view prioritized content
We need to provide detailed workflows for each of these.
# Bootstrap initial members
# Members can onboard new members
- A non-member is equivalent to a member with no reputation.
- Non-members can post and have their posts reviewed in order to gain reputation.
# Members can perform actions that affect each other's reputation
- Members can positively/negatively cite posts (what kind of posts?) by other members
- Stake reputation on these posts
- Validation pool determines the outcome - poster may gain or lose reputation,
- strength of effect can be influenced by ratio of upvotes
- Upvote = Post?
- Upvote = Vote in validation pool?
- Multiple types of reputation?
- Correctness
- "Goodness"
- Humor
# Members can vote to prioritize content
# Public can view prioritized content

View File

@ -1,4 +1,3 @@
import params from '../../params.js';
import { Forum } from './forum.js';
import { ReputationTokenContract } from '../reputation/reputation-token.js';
import { ValidationPool } from './validation-pool.js';
@ -34,11 +33,11 @@ export class DAO extends Actor {
Array.from(this.validationPools.values());
}
listActiveVoters() {
listActiveVoters({ activeVoterThreshold } = {}) {
return Array.from(this.experts.values()).filter((voter) => {
const hasVoted = !!voter.dateLastVote;
const withinThreshold = !params.activeVoterThreshold
|| new Date() - voter.dateLastVote >= params.activeVoterThreshold;
const withinThreshold = !activeVoterThreshold
|| new Date() - voter.dateLastVote >= activeVoterThreshold;
return hasVoted && withinThreshold;
});
}

View File

@ -1,7 +1,6 @@
import { WDAG } from '../supporting/wdag.js';
import { Action } from '../display/action.js';
import { Actor } from '../display/actor.js';
import params from '../../params.js';
import { ReputationHolder } from '../reputation/reputation-holder.js';
import { displayNumber } from '../../util/helpers.js';
import {
@ -21,25 +20,6 @@ class Post extends Actor {
this.authors = postContent.authors;
this.citations = postContent.citations;
this.title = postContent.title;
const leachingTotal = this.citations
.filter(({ weight }) => weight < 0)
.reduce((total, { weight }) => total += -weight, 0);
const donationTotal = this.citations
.filter(({ weight }) => weight > 0)
.reduce((total, { weight }) => total += weight, 0);
// TODO: Move evaluation of these parameters to Validation Pool
if (leachingTotal > params.revaluationLimit) {
throw new Error('Post leaching total exceeds revaluation limit '
+ `(${leachingTotal} > ${params.revaluationLimit})`);
}
if (donationTotal > params.revaluationLimit) {
throw new Error('Post donation total exceeds revaluation limit '
+ `(${donationTotal} > ${params.revaluationLimit})`);
}
if (this.citations.some(({ weight }) => Math.abs(weight) > params.revaluationLimit)) {
throw new Error(`Each citation magnitude must not exceed revaluation limit ${params.revaluationLimit}`);
}
}
getLabel() {
@ -110,7 +90,7 @@ export class Forum extends ReputationHolder {
// getContract(type) { }
async onValidate({
pool, postId, tokenId,
pool, postId, tokenId, referenceChainLimit, leachingValue,
}) {
console.log('onValidate', { pool, postId, tokenId });
const initialValue = this.dao.reputation.valueOf(tokenId);
@ -160,7 +140,12 @@ export class Forum extends ReputationHolder {
// Compute reputation rewards
await this.propagateValue(
{ to: postVertex, from: { data: pool } },
{ rewardsAccumulator, increment: initialValue },
{
rewardsAccumulator,
increment: initialValue,
referenceChainLimit,
leachingValue,
},
);
// Apply computed rewards to update values of tokens
@ -200,17 +185,22 @@ export class Forum extends ReputationHolder {
* @param {Object} opaqueData
*/
async propagateValue(edge, {
rewardsAccumulator, increment, depth = 0, initialNegative = false,
rewardsAccumulator,
increment,
depth = 0,
initialNegative = false,
referenceChainLimit,
leachingValue
}) {
const postVertex = edge.to;
const post = postVertex.data;
this.actions.propagate.log(edge.from.data, post, `(${increment})`);
if (!!params.referenceChainLimit && depth > params.referenceChainLimit) {
if (!!referenceChainLimit && depth > referenceChainLimit) {
this.actions.propagate.log(
edge.from.data,
post,
`referenceChainLimit (${params.referenceChainLimit}) reached`,
`referenceChainLimit (${referenceChainLimit}) reached`,
null,
'-x',
);
@ -269,6 +259,8 @@ export class Forum extends ReputationHolder {
increment: outboundAmount,
depth: depth + 1,
initialNegative: initialNegative || (depth === 0 && outboundAmount < 0),
referenceChainLimit,
leachingValue
});
// Any excess (negative) amount that could not be propagated,
@ -290,7 +282,7 @@ export class Forum extends ReputationHolder {
this.actions.confirm.log(
citationEdge.to.data,
citationEdge.from.data,
`(refund: ${displayNumber(refundFromOutbound)}, leach: ${outboundAmount * params.leachingValue})`,
`(refund: ${displayNumber(refundFromOutbound)}, leach: ${outboundAmount * leachingValue})`,
undefined,
'-->>',
);
@ -301,11 +293,11 @@ export class Forum extends ReputationHolder {
// First, leach value via negative citations
const totalLeachingAmount = await propagate(false);
increment -= totalLeachingAmount * params.leachingValue;
increment -= totalLeachingAmount * leachingValue;
// Now propagate value via positive citations
const totalDonationAmount = await propagate(true);
increment -= totalDonationAmount * params.leachingValue;
increment -= totalDonationAmount * leachingValue;
// Apply the remaining increment to the present post
const rawNewValue = post.value + increment;

View File

@ -1,10 +1,36 @@
import { ReputationHolder } from '../reputation/reputation-holder.js';
import { Stake } from '../supporting/stake.js';
import { Voter } from '../supporting/voter.js';
import params from '../../params.js';
import { Action } from '../display/action.js';
import { displayNumber } from '../../util/helpers.js';
const params = {
/* Validation Pool parameters */
mintingRatio: () => 1, // c1
// NOTE: c2 overlaps with c3 and adds excess complexity, so we omit it for now
stakeForAuthor: 0.5, // c3
winningRatio: 0.5, // c4
quorum: 0, // c5
activeVoterThreshold: null, // c6
voteDuration: {
// c7
min: 0,
max: null,
},
// NOTE: c8 is the token loss ratio, which is specified as a runtime argument
contentiousDebate: {
period: 5000, // c9
stages: 3, // c10
},
lockingTimeExponent: 0, // c11
/* Forum parameters */
initialPostValue: () => 1, // q1
revaluationLimit: 1, // q2
referenceChainLimit: 3, // q3
leachingValue: 1, // q4
};
const ValidationPoolStates = Object.freeze({
OPEN: 'OPEN',
CLOSED: 'CLOSED',
@ -63,8 +89,30 @@ export class ValidationPool extends ReputationHolder {
}]; got ${duration}`,
);
}
this.dao = dao;
this.postId = postId;
const post = this.dao.forum.graph.getVertexData(postId);
const leachingTotal = post.citations
.filter(({ weight }) => weight < 0)
.reduce((total, { weight }) => total += -weight, 0);
const donationTotal = post.citations
.filter(({ weight }) => weight > 0)
.reduce((total, { weight }) => total += weight, 0);
if (leachingTotal > params.revaluationLimit) {
throw new Error('Post leaching total exceeds revaluation limit '
+ `(${leachingTotal} > ${params.revaluationLimit})`);
}
if (donationTotal > params.revaluationLimit) {
throw new Error('Post donation total exceeds revaluation limit '
+ `(${donationTotal} > ${params.revaluationLimit})`);
}
if (post.citations.some(({ weight }) => Math.abs(weight) > params.revaluationLimit)) {
throw new Error(`Each citation magnitude must not exceed revaluation limit ${params.revaluationLimit}`);
}
this.state = ValidationPoolStates.OPEN;
this.setStatus('Open');
this.stakes = new Set();
@ -136,7 +184,7 @@ export class ValidationPool extends ReputationHolder {
*/
getTotalStakedOnPost(outcome) {
return this.getStakes(outcome, { excludeSystem: false })
.map((stake) => stake.getStakeValue())
.map((stake) => stake.getStakeValue({ lockingTimeExponent: params.lockingTimeExponent }))
.reduce((acc, cur) => (acc += cur), 0);
}
@ -262,7 +310,7 @@ export class ValidationPool extends ReputationHolder {
// Compute rewards for the winning voters, in proportion to the value of their stakes.
for (const stake of winningEntries) {
const { tokenId, amount } = stake;
const value = stake.getStakeValue();
const value = stake.getStakeValue({ lockingTimeExponent: params.lockingTimeExponent });
const reward = tokensForWinners * (value / totalValueOfStakesForWin);
// Also return each winning voter their staked amount
const reputationPublicKey = this.dao.reputation.ownerOf(tokenId);
@ -287,6 +335,8 @@ export class ValidationPool extends ReputationHolder {
pool: this,
postId: this.postId,
tokenId: this.tokenId,
referenceChainLimit: params.referenceChainLimit,
leachingValue: params.leachingValue,
});
}

View File

@ -1,5 +1,3 @@
import params from '../../params.js';
export class Stake {
constructor({
tokenId, position, amount, lockingTime,
@ -10,7 +8,7 @@ export class Stake {
this.lockingTime = lockingTime;
}
getStakeValue() {
return this.amount * this.lockingTime ** params.lockingTimeExponent;
getStakeValue({ lockingTimeExponent } = {}) {
return this.amount * this.lockingTime ** lockingTimeExponent;
}
}

View File

@ -1,28 +0,0 @@
const params = {
/* Validation Pool parameters */
mintingRatio: () => 1, // c1
// NOTE: c2 overlaps with c3 and adds excess complexity, so we omit it for now
stakeForAuthor: 0.5, // c3
winningRatio: 0.5, // c4
quorum: 0, // c5
activeVoterThreshold: null, // c6
voteDuration: {
// c7
min: 0,
max: null,
},
// NOTE: c8 is the token loss ratio, which is specified as a runtime argument
contentiousDebate: {
period: 5000, // c9
stages: 3, // c10
},
lockingTimeExponent: 0, // c11
/* Forum parameters */
initialPostValue: () => 1, // q1
revaluationLimit: 1, // q2
referenceChainLimit: 3, // q3
leachingValue: 1, // q4
};
export default params;

View File

@ -1,7 +1,7 @@
<!DOCTYPE html>
<head>
<title>VM</title>
<title>All Tests</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="https://unpkg.com/mocha/mocha.css" />
<link type="text/css" rel="stylesheet" href="../index.css" />

View File

@ -2,7 +2,6 @@ import { Box } from '../../../classes/display/box.js';
import { Scene } from '../../../classes/display/scene.js';
import { Expert } from '../../../classes/actors/expert.js';
import { PostContent } from '../../../classes/supporting/post-content.js';
import params from '../../../params.js';
import { DAO } from '../../../classes/dao/dao.js';
import { delayOrWait } from '../../../classes/display/controls.js';
@ -77,14 +76,6 @@ export class ForumTest {
scene.withFlowchart();
scene.withTable();
scene.addDisplayValue('c3. stakeForAuthor').set(params.stakeForAuthor);
scene.addDisplayValue('q2. revaluationLimit').set(params.revaluationLimit);
scene
.addDisplayValue('q3. referenceChainLimit')
.set(params.referenceChainLimit);
scene.addDisplayValue('q4. leachingValue').set(params.leachingValue);
scene.addDisplayValue('&nbsp;');
// If we're going to announce experts, announce the DAO so it appears first.
this.dao = new DAO('DAO', scene, { announce: this.options.displayAuthors });
this.forum = this.dao.forum;