Clarify terminology: Bench, Validation Pool, Vote
This commit is contained in:
parent
c44c70cf03
commit
c89ba51dab
|
@ -0,0 +1,72 @@
|
|||
import { Actor } from "./actor.js";
|
||||
import { Reputations } from "./reputation.js";
|
||||
import { ValidationPool } from "./validation-pool.js";
|
||||
import { Vote } from "./vote.js";
|
||||
import { Voter } from "./voter.js";
|
||||
import params from "./params.js";
|
||||
|
||||
export class Bench extends Actor {
|
||||
constructor(name, scene) {
|
||||
super(name, scene);
|
||||
this.validationPools = new Map();
|
||||
this.voters = new Map();
|
||||
this.reputations = new Reputations();
|
||||
|
||||
this.actions = {
|
||||
};
|
||||
}
|
||||
|
||||
listValidationPools() {
|
||||
Array.from(this.validationPools.values());
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
getTotalReputation() {
|
||||
return this.reputations.getTotal();
|
||||
}
|
||||
|
||||
getTotalAvailableReputation() {
|
||||
return this.reputations.getTotalAvailable();
|
||||
}
|
||||
|
||||
getTotalActiveReputation() {
|
||||
return this.listActiveVoters()
|
||||
.map(({reputationPublicKey}) => this.reputations.getTokens(reputationPublicKey))
|
||||
.reduce((acc, cur) => acc += cur, 0);
|
||||
}
|
||||
|
||||
getTotalActiveAvailableReputation() {
|
||||
return this.listActiveVoters()
|
||||
.map(({reputationPublicKey}) => this.reputations.getAvailableTokens(reputationPublicKey))
|
||||
.reduce((acc, cur) => acc += cur, 0);
|
||||
}
|
||||
|
||||
initiateValidationPool(authorId, {fee, duration, tokenLossRatio, contentiousDebate}) {
|
||||
const vote = new ValidationPool(this, authorId, {fee, duration, tokenLossRatio, contentiousDebate});
|
||||
this.validationPools.set(vote.id, vote);
|
||||
return vote.id;
|
||||
}
|
||||
|
||||
castVote(voteId, signingPublicKey, position, stake, lockingTime) {
|
||||
const vote = new Vote(position, stake, lockingTime);
|
||||
const validationPool = this.validationPools.get(voteId);
|
||||
validationPool.castVote(signingPublicKey, vote);
|
||||
}
|
||||
|
||||
revealIdentity(voteId, signingPublicKey, reputationPublicKey) {
|
||||
const validationPool = this.validationPools.get(voteId);
|
||||
const voter = this.voters.get(reputationPublicKey) ?? new Voter(reputationPublicKey);
|
||||
voter.addVoteRecord(validationPool);
|
||||
this.voters.set(reputationPublicKey, voter);
|
||||
validationPool.revealIdentity(signingPublicKey, voter);
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
class Blockchain {
|
||||
constructor() {
|
||||
this.posts = new CollectionWithReputation();
|
||||
this.authors = new CollectionWithReputation();
|
||||
this.nodes = new CollectionWithReputation();
|
||||
}
|
||||
|
||||
vote(voter, batch) {
|
||||
if (!this.activeVote) {
|
||||
this.activeVote = new VoteInstance(batch);
|
||||
this.activeVote.vote(voter, true);
|
||||
return;
|
||||
}
|
||||
if (this.activeVote.matches(batch)) {
|
||||
this.activeVote.vote(voter, true);
|
||||
} else {
|
||||
this.activeVote.vote(voter, false);
|
||||
}
|
||||
}
|
||||
|
||||
applyBatch(batch) {}
|
||||
}
|
||||
|
||||
class CollectionWithReputation {
|
||||
constructor() {
|
||||
this.collection = new Map();
|
||||
this.reputations = new Map();
|
||||
}
|
||||
|
||||
set(id, value) {
|
||||
this.collection.set(id, value);
|
||||
}
|
||||
|
||||
get(id) {
|
||||
this.collection.get(id);
|
||||
}
|
||||
|
||||
setReputation(id, value) {
|
||||
this.reputations.set(id, value);
|
||||
}
|
||||
|
||||
get(id) {
|
||||
this.reputations.get(id);
|
||||
}
|
||||
}
|
||||
|
||||
class VoteInstance {
|
||||
constructor(content) {
|
||||
this.content = content;
|
||||
this.votes = new Map();
|
||||
}
|
||||
|
||||
matches(content) {
|
||||
return JSON.stringify(content) === JSON.stringify(this.content);
|
||||
}
|
||||
|
||||
vote(voter, opinion) {
|
||||
this.votes.set(voter.id, { voter, opinion });
|
||||
}
|
||||
|
||||
finalize() {
|
||||
const count = { for: 0, against: 0 };
|
||||
for (const vote of this.votes) {
|
||||
if (vote.opinion === true) {
|
||||
count.for++;
|
||||
} else {
|
||||
count.against++;
|
||||
}
|
||||
}
|
||||
return count.for > count.against;
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ export class Member extends Actor {
|
|||
super(name, scene);
|
||||
this.actions = {
|
||||
submitPost: new Action('submit post', scene),
|
||||
initiateVote: new Action('initiate vote', scene),
|
||||
initiateValidationPool: new Action('initiate vote', scene),
|
||||
castVote: new Action('cast vote', scene),
|
||||
revealIdentity: new Action('reveal identity', scene),
|
||||
};
|
||||
|
@ -31,10 +31,10 @@ export class Member extends Actor {
|
|||
await forumNode.receiveMessage(JSON.stringify(postMessage.toJSON()));
|
||||
}
|
||||
|
||||
initiateVote(validationPool, options) {
|
||||
// For now, directly call validationPool.initiateVote();
|
||||
this.actions.initiateVote.log(this, validationPool);
|
||||
return validationPool.initiateVote(this.reputationPublicKey, options);
|
||||
initiateValidationPool(bench, options) {
|
||||
// For now, directly call bench.initiateValidationPool();
|
||||
this.actions.initiateValidationPool.log(this, bench);
|
||||
return bench.initiateValidationPool(this.reputationPublicKey, options);
|
||||
}
|
||||
|
||||
async castVote(validationPool, voteId, position, stake, lockingTime) {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { CryptoUtil } from "../crypto.js";
|
||||
import { CryptoUtil } from "./crypto.js";
|
||||
import params from "./params.js";
|
||||
|
||||
const VoteInstanceStates = Object.freeze({
|
||||
const ValidationPoolStates = Object.freeze({
|
||||
OPEN: "OPEN",
|
||||
CLOSED: "CLOSED",
|
||||
});
|
||||
|
||||
export class VoteInstance {
|
||||
export class ValidationPool {
|
||||
constructor(validationPool, authorId, {fee, duration, tokenLossRatio, contentiousDebate = false}) {
|
||||
// If contentiousDebate = true, we will follow the progression defined by getTokenLossRatio()
|
||||
if (!contentiousDebate && (tokenLossRatio < 0 || tokenLossRatio > 1 || [null, undefined].includes(tokenLossRatio))) {
|
||||
|
@ -15,7 +15,7 @@ export class VoteInstance {
|
|||
if (duration < params.voteDuration.min || (params.voteDuration.max && duration > params.voteDuration.max) || [null, undefined].includes(duration)) {
|
||||
throw new Error(`Duration must be in the range [${params.voteDuration.min}, ${params.voteDuration.max ?? 'Inf'}]; got ${duration}`);
|
||||
}
|
||||
this.state = VoteInstanceStates.OPEN;
|
||||
this.state = ValidationPoolStates.OPEN;
|
||||
this.votes = new Map();
|
||||
this.voters = new Map();
|
||||
this.validationPool = validationPool;
|
||||
|
@ -34,7 +34,7 @@ export class VoteInstance {
|
|||
}
|
||||
|
||||
castVote(signingPublicKey, vote) {
|
||||
if (this.state === VoteInstanceStates.CLOSED) {
|
||||
if (this.state === ValidationPoolStates.CLOSED) {
|
||||
throw new Error(`Vote ${this.id} is closed`);
|
||||
}
|
||||
if (this.duration && new Date() - this.dateStart > this.duration) {
|
||||
|
@ -56,7 +56,7 @@ export class VoteInstance {
|
|||
if (this.votes.size === this.voters.size) {
|
||||
// All voters have revealed their reputation public keys
|
||||
// Now we can evaluate winning conditions
|
||||
this.state = VoteInstanceStates.CLOSED;
|
||||
this.state = ValidationPoolStates.CLOSED;
|
||||
const result = this.evaluateWinningConditions();
|
||||
this.applyTokenLocking();
|
||||
this.distributeTokens(result);
|
||||
|
@ -103,7 +103,7 @@ export class VoteInstance {
|
|||
|
||||
const upvoteValue = getTotalValue(true);
|
||||
const downvoteValue = getTotalValue(false);
|
||||
const activeAvailableReputation = this.validationPool.activeAvailableReputation();
|
||||
const activeAvailableReputation = this.validationPool.getTotalActiveAvailableReputation();
|
||||
const votePasses = upvoteValue >= params.winningRatio * downvoteValue;
|
||||
const quorumMet = upvoteValue + downvoteValue >= params.quorum * activeAvailableReputation;
|
||||
|
|
@ -1 +0,0 @@
|
|||
export { ValidationPool } from "./validation-pool.js";
|
|
@ -1,59 +0,0 @@
|
|||
import { Actor } from "../actor.js";
|
||||
import { Action } from "../action.js";
|
||||
import { Reputations } from "../reputation.js";
|
||||
import { VoteInstance } from "./vote-instance.js";
|
||||
import { Vote } from "./vote.js";
|
||||
import { Voter } from "./voter.js";
|
||||
import params from "./params.js";
|
||||
|
||||
export class ValidationPool extends Actor {
|
||||
constructor(name, scene) {
|
||||
super(name, scene);
|
||||
this.voteInstances = new Map();
|
||||
this.voters = new Map();
|
||||
this.reputations = new Reputations();
|
||||
|
||||
this.actions = {
|
||||
};
|
||||
}
|
||||
|
||||
listVoteInstances() {
|
||||
Array.from(this.voteInstances.values());
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
activeAvailableReputation() {
|
||||
return this.listActiveVoters()
|
||||
.map(({reputationPublicKey}) => this.reputations.getAvailableTokens(reputationPublicKey))
|
||||
.reduce((acc, cur) => acc += cur, 0);
|
||||
}
|
||||
|
||||
initiateVote(authorId, {fee, duration, tokenLossRatio, contentiousDebate}) {
|
||||
const vote = new VoteInstance(this, authorId, {fee, duration, tokenLossRatio, contentiousDebate});
|
||||
this.voteInstances.set(vote.id, vote);
|
||||
return vote.id;
|
||||
}
|
||||
|
||||
castVote(voteId, signingPublicKey, position, stake, lockingTime) {
|
||||
const vote = new Vote(position, stake, lockingTime);
|
||||
const voteInstance = this.voteInstances.get(voteId);
|
||||
voteInstance.castVote(signingPublicKey, vote);
|
||||
}
|
||||
|
||||
revealIdentity(voteId, signingPublicKey, reputationPublicKey) {
|
||||
const voteInstance = this.voteInstances.get(voteId);
|
||||
const voter = this.voters.get(reputationPublicKey) ?? new Voter(reputationPublicKey);
|
||||
voter.addVoteRecord(voteInstance);
|
||||
this.voters.set(reputationPublicKey, voter);
|
||||
voteInstance.revealIdentity(signingPublicKey, voter);
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
font-size: 12pt;
|
||||
}
|
||||
.box .name {
|
||||
width: 12em;
|
||||
width: 15em;
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
margin-right: 6pt;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Box } from './classes/box.js';
|
||||
import { Scene } from './classes/scene.js';
|
||||
import { Member } from './classes/member.js';
|
||||
import { ValidationPool } from './classes/validation-pool/index.js';
|
||||
import { Bench } from './classes/bench.js';
|
||||
|
||||
const delay = async (ms) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
@ -12,29 +12,39 @@ const rootBox = new Box('rootBox', rootElement).flex();
|
|||
|
||||
const scene = window.scene = new Scene('Validation Pool test', rootBox).log('sequenceDiagram');
|
||||
|
||||
const pool = window.validationPool = new ValidationPool("validationPool", scene);
|
||||
const bench = window.bench = new Bench("Bench", scene);
|
||||
|
||||
const member1 = window.member1 = (await new Member("member1", scene).initialize()).setValue('rep', 0);
|
||||
const member2 = window.member2 = (await new Member("member2", scene).initialize()).setValue('rep', 0);
|
||||
const member1 = window.member1 = await new Member("member1", scene).initialize();
|
||||
const member2 = window.member2 = await new Member("member2", scene).initialize();
|
||||
|
||||
const updateDisplayValues = () => {
|
||||
member1.setValue('rep', bench.reputations.getTokens(member1.reputationPublicKey));
|
||||
member2.setValue('rep', bench.reputations.getTokens(member2.reputationPublicKey));
|
||||
bench.setValue('total rep', bench.getTotalReputation());
|
||||
bench.setValue('available rep', bench.getTotalAvailableReputation());
|
||||
bench.setValue('active rep', bench.getTotalActiveReputation());
|
||||
bench.setValue('active available rep', bench.getTotalActiveAvailableReputation());
|
||||
};
|
||||
|
||||
updateDisplayValues();
|
||||
|
||||
await delay(1000);
|
||||
|
||||
// First member can self-approve
|
||||
const vote1 = member1.initiateVote(pool, {fee: 1, duration: 1000, tokenLossRatio: 1});
|
||||
await member1.castVote(pool, vote1, true, 0, 0);
|
||||
await member1.revealIdentity(pool, vote1); // Vote passes
|
||||
member1.setValue('rep', pool.reputations.getTokens(member1.reputationPublicKey));
|
||||
const vote1 = member1.initiateValidationPool(bench, {fee: 1, duration: 1000, tokenLossRatio: 1});
|
||||
await member1.castVote(bench, vote1, true, 0, 0);
|
||||
await member1.revealIdentity(bench, vote1); // Vote passes
|
||||
updateDisplayValues();
|
||||
|
||||
// const vote2 = member2.initiateVote(pool, {fee: 1, duration: 1000, tokenLossRatio: 1});
|
||||
// await member2.castVote(pool, vote2, true, 0);
|
||||
// await member2.revealIdentity(pool, vote2); // Quorum is not met!
|
||||
// const vote2 = member2.initiateValidationPool(bench, {fee: 1, duration: 1000, tokenLossRatio: 1});
|
||||
// await member2.castVote(bench, vote2, true, 0);
|
||||
// await member2.revealIdentity(bench, vote2); // Quorum is not met!
|
||||
|
||||
await delay(1000);
|
||||
|
||||
// Second member must be approved by first member
|
||||
const vote2 = member2.initiateVote(pool, {fee: 1, duration: 1000, tokenLossRatio: 1});
|
||||
await member1.castVote(pool, vote2, true, 0.5, 1);
|
||||
await member1.revealIdentity(pool, vote2); // Vote passes
|
||||
const vote2 = member2.initiateValidationPool(bench, {fee: 1, duration: 1000, tokenLossRatio: 1});
|
||||
await member1.castVote(bench, vote2, true, 0.5, 1);
|
||||
await member1.revealIdentity(bench, vote2); // Vote passes
|
||||
|
||||
member1.setValue('rep', pool.reputations.getTokens(member1.reputationPublicKey));
|
||||
member2.setValue('rep', pool.reputations.getTokens(member2.reputationPublicKey));
|
||||
updateDisplayValues();
|
||||
|
|
Loading…
Reference in New Issue