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:
commit
ce4f78aa97
|
@ -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.
|
||||
|
||||
---
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
|
@ -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" />
|
||||
|
|
|
@ -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(' ');
|
||||
|
||||
// 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;
|
||||
|
|
Loading…
Reference in New Issue