Fixup forum logic
This commit is contained in:
parent
7c7d01aa91
commit
6cc1c25b37
|
@ -85,3 +85,15 @@ Token loss ratio
|
|||
---
|
||||
|
||||
parameter q_4 -- what is c_n?
|
||||
|
||||
---
|
||||
|
||||
what is reputation?
|
||||
valuable evidence that you're going to do what you say you'll do in the future
|
||||
|
||||
---
|
||||
|
||||
for now, combine c2 and c3
|
||||
|
||||
validation pool should compute rewards for author,
|
||||
then send that to the forum to distribute.
|
||||
|
|
|
@ -4,7 +4,7 @@ export class Actor {
|
|||
this.scene = scene;
|
||||
this.callbacks = new Map();
|
||||
this.status = this.scene.addDisplayValue(`${this.name} status`);
|
||||
this.status.set('New');
|
||||
this.status.set('Created');
|
||||
this.values = new Map();
|
||||
this.active = 0;
|
||||
this.scene.registerActor(this);
|
||||
|
|
|
@ -25,12 +25,11 @@ export class Bench extends Actor {
|
|||
}
|
||||
|
||||
listActiveVoters() {
|
||||
const now = new Date();
|
||||
const thresholdSet = !!params.activeVoterThreshold;
|
||||
return Array.from(this.voters.values()).filter((voter) => {
|
||||
const hasVoted = !!voter.dateLastVote;
|
||||
const withinThreshold = now - voter.dateLastVote >= params.activeVoterThreshold;
|
||||
return hasVoted && (!thresholdSet || withinThreshold);
|
||||
const withinThreshold = !params.activeVoterThreshold
|
||||
|| new Date() - voter.dateLastVote >= params.activeVoterThreshold;
|
||||
return hasVoted && withinThreshold;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ export class Expert extends Actor {
|
|||
await this.actions.initiateValidationPool.log(
|
||||
this,
|
||||
bench,
|
||||
`(fee: ${poolOptions.fee})`,
|
||||
`(fee: ${poolOptions.fee}, stake: ${poolOptions.authorStake ?? 0})`,
|
||||
);
|
||||
const pool = await bench.initiateValidationPool(poolOptions);
|
||||
this.validationPools.set(pool.id, poolOptions);
|
||||
|
|
|
@ -62,8 +62,7 @@ export class Forum extends Actor {
|
|||
return this.posts.getVerticesData();
|
||||
}
|
||||
|
||||
async setPostValue(postId, value) {
|
||||
const post = this.getPost(postId);
|
||||
async setPostValue(post, value) {
|
||||
post.value = value;
|
||||
await post.setValue('value', value);
|
||||
if (this.scene.flowchart) {
|
||||
|
@ -71,26 +70,36 @@ export class Forum extends Actor {
|
|||
}
|
||||
}
|
||||
|
||||
getPostValue(postId) {
|
||||
const post = this.getPost(postId);
|
||||
return post.value;
|
||||
}
|
||||
|
||||
getTotalValue() {
|
||||
return this.getPosts().reduce((total, { value }) => total += value, 0);
|
||||
}
|
||||
|
||||
async propagateValue(fromActor, postId, increment, depth = 0) {
|
||||
if (depth === 0 && this.scene.flowchart) {
|
||||
const randomId = CryptoUtil.randomUUID().replaceAll('-', '').slice(0, 8);
|
||||
this.scene.flowchart.log(`${postId}_initial_value_${randomId}[${increment}] -- initial value --> ${postId}`);
|
||||
async onValidate(bench, pool, postId, initialValue) {
|
||||
initialValue *= params.initialPostValue();
|
||||
|
||||
if (this.scene.flowchart) {
|
||||
this.scene.flowchart.log(`${postId}_initial_value[${initialValue}] -- initial value --> ${postId}`);
|
||||
}
|
||||
if (params.maxPropagationDepth >= 0 && depth > params.maxPropagationDepth) {
|
||||
|
||||
const post = this.getPost(postId);
|
||||
post.setStatus('Validated');
|
||||
const rewards = await this.propagateValue(pool, post, initialValue);
|
||||
const totalRewards = Array.from(rewards.values()).reduce((total, value) => total += value, 0);
|
||||
console.log('total awards from forum:', totalRewards);
|
||||
// Apply computed rewards
|
||||
for (const [id, value] of rewards) {
|
||||
bench.reputations.addTokens(id, value);
|
||||
}
|
||||
}
|
||||
|
||||
async propagateValue(fromActor, post, increment, depth = 0) {
|
||||
if (params.referenceChainLimit >= 0 && depth > params.referenceChainLimit) {
|
||||
return [];
|
||||
}
|
||||
const post = this.getPost(postId);
|
||||
this.actions.propagateValue.log(fromActor, post, `(increment: ${increment})`);
|
||||
|
||||
this.actions.propagateValue.log(fromActor, post, `(${increment})`);
|
||||
|
||||
// Apply leaching value
|
||||
const adjustedIncrement = increment * (1 - params.leachingValue * post.totalCitationWeight);
|
||||
|
||||
const rewards = new Map();
|
||||
|
@ -102,10 +111,7 @@ export class Forum extends Actor {
|
|||
};
|
||||
|
||||
// Increment the value of the post
|
||||
// Apply leaching value
|
||||
const currentValue = this.getPostValue(postId);
|
||||
const newValue = currentValue + adjustedIncrement;
|
||||
await this.setPostValue(postId, newValue);
|
||||
await this.setPostValue(post, post.value + adjustedIncrement);
|
||||
|
||||
// Award reputation to post author
|
||||
console.log('reward for post author', post.authorPublicKey, adjustedIncrement);
|
||||
|
@ -113,7 +119,8 @@ export class Forum extends Actor {
|
|||
|
||||
// Recursively distribute reputation to citations, according to weights
|
||||
for (const { postId: citedPostId, weight } of post.citations) {
|
||||
addRewards(await this.propagateValue(post, citedPostId, weight * increment, depth + 1));
|
||||
const citedPost = this.getPost(citedPostId);
|
||||
addRewards(await this.propagateValue(post, citedPost, weight * increment, depth + 1));
|
||||
}
|
||||
|
||||
return rewards;
|
||||
|
|
|
@ -68,11 +68,7 @@ export class ValidationPool extends Actor {
|
|||
this.duration = duration;
|
||||
this.tokenLossRatio = tokenLossRatio;
|
||||
this.contentiousDebate = contentiousDebate;
|
||||
this.tokensMinted = fee * params.mintingRatio;
|
||||
this.tokens = {
|
||||
for: this.tokensMinted * params.stakeForWin,
|
||||
against: this.tokensMinted * (1 - params.stakeForWin),
|
||||
};
|
||||
this.tokensMinted = fee * params.mintingRatio();
|
||||
// Tokens minted "for" the post go toward stake of author voting for their own post.
|
||||
// Also, author can provide additional stakes, e.g. availability stakes for work evidence post.
|
||||
this.castVote(signingPublicKey, {
|
||||
|
@ -80,12 +76,21 @@ export class ValidationPool extends Actor {
|
|||
stake: this.tokensMinted * params.stakeForAuthor + authorStake,
|
||||
anonymous,
|
||||
});
|
||||
this.castVote(undefined, {
|
||||
position: false,
|
||||
stake: this.tokensMinted * (1 - params.stakeForAuthor),
|
||||
isSystemVote: true,
|
||||
});
|
||||
}
|
||||
|
||||
async castVote(signingPublicKey, {
|
||||
position, stake, lockingTime = 0, anonymous = true,
|
||||
position, stake, lockingTime = 0, anonymous = true, isSystemVote = false,
|
||||
}) {
|
||||
const vote = new Vote(position, stake, lockingTime);
|
||||
if (isSystemVote) {
|
||||
signingPublicKey = CryptoUtil.randomUUID();
|
||||
anonymous = false;
|
||||
}
|
||||
const vote = new Vote(position, stake, lockingTime, isSystemVote);
|
||||
if (this.state === ValidationPoolStates.CLOSED) {
|
||||
throw new Error(`Validation pool ${this.id} is closed`);
|
||||
}
|
||||
|
@ -100,10 +105,10 @@ export class ValidationPool extends Actor {
|
|||
}
|
||||
}
|
||||
|
||||
listVotes(position) {
|
||||
listVotes(filter) {
|
||||
return new Map(
|
||||
Array.from(this.votes).filter(
|
||||
([_, vote]) => vote.position === position,
|
||||
([_, vote]) => filter(vote),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -175,7 +180,9 @@ export class ValidationPool extends Actor {
|
|||
this.setStatus('Closed');
|
||||
|
||||
const getVoteValue = ({ stake, lockingTime }) => stake * lockingTime ** params.lockingTimeExponent;
|
||||
const getTotalValue = (position) => Array.from(this.listVotes(position).values())
|
||||
const getTotalValue = (votePosition) => Array.from(this.listVotes(
|
||||
({ position }) => position === votePosition,
|
||||
).values())
|
||||
.map(getVoteValue)
|
||||
.reduce((acc, cur) => (acc += cur), 0);
|
||||
|
||||
|
@ -192,8 +199,8 @@ export class ValidationPool extends Actor {
|
|||
};
|
||||
|
||||
if (quorumMet) {
|
||||
this.setStatus(`Resolved - ${result ? 'Won' : 'Lost'}`);
|
||||
this.scene.sequence.log(`note over ${this.name} : ${result ? 'Win' : 'Lose'}`);
|
||||
this.setStatus(`Resolved - ${votePasses ? 'Won' : 'Lost'}`);
|
||||
this.scene.sequence.log(`note over ${this.name} : ${votePasses ? 'Win' : 'Lose'}`);
|
||||
this.applyTokenLocking();
|
||||
await this.distributeReputation(result);
|
||||
// TODO: distribute fees
|
||||
|
@ -208,54 +215,40 @@ export class ValidationPool extends Actor {
|
|||
}
|
||||
|
||||
async distributeReputation({ votePasses }) {
|
||||
const rewards = new Map();
|
||||
const addReward = (id, value) => rewards.set(id, (rewards.get(id) ?? 0) + value);
|
||||
|
||||
// TODO: Take tokenLossRatio into account
|
||||
const getTotalStaked = (position) => Array.from(this.listVotes(position).values())
|
||||
const getTotalStaked = (votePosition, excludeSystem = false) => Array.from(this.listVotes(
|
||||
({ position, isSystemVote }) => position === votePosition && (!excludeSystem || !isSystemVote),
|
||||
).values())
|
||||
.map(({ stake }) => stake)
|
||||
.reduce((acc, cur) => (acc += cur), 0);
|
||||
const tokensForWinners = votePasses ? (this.tokens.for + getTotalStaked(false))
|
||||
: (this.tokens.against + getTotalStaked(true));
|
||||
const winningVotes = this.listVotes(votePasses);
|
||||
const tokensForWinners = getTotalStaked(!votePasses);
|
||||
const winningVotes = this.listVotes(({ position, isSystemVote }) => position === votePasses && !isSystemVote);
|
||||
|
||||
// Reward the winning voters, in proportion to their stakes
|
||||
const rewards = new Map();
|
||||
for (const [signingPublicKey, { stake }] of winningVotes) {
|
||||
const { reputationPublicKey } = this.voters.get(signingPublicKey);
|
||||
const reward = (tokensForWinners * stake) / getTotalStaked(votePasses);
|
||||
addReward(reputationPublicKey, reward);
|
||||
rewards.set(reputationPublicKey, reward);
|
||||
console.log(`reward for winning voter ${reputationPublicKey}:`, reward);
|
||||
}
|
||||
|
||||
const awardsFromVoting = Array.from(rewards.values()).reduce((total, value) => total += value, 0);
|
||||
console.log('total awards from voting:', awardsFromVoting);
|
||||
|
||||
if (votePasses && !!this.forum) {
|
||||
if (votePasses) {
|
||||
const authorReputationPublicKey = this.voters.get(this.authorSigningPublicKey).reputationPublicKey;
|
||||
const tokensForAuthor = this.tokensMinted * params.stakeForAuthor + rewards.get(authorReputationPublicKey);
|
||||
|
||||
if (votePasses && !!this.forum) {
|
||||
// Recurse through forum to determine reputation effects
|
||||
const forumReputationEffects = await this.forum.propagateValue(this, this.postId, this.tokensMinted);
|
||||
for (const [id, value] of forumReputationEffects) {
|
||||
addReward(id, value);
|
||||
await this.forum.onValidate(
|
||||
this.bench,
|
||||
this,
|
||||
this.postId,
|
||||
tokensForAuthor,
|
||||
);
|
||||
}
|
||||
const awardsFromForum = Array.from(forumReputationEffects.values()).reduce((total, value) => total += value, 0);
|
||||
console.log('total awards from forum:', awardsFromForum);
|
||||
}
|
||||
|
||||
// Allow for possible attenuation of total value of post, e.g. based on degree of contention
|
||||
const initialPostValue = this.tokensMinted * params.initialPostValue();
|
||||
|
||||
// Scale all rewards so that the total is correct
|
||||
// TODO: Add more precise assertions; otherwise this operation could mask errors.
|
||||
const currentTotal = Array.from(rewards.values()).reduce((total, value) => total += value, 0);
|
||||
console.log('total awards before normalization:', currentTotal);
|
||||
for (const [id, value] of rewards) {
|
||||
const normalizedValue = (value * initialPostValue) / currentTotal;
|
||||
console.log(`normalized reward for ${id}: ${value} -> ${normalizedValue}`);
|
||||
rewards.set(id, normalizedValue);
|
||||
}
|
||||
|
||||
// Apply computed rewards
|
||||
for (const [id, value] of rewards) {
|
||||
this.bench.reputations.addTokens(id, value);
|
||||
}
|
||||
|
||||
console.log('pool complete');
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
export class Vote {
|
||||
constructor(position, stake, lockingTime) {
|
||||
constructor(position, stake, lockingTime, isSystemVote = false) {
|
||||
this.position = position;
|
||||
this.stake = stake;
|
||||
this.lockingTime = lockingTime;
|
||||
this.isSystemVote = isSystemVote;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const params = {
|
||||
/* Validation Pool parameters */
|
||||
mintingRatio: 1, // c1
|
||||
stakeForWin: 0.5, // c2
|
||||
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
|
||||
|
|
|
@ -22,9 +22,8 @@
|
|||
const rootElement = document.getElementById("availability-test");
|
||||
const rootBox = new Box("rootBox", rootElement).flex();
|
||||
|
||||
const scene = (window.scene = new Scene("Availability test", rootBox).log(
|
||||
"sequenceDiagram"
|
||||
));
|
||||
const scene = (window.scene = new Scene("Availability test", rootBox));
|
||||
scene.withSequenceDiagram();
|
||||
|
||||
const experts = (window.experts = []);
|
||||
const newExpert = async () => {
|
||||
|
|
|
@ -28,7 +28,12 @@
|
|||
scene.withSequenceDiagram();
|
||||
scene.withFlowchart();
|
||||
|
||||
scene.addDisplayValue("leachingValue").set(params.leachingValue);
|
||||
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(" ");
|
||||
|
||||
const experts = (window.experts = []);
|
||||
|
@ -37,14 +42,15 @@
|
|||
const name = `Expert${index + 1}`;
|
||||
const expert = await new Expert(name, scene).initialize();
|
||||
experts.push(expert);
|
||||
// bench.reputations.addTokens(expert.reputationPublicKey, 50);
|
||||
return expert;
|
||||
};
|
||||
|
||||
const forum = (window.forum = new Forum("Forum", scene));
|
||||
const bench = (window.bench = new Bench(forum, "Bench", scene));
|
||||
const expert1 = await newExpert();
|
||||
const expert2 = await newExpert();
|
||||
const expert3 = await newExpert();
|
||||
const forum = (window.forum = new Forum("Forum", scene));
|
||||
const bench = (window.bench = new Bench(forum, "Bench", scene));
|
||||
|
||||
const updateDisplayValues = async () => {
|
||||
for (const expert of experts) {
|
||||
|
@ -74,6 +80,7 @@
|
|||
fee: 10,
|
||||
duration: 1000,
|
||||
tokenLossRatio: 1,
|
||||
// authorStake: 10,
|
||||
}
|
||||
);
|
||||
await updateDisplayValuesAndDelay(1000);
|
||||
|
|
Loading…
Reference in New Issue