Improve Availability test
This commit is contained in:
parent
6ad293b5b8
commit
fc3138adab
|
@ -3,3 +3,23 @@
|
||||||
- Receiving payments
|
- Receiving payments
|
||||||
- Distributing payments to participants
|
- Distributing payments to participants
|
||||||
- Computing updates to forum graph
|
- Computing updates to forum graph
|
||||||
|
|
||||||
|
## Receiving payments
|
||||||
|
|
||||||
|
Business SC will need to implement a financial model.
|
||||||
|
|
||||||
|
# Excerpts from DeSciPubDAOArchit22July19PrintCut.pdf
|
||||||
|
|
||||||
|
> With today’s prices, however, we will begin by programming this all off-chain and simplify the reputation tokens to be less dynamic in their evaluation. Next iteration improves the decentralization commensurate with practical realities.
|
||||||
|
|
||||||
|
# Questions
|
||||||
|
|
||||||
|
## Validation pool termination
|
||||||
|
|
||||||
|
How do we want to handle this?
|
||||||
|
The validation pool specifies a duration.
|
||||||
|
We don't want to compute results until the end of this duration.
|
||||||
|
We're currently supporting anonymous voting.
|
||||||
|
With anonymous voting, we need to wait until the end of the vote duration,
|
||||||
|
and then have a separate interval in which voters reveal their identities.
|
||||||
|
For now, we can let anonymous voters reveal their identities at any time
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<head>
|
<head>
|
||||||
<title>Forum</title>
|
<title>Availability test</title>
|
||||||
<script type="module" src="./availability-test.js" defer></script>
|
<script type="module" src="./availability-test.js" defer></script>
|
||||||
<link type="text/css" rel="stylesheet" href="./index.css" />
|
<link type="text/css" rel="stylesheet" href="./index.css" />
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -6,17 +6,32 @@ import { Business } from './classes/business.js';
|
||||||
import { Availability } from './classes/availability.js';
|
import { Availability } from './classes/availability.js';
|
||||||
import { delay } from './util.js';
|
import { delay } from './util.js';
|
||||||
import { Forum } from './classes/forum.js';
|
import { Forum } from './classes/forum.js';
|
||||||
|
import { Public } from './classes/public.js';
|
||||||
|
|
||||||
|
const DELAY_INTERVAL = 500;
|
||||||
|
|
||||||
const rootElement = document.getElementById('availability-test');
|
const rootElement = document.getElementById('availability-test');
|
||||||
const rootBox = new Box('rootBox', rootElement).flex();
|
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).log('sequenceDiagram');
|
||||||
const member1 = window.member1 = await new Member('Member1', scene).initialize();
|
|
||||||
const member2 = window.member2 = await new Member('Member2', scene).initialize();
|
const members = window.members = [];
|
||||||
|
const newMember = async () => {
|
||||||
|
const index = members.length;
|
||||||
|
const name = `Member${index + 1}`;
|
||||||
|
const member = await new Member(name, scene).initialize();
|
||||||
|
members.push(member);
|
||||||
|
return member;
|
||||||
|
};
|
||||||
|
|
||||||
|
const member1 = await newMember();
|
||||||
|
const member2 = await newMember();
|
||||||
|
await newMember();
|
||||||
const bench = window.bench = new Bench('Bench', scene);
|
const bench = window.bench = new Bench('Bench', scene);
|
||||||
const forum = window.forum = new Forum(bench, 'Forum', scene);
|
const forum = window.forum = new Forum(bench, 'Forum', scene);
|
||||||
const availability = window.bench = new Availability(bench, 'Availability', scene);
|
const availability = window.bench = new Availability(bench, 'Availability', scene);
|
||||||
const business = window.bench = new Business(bench, forum, availability, 'Business', scene);
|
const business = window.business = new Business(bench, forum, availability, 'Business', scene);
|
||||||
|
const requestor = window.requestor = new Public('Public', scene);
|
||||||
|
|
||||||
const updateDisplayValues = async () => {
|
const updateDisplayValues = async () => {
|
||||||
member1.setValue('rep', bench.reputations.getTokens(member1.reputationPublicKey));
|
member1.setValue('rep', bench.reputations.getTokens(member1.reputationPublicKey));
|
||||||
|
@ -25,44 +40,65 @@ const updateDisplayValues = async () => {
|
||||||
await scene.renderSequenceDiagram();
|
await scene.renderSequenceDiagram();
|
||||||
};
|
};
|
||||||
|
|
||||||
updateDisplayValues();
|
const updateDisplayValuesAndDelay = async () => {
|
||||||
|
await updateDisplayValues();
|
||||||
|
await delay(DELAY_INTERVAL);
|
||||||
|
};
|
||||||
|
|
||||||
// const post1 = window.post1 = new PostContent({ message: 'hi' });
|
const getActiveWorker = async () => {
|
||||||
// const post2 = window.post2 = new PostContent({ message: 'hello' }).addCitation(window.post1.id, 1.0);
|
let worker;
|
||||||
|
let request;
|
||||||
|
for (const member of members) {
|
||||||
|
request = await member.getAssignedWork(availability, business);
|
||||||
|
if (request) {
|
||||||
|
worker = member;
|
||||||
|
worker.actions.getAssignedWork.log(worker, availability);
|
||||||
|
worker.activate();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { worker, request };
|
||||||
|
};
|
||||||
|
|
||||||
|
const voteForWorkEvidence = async (worker, pool) => {
|
||||||
|
for (const member of members) {
|
||||||
|
if (member !== worker) {
|
||||||
|
await member.castVote(pool, { position: true, stake: 1, anonymous: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await updateDisplayValuesAndDelay();
|
||||||
|
|
||||||
// Populate availability pool
|
// Populate availability pool
|
||||||
availability.register(member1.reputationPublicKey, 1);
|
await member1.registerAvailability(availability, 1);
|
||||||
availability.register(member2.reputationPublicKey, 1);
|
await member2.registerAvailability(availability, 1);
|
||||||
|
await updateDisplayValuesAndDelay();
|
||||||
await delay(500);
|
|
||||||
|
|
||||||
// Submit work request
|
// Submit work request
|
||||||
const requestId = await business.submitRequest(100, { please: 'do some work' });
|
await requestor.submitRequest(business, { fee: 100 }, { please: 'do some work' });
|
||||||
await scene.renderSequenceDiagram();
|
await updateDisplayValuesAndDelay();
|
||||||
await delay(500);
|
|
||||||
|
// Receive work request
|
||||||
|
const { worker, request } = await getActiveWorker();
|
||||||
|
|
||||||
// Submit work evidence
|
// Submit work evidence
|
||||||
const pool = await business.submitWork(member1.reputationPublicKey, requestId, {
|
const pool = await worker.submitWork(business, request.id, {
|
||||||
here: 'is some evidence of work product',
|
here: 'is some evidence of work product',
|
||||||
}, {
|
}, {
|
||||||
tokenLossRatio: 1,
|
tokenLossRatio: 1,
|
||||||
duration: 1000,
|
duration: 1000,
|
||||||
});
|
});
|
||||||
|
worker.deactivate();
|
||||||
await scene.renderSequenceDiagram();
|
await updateDisplayValuesAndDelay();
|
||||||
await delay(500);
|
|
||||||
|
|
||||||
// Vote on work evidence
|
// Vote on work evidence
|
||||||
await member2.castVote(pool, { position: true, stake: 1 });
|
await voteForWorkEvidence(worker, pool);
|
||||||
await scene.renderSequenceDiagram();
|
await updateDisplayValuesAndDelay();
|
||||||
await delay(500);
|
|
||||||
|
|
||||||
await member2.revealIdentity(pool);
|
// Wait for validation pool duration to elapse
|
||||||
await scene.renderSequenceDiagram();
|
await delay(1000);
|
||||||
await delay(500);
|
|
||||||
|
|
||||||
await member1.revealIdentity(pool, member1.reputationPublicKey);
|
// Distribute reputation awards and fees
|
||||||
await scene.renderSequenceDiagram();
|
await pool.evaluateWinningConditions();
|
||||||
|
await updateDisplayValuesAndDelay();
|
||||||
// Distribute reputation awards
|
|
||||||
// Distribute fees
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { Action } from './action.js';
|
||||||
import { Actor } from './actor.js';
|
import { Actor } from './actor.js';
|
||||||
|
|
||||||
class Worker {
|
class Worker {
|
||||||
|
@ -21,6 +22,10 @@ export class Availability extends Actor {
|
||||||
constructor(bench, name, scene) {
|
constructor(bench, name, scene) {
|
||||||
super(name, scene);
|
super(name, scene);
|
||||||
this.bench = bench;
|
this.bench = bench;
|
||||||
|
|
||||||
|
this.actions = {
|
||||||
|
assignWork: new Action('assign work', scene),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
register(reputationPublicKey, stake) {
|
register(reputationPublicKey, stake) {
|
||||||
|
@ -44,6 +49,11 @@ export class Availability extends Actor {
|
||||||
const worker = this.availableWorkers[index];
|
const worker = this.availableWorkers[index];
|
||||||
worker.available = false;
|
worker.available = false;
|
||||||
worker.assignedRequestId = requestId;
|
worker.assignedRequestId = requestId;
|
||||||
// TOOD: Notify assignee
|
// TODO: Notify assignee
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAssignedWork(reputationPublicKey) {
|
||||||
|
const worker = this.workers.get(reputationPublicKey);
|
||||||
|
return worker.assignedRequestId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,7 @@ export class Bench extends Actor {
|
||||||
contentiousDebate,
|
contentiousDebate,
|
||||||
signingPublicKey,
|
signingPublicKey,
|
||||||
authorStake,
|
authorStake,
|
||||||
|
anonymous,
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
const validationPoolNumber = this.validationPools.size + 1;
|
const validationPoolNumber = this.validationPools.size + 1;
|
||||||
|
@ -79,6 +80,7 @@ export class Bench extends Actor {
|
||||||
contentiousDebate,
|
contentiousDebate,
|
||||||
signingPublicKey,
|
signingPublicKey,
|
||||||
authorStake,
|
authorStake,
|
||||||
|
anonymous,
|
||||||
},
|
},
|
||||||
`pool${validationPoolNumber}`,
|
`pool${validationPoolNumber}`,
|
||||||
this.scene,
|
this.scene,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { Action } from './action.js';
|
||||||
import { Actor } from './actor.js';
|
import { Actor } from './actor.js';
|
||||||
import { CryptoUtil } from './crypto.js';
|
import { CryptoUtil } from './crypto.js';
|
||||||
import { PostContent } from './post.js';
|
import { PostContent } from './post.js';
|
||||||
|
@ -21,6 +22,12 @@ export class Business extends Actor {
|
||||||
this.bench = bench;
|
this.bench = bench;
|
||||||
this.forum = forum;
|
this.forum = forum;
|
||||||
this.availability = availability;
|
this.availability = availability;
|
||||||
|
|
||||||
|
this.actions = {
|
||||||
|
assignWork: new Action('assign work', scene),
|
||||||
|
addPost: new Action('add post', scene),
|
||||||
|
initiateValidationPool: new Action('initiate validation pool', scene),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,29 +40,41 @@ export class Business extends Actor {
|
||||||
async submitRequest(fee, content) {
|
async submitRequest(fee, content) {
|
||||||
const request = new Request(fee, content);
|
const request = new Request(fee, content);
|
||||||
this.requests.set(request.id, request);
|
this.requests.set(request.id, request);
|
||||||
|
this.actions.assignWork.log(this, this.availability);
|
||||||
await this.availability.assignWork(request.id);
|
await this.availability.assignWork(request.id);
|
||||||
return request.id;
|
return request.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getRequest(requestId) {
|
||||||
|
const request = this.requests.get(requestId);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
async submitWork(reputationPublicKey, requestId, workEvidence, { tokenLossRatio, duration }) {
|
async submitWork(reputationPublicKey, requestId, workEvidence, { tokenLossRatio, duration }) {
|
||||||
const { fee } = this.requests.get(requestId);
|
const request = this.requests.get(requestId);
|
||||||
|
if (!request) {
|
||||||
|
throw new Error(`Request not found! id: ${requestId}`);
|
||||||
|
}
|
||||||
|
|
||||||
// Create a post representing this submission.
|
// Create a post representing this submission.
|
||||||
const post = new PostContent({
|
const post = new PostContent({
|
||||||
requestId,
|
requestId,
|
||||||
workEvidence,
|
workEvidence,
|
||||||
});
|
});
|
||||||
|
this.actions.addPost.log(this, this.forum);
|
||||||
await this.forum.addPost(reputationPublicKey, post);
|
await this.forum.addPost(reputationPublicKey, post);
|
||||||
|
|
||||||
// Initiate a validation pool for this work evidence.
|
// Initiate a validation pool for this work evidence.
|
||||||
// Validation pool supports secret ballots but we aren't using that here, since we want
|
// Validation pool supports secret ballots but we aren't using that here, since we want
|
||||||
// the post to be attributable to the reputation holder.
|
// the post to be attributable to the reputation holder.
|
||||||
|
this.actions.initiateValidationPool.log(this, this.bench);
|
||||||
const pool = await this.bench.initiateValidationPool(reputationPublicKey, {
|
const pool = await this.bench.initiateValidationPool(reputationPublicKey, {
|
||||||
postId: post.id,
|
postId: post.id,
|
||||||
fee,
|
fee: request.fee,
|
||||||
duration,
|
duration,
|
||||||
tokenLossRatio,
|
tokenLossRatio,
|
||||||
signingPublicKey: reputationPublicKey,
|
signingPublicKey: reputationPublicKey,
|
||||||
|
anonymous: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// When the validation pool concludes,
|
// When the validation pool concludes,
|
||||||
|
|
|
@ -34,14 +34,6 @@ export class Forum extends Actor {
|
||||||
for (const { postId: citedPostId, weight } of postContent.citations) {
|
for (const { postId: citedPostId, weight } of postContent.citations) {
|
||||||
this.posts.addEdge('citation', post.id, citedPostId, { weight });
|
this.posts.addEdge('citation', post.id, citedPostId, { weight });
|
||||||
}
|
}
|
||||||
// this.applyReputationEffects(post);
|
|
||||||
// initiateValidationPool(authorId, {postId, fee, duration, tokenLossRatio, contentiousDebate, signingPublicKey}) {
|
|
||||||
|
|
||||||
// const pool = await this.bench.initiateValidationPool(authorId, {
|
|
||||||
// ...poolParams,
|
|
||||||
// postId,
|
|
||||||
// });
|
|
||||||
// return pool;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getPost(postId) {
|
getPost(postId) {
|
||||||
|
|
|
@ -12,6 +12,9 @@ export class Member extends Actor {
|
||||||
initiateValidationPool: new Action('initiate validation pool', scene),
|
initiateValidationPool: new Action('initiate validation pool', scene),
|
||||||
castVote: new Action('cast vote', scene),
|
castVote: new Action('cast vote', scene),
|
||||||
revealIdentity: new Action('reveal identity', scene),
|
revealIdentity: new Action('reveal identity', scene),
|
||||||
|
registerAvailability: new Action('register availability', scene),
|
||||||
|
getAssignedWork: new Action('get assigned work', scene),
|
||||||
|
submitWork: new Action('submit work evidence', scene),
|
||||||
};
|
};
|
||||||
this.validationPools = new Map();
|
this.validationPools = new Map();
|
||||||
}
|
}
|
||||||
|
@ -51,30 +54,49 @@ export class Member extends Actor {
|
||||||
return pool;
|
return pool;
|
||||||
}
|
}
|
||||||
|
|
||||||
async castVote(validationPool, { position, stake, lockingTime }) {
|
async castVote(validationPool, {
|
||||||
const signingKey = await CryptoUtil.generateAsymmetricKey();
|
position, stake, lockingTime, anonymous = true,
|
||||||
const signingPublicKey = await CryptoUtil.exportKey(signingKey.publicKey);
|
}) {
|
||||||
this.validationPools.set(validationPool.id, { signingPublicKey });
|
let signingPublicKey;
|
||||||
|
if (anonymous) {
|
||||||
|
const signingKey = await CryptoUtil.generateAsymmetricKey();
|
||||||
|
signingPublicKey = await CryptoUtil.exportKey(signingKey.publicKey);
|
||||||
|
this.validationPools.set(validationPool.id, { signingPublicKey });
|
||||||
|
} else {
|
||||||
|
signingPublicKey = this.reputationPublicKey;
|
||||||
|
}
|
||||||
// TODO: encrypt vote
|
// TODO: encrypt vote
|
||||||
// TODO: sign message
|
// TODO: sign message
|
||||||
this.actions.castVote.log(
|
this.actions.castVote.log(
|
||||||
this,
|
this,
|
||||||
validationPool,
|
validationPool,
|
||||||
`(${position ? 'for' : 'against'}, stake: ${stake})`,
|
`(${position ? 'for' : 'against'}, stake: ${stake}, anonymous: ${anonymous})`,
|
||||||
);
|
);
|
||||||
validationPool.castVote(signingPublicKey, position, stake, lockingTime);
|
return validationPool.castVote(signingPublicKey, {
|
||||||
|
position, stake, lockingTime, anonymous,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async revealIdentity(validationPool, signingPublicKey) {
|
async revealIdentity(validationPool) {
|
||||||
if (!signingPublicKey) {
|
const { signingPublicKey } = this.validationPools.get(validationPool.id);
|
||||||
signingPublicKey = this.validationPools.get(validationPool.id).signingPublicKey;
|
|
||||||
}
|
|
||||||
// TODO: sign message
|
// TODO: sign message
|
||||||
this.actions.revealIdentity.log(this, validationPool);
|
this.actions.revealIdentity.log(this, validationPool);
|
||||||
validationPool.revealIdentity(signingPublicKey, this.reputationPublicKey);
|
validationPool.revealIdentity(signingPublicKey, this.reputationPublicKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
async submitWork(business, requestId, evidence, { tokenLossRatio }) {
|
async registerAvailability(availability, stake) {
|
||||||
await business.submitWork(this.reputationPublicKey, requestId, evidence, { tokenLossRatio });
|
this.actions.registerAvailability.log(this, availability, `(stake: ${stake})`);
|
||||||
|
await availability.register(this.reputationPublicKey, stake);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAssignedWork(availability, business) {
|
||||||
|
const requestId = await availability.getAssignedWork(this.reputationPublicKey);
|
||||||
|
const request = await business.getRequest(requestId);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
async submitWork(business, requestId, evidence, { tokenLossRatio, duration }) {
|
||||||
|
this.actions.submitWork.log(this, business);
|
||||||
|
return business.submitWork(this.reputationPublicKey, requestId, evidence, { tokenLossRatio, duration });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { Action } from './action.js';
|
||||||
|
import { Actor } from './actor.js';
|
||||||
|
|
||||||
|
export class Public extends Actor {
|
||||||
|
constructor(name, scene) {
|
||||||
|
super(name, scene);
|
||||||
|
this.actions = {
|
||||||
|
submitRequest: new Action('submit work request', scene),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async submitRequest(business, { fee }, content) {
|
||||||
|
this.actions.submitRequest.log(this, business, `(fee: ${fee})`);
|
||||||
|
return business.submitRequest(fee, content);
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ import params from './params.js';
|
||||||
const ValidationPoolStates = Object.freeze({
|
const ValidationPoolStates = Object.freeze({
|
||||||
OPEN: 'OPEN',
|
OPEN: 'OPEN',
|
||||||
CLOSED: 'CLOSED',
|
CLOSED: 'CLOSED',
|
||||||
|
RESOLVED: 'RESOLVED',
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,6 +25,7 @@ export class ValidationPool extends Actor {
|
||||||
tokenLossRatio,
|
tokenLossRatio,
|
||||||
contentiousDebate = false,
|
contentiousDebate = false,
|
||||||
authorStake = 0,
|
authorStake = 0,
|
||||||
|
anonymous = true,
|
||||||
},
|
},
|
||||||
name,
|
name,
|
||||||
scene,
|
scene,
|
||||||
|
@ -71,10 +73,19 @@ export class ValidationPool extends Actor {
|
||||||
// tokens minted "for" the post go toward stake of author voting for their own post
|
// 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
|
// also, author can provide additional stakes, e.g. availability stakes for work evidence post
|
||||||
console.log('initiateValidationPool casting vote', { signingPublicKey });
|
console.log('initiateValidationPool casting vote', { signingPublicKey });
|
||||||
this.castVote(signingPublicKey, true, this.tokens.for + authorStake, 0);
|
this.castVote(signingPublicKey, {
|
||||||
|
position: true,
|
||||||
|
stake: this.tokens.for + authorStake,
|
||||||
|
anonymous,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
castVote(signingPublicKey, position, stake, lockingTime) {
|
async castVote(signingPublicKey, {
|
||||||
|
position, stake, lockingTime = 0, anonymous = true,
|
||||||
|
}) {
|
||||||
|
console.log('castVote', {
|
||||||
|
signingPublicKey, position, stake, anonymous,
|
||||||
|
});
|
||||||
const vote = new Vote(position, stake, lockingTime);
|
const vote = new Vote(position, stake, lockingTime);
|
||||||
if (this.state === ValidationPoolStates.CLOSED) {
|
if (this.state === ValidationPoolStates.CLOSED) {
|
||||||
throw new Error(`Validation pool ${this.id} is closed`);
|
throw new Error(`Validation pool ${this.id} is closed`);
|
||||||
|
@ -85,6 +96,10 @@ export class ValidationPool extends Actor {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.votes.set(signingPublicKey, vote);
|
this.votes.set(signingPublicKey, vote);
|
||||||
|
if (!anonymous) {
|
||||||
|
console.log('castVote: revealing identity since this is not an anonymous vote');
|
||||||
|
await this.revealIdentity(signingPublicKey, signingPublicKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
listVotes(position) {
|
listVotes(position) {
|
||||||
|
@ -95,7 +110,7 @@ export class ValidationPool extends Actor {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
revealIdentity(signingPublicKey, reputationPublicKey) {
|
async revealIdentity(signingPublicKey, reputationPublicKey) {
|
||||||
if (!this.votes.get(signingPublicKey)) {
|
if (!this.votes.get(signingPublicKey)) {
|
||||||
throw new Error('Must vote before revealing identity');
|
throw new Error('Must vote before revealing identity');
|
||||||
}
|
}
|
||||||
|
@ -104,23 +119,6 @@ export class ValidationPool extends Actor {
|
||||||
voter.addVoteRecord(this);
|
voter.addVoteRecord(this);
|
||||||
this.bench.voters.set(reputationPublicKey, voter);
|
this.bench.voters.set(reputationPublicKey, voter);
|
||||||
this.voters.set(signingPublicKey, voter);
|
this.voters.set(signingPublicKey, voter);
|
||||||
if (this.votes.size === this.voters.size) {
|
|
||||||
// All voters have revealed their reputation public keys
|
|
||||||
// Now we can evaluate winning conditions
|
|
||||||
this.state = ValidationPoolStates.CLOSED;
|
|
||||||
this.setStatus('Closed');
|
|
||||||
const result = this.evaluateWinningConditions();
|
|
||||||
if (result === null) {
|
|
||||||
this.setStatus('Closed - Quorum not met');
|
|
||||||
this.scene.log(`note over ${this.name} : Quorum not met`);
|
|
||||||
} else {
|
|
||||||
this.setStatus(`Closed - ${result ? 'Won' : 'Lost'}`);
|
|
||||||
this.scene.log(`note over ${this.name} : ${result ? 'Win' : 'Lose'}`);
|
|
||||||
this.applyTokenLocking();
|
|
||||||
this.distributeTokens(result);
|
|
||||||
}
|
|
||||||
this.deactivate();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getTokenLossRatio() {
|
getTokenLossRatio() {
|
||||||
|
@ -163,7 +161,21 @@ export class ValidationPool extends Actor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
evaluateWinningConditions() {
|
async evaluateWinningConditions() {
|
||||||
|
if (this.state === ValidationPoolStates.RESOLVED) {
|
||||||
|
throw new Error('Validation pool has already been resolved!');
|
||||||
|
}
|
||||||
|
const elapsed = new Date() - this.dateStart;
|
||||||
|
if (elapsed < this.duration) {
|
||||||
|
throw new Error(`Validation pool duration has not yet elapsed! ${this.duration - elapsed} ms remaining.`);
|
||||||
|
}
|
||||||
|
if (this.voters.size < this.votes.size) {
|
||||||
|
throw new Error('Not all voters have revealed their reputation public keys!');
|
||||||
|
}
|
||||||
|
// Now we can evaluate winning conditions
|
||||||
|
this.state = ValidationPoolStates.CLOSED;
|
||||||
|
this.setStatus('Closed');
|
||||||
|
|
||||||
const getVoteValue = ({ stake, lockingTime }) => stake * lockingTime ** params.lockingTimeExponent;
|
const getVoteValue = ({ stake, lockingTime }) => stake * lockingTime ** params.lockingTimeExponent;
|
||||||
const getTotalValue = (position) => Array.from(this.listVotes(position).values())
|
const getTotalValue = (position) => Array.from(this.listVotes(position).values())
|
||||||
.map(getVoteValue)
|
.map(getVoteValue)
|
||||||
|
@ -175,7 +187,21 @@ export class ValidationPool extends Actor {
|
||||||
const votePasses = upvoteValue >= params.winningRatio * downvoteValue;
|
const votePasses = upvoteValue >= params.winningRatio * downvoteValue;
|
||||||
const quorumMet = upvoteValue + downvoteValue >= params.quorum * activeAvailableReputation;
|
const quorumMet = upvoteValue + downvoteValue >= params.quorum * activeAvailableReputation;
|
||||||
|
|
||||||
return quorumMet ? votePasses : null;
|
const result = quorumMet ? votePasses : null;
|
||||||
|
|
||||||
|
if (result === null) {
|
||||||
|
this.setStatus('Resolved - Quorum not met');
|
||||||
|
this.scene.log(`note over ${this.name} : Quorum not met`);
|
||||||
|
} else {
|
||||||
|
this.setStatus(`Resolved - ${result ? 'Won' : 'Lost'}`);
|
||||||
|
this.scene.log(`note over ${this.name} : ${result ? 'Win' : 'Lose'}`);
|
||||||
|
this.applyTokenLocking();
|
||||||
|
this.distributeTokens(result);
|
||||||
|
}
|
||||||
|
this.deactivate();
|
||||||
|
this.state = ValidationPoolStates.RESOLVED;
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
distributeTokens(result) {
|
distributeTokens(result) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<head>
|
<head>
|
||||||
<title>Forum Graph</title>
|
<title>Forum Graph: Debounce test</title>
|
||||||
<script type="module" src="./debounce-test.js" defer></script>
|
<script type="module" src="./debounce-test.js" defer></script>
|
||||||
<link type="text/css" rel="stylesheet" href="./index.css" />
|
<link type="text/css" rel="stylesheet" href="./index.css" />
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<head>
|
<head>
|
||||||
<title>Forum</title>
|
<title>Forum Network test</title>
|
||||||
<script type="module" src="./forum-network-test.js" defer></script>
|
<script type="module" src="./forum-network-test.js" defer></script>
|
||||||
<link type="text/css" rel="stylesheet" href="./index.css" />
|
<link type="text/css" rel="stylesheet" href="./index.css" />
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -1,20 +1,24 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<head>
|
<head>
|
||||||
<title>Forum Network</title>
|
<title>Mermaid test</title>
|
||||||
<link type="text/css" rel="stylesheet" href="./index.css" />
|
<link type="text/css" rel="stylesheet" href="./index.css" />
|
||||||
|
|
||||||
<script type="module" defer>
|
<script type="module" defer>
|
||||||
// import mermaid from './mermaid.mjs';
|
// import mermaid from './mermaid.mjs';
|
||||||
import mermaid from 'https://unpkg.com/mermaid@9.2.2/dist/mermaid.esm.mjs';
|
import mermaid from "https://unpkg.com/mermaid@9.2.2/dist/mermaid.esm.mjs";
|
||||||
mermaid.mermaidAPI.initialize({ startOnLoad: false });
|
mermaid.mermaidAPI.initialize({ startOnLoad: false });
|
||||||
// Example of using the API var
|
// Example of using the API var
|
||||||
const element = document.querySelector('#graphDiv');
|
const element = document.querySelector("#graphDiv");
|
||||||
const insertSvg = function (svgCode, bindFunctions) {
|
const insertSvg = function (svgCode, bindFunctions) {
|
||||||
element.innerHTML = svgCode;
|
element.innerHTML = svgCode;
|
||||||
};
|
};
|
||||||
const graphDefinition = 'graph TB\na-->b';
|
const graphDefinition = "graph TB\na-->b";
|
||||||
const graph = await mermaid.mermaidAPI.render('graphDiv', graphDefinition, insertSvg);
|
const graph = await mermaid.mermaidAPI.render(
|
||||||
const div = document.createElement('div');
|
"graphDiv",
|
||||||
|
graphDefinition,
|
||||||
|
insertSvg
|
||||||
|
);
|
||||||
|
const div = document.createElement("div");
|
||||||
div.innerHTML = graph;
|
div.innerHTML = graph;
|
||||||
document.body.append(div);
|
document.body.append(div);
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<head>
|
<head>
|
||||||
<title>Forum</title>
|
<title>Validation Pool test</title>
|
||||||
<script type="module" src="./validation-pool-test.js" defer></script>
|
<script type="module" src="./validation-pool-test.js" defer></script>
|
||||||
<link type="text/css" rel="stylesheet" href="./index.css" />
|
<link type="text/css" rel="stylesheet" href="./index.css" />
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -30,8 +30,9 @@ await delay(1000);
|
||||||
// First member can self-approve
|
// First member can self-approve
|
||||||
{
|
{
|
||||||
const pool = await member1.initiateValidationPool(bench, { fee: 7, duration: 1000, tokenLossRatio: 1 });
|
const pool = await member1.initiateValidationPool(bench, { fee: 7, duration: 1000, tokenLossRatio: 1 });
|
||||||
// await member1.castVote(pool, true, 0, 0);
|
await member1.revealIdentity(pool);
|
||||||
await member1.revealIdentity(pool); // Vote passes
|
await delay(1000);
|
||||||
|
await pool.evaluateWinningConditions(); // Vote passes
|
||||||
await updateDisplayValues();
|
await updateDisplayValues();
|
||||||
await delay(1000);
|
await delay(1000);
|
||||||
}
|
}
|
||||||
|
@ -39,7 +40,9 @@ await delay(1000);
|
||||||
// Failure example: second member can not self-approve
|
// Failure example: second member can not self-approve
|
||||||
try {
|
try {
|
||||||
const pool = await member2.initiateValidationPool(bench, { fee: 1, duration: 1000, tokenLossRatio: 1 });
|
const pool = await member2.initiateValidationPool(bench, { fee: 1, duration: 1000, tokenLossRatio: 1 });
|
||||||
await member2.revealIdentity(pool); // Quorum not met!
|
await member2.revealIdentity(pool);
|
||||||
|
await delay(1000);
|
||||||
|
await pool.evaluateWinningConditions(); // Quorum not met!
|
||||||
await updateDisplayValues();
|
await updateDisplayValues();
|
||||||
await delay(1000);
|
await delay(1000);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -56,7 +59,9 @@ try {
|
||||||
const pool = await member2.initiateValidationPool(bench, { fee: 1, duration: 1000, tokenLossRatio: 1 });
|
const pool = await member2.initiateValidationPool(bench, { fee: 1, duration: 1000, tokenLossRatio: 1 });
|
||||||
await member1.castVote(pool, { position: true, stake: 4, lockingTime: 0 });
|
await member1.castVote(pool, { position: true, stake: 4, lockingTime: 0 });
|
||||||
await member1.revealIdentity(pool);
|
await member1.revealIdentity(pool);
|
||||||
await member2.revealIdentity(pool); // Vote passes
|
await member2.revealIdentity(pool);
|
||||||
|
await delay(1000);
|
||||||
|
await pool.evaluateWinningConditions(); // Vote passes
|
||||||
await updateDisplayValues();
|
await updateDisplayValues();
|
||||||
await delay(1000);
|
await delay(1000);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue