eslint fixes
This commit is contained in:
parent
d63a93562f
commit
59c10f1ac2
|
@ -3,13 +3,27 @@ module.exports = {
|
||||||
browser: true,
|
browser: true,
|
||||||
es2021: true,
|
es2021: true,
|
||||||
},
|
},
|
||||||
extends: 'airbnb-base',
|
extends: ['airbnb-base'],
|
||||||
overrides: [],
|
overrides: [],
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
ecmaVersion: 'latest',
|
ecmaVersion: 'latest',
|
||||||
sourceType: 'module',
|
sourceType: 'module',
|
||||||
},
|
},
|
||||||
|
// plugins: ["import"],
|
||||||
rules: {
|
rules: {
|
||||||
'import/extenstions': ['always'],
|
'import/extensions': ['error', 'always'],
|
||||||
|
'import/prefer-default-export': ['off'],
|
||||||
|
'import/no-unresolved': ['error', { ignore: ['^http'] }],
|
||||||
|
'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
|
||||||
|
'max-classes-per-file': ['off'],
|
||||||
|
'no-param-reassign': ['off'],
|
||||||
|
'no-plusplus': ['off'],
|
||||||
|
'no-restricted-syntax': ['off'],
|
||||||
|
'max-len': ['warn', 120],
|
||||||
|
'no-console': ['off'],
|
||||||
|
'no-return-assign': ['off'],
|
||||||
|
'no-multi-assign': ['off'],
|
||||||
|
'no-constant-condition': ['off'],
|
||||||
|
'no-await-in-loop': ['off'],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Challenges
|
||||||
|
|
||||||
|
- Receiving payments
|
||||||
|
- Distributing payments to participants
|
||||||
|
- Computing updates to forum graph
|
|
@ -1,5 +1,5 @@
|
||||||
import { Box } from "./classes/box.js";
|
import { Box } from './classes/box.js';
|
||||||
import { Scene } from "./classes/scene.js";
|
import { Scene } from './classes/scene.js';
|
||||||
|
|
||||||
const rootElement = document.getElementById('basic');
|
const rootElement = document.getElementById('basic');
|
||||||
const rootBox = new Box('rootBox', rootElement).flex();
|
const rootBox = new Box('rootBox', rootElement).flex();
|
||||||
|
@ -11,7 +11,9 @@ function randomDelay(min, max) {
|
||||||
|
|
||||||
function delay(min, max = min) {
|
function delay(min, max = min) {
|
||||||
const delayMs = min + Math.random() * (max - min);
|
const delayMs = min + Math.random() * (max - min);
|
||||||
return new Promise((resolve) => setTimeout(resolve, delayMs));
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, delayMs);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (true) {
|
if (true) {
|
||||||
|
@ -46,7 +48,7 @@ if (true) {
|
||||||
}, randomDelay(500, 1500));
|
}, randomDelay(500, 1500));
|
||||||
});
|
});
|
||||||
|
|
||||||
blockchain.on(readBlockchainData, (src, detail) => {
|
blockchain.on(readBlockchainData, (src, _detail) => {
|
||||||
blockchainStatus.set('Processing request');
|
blockchainStatus.set('Processing request');
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
blockchain.send(src, blockchainData, {});
|
blockchain.send(src, blockchainData, {});
|
||||||
|
@ -64,7 +66,7 @@ if (true) {
|
||||||
}, randomDelay(6000, 12000));
|
}, randomDelay(6000, 12000));
|
||||||
}
|
}
|
||||||
|
|
||||||
(async function () {
|
(async function run() {
|
||||||
const scene = new Scene('Scene 2', rootBox);
|
const scene = new Scene('Scene 2', rootBox);
|
||||||
|
|
||||||
const webClient = scene.addActor('webClient');
|
const webClient = scene.addActor('webClient');
|
||||||
|
@ -162,12 +164,12 @@ if (true) {
|
||||||
});
|
});
|
||||||
|
|
||||||
node.on(memoryData, (_src, _data) => {
|
node.on(memoryData, (_src, _data) => {
|
||||||
node.on(storageData, (_src, _data) => {
|
node.on(storageData, (__src, __data) => {
|
||||||
if (detail?.readConcern === 'single') {
|
if (detail?.readConcern === 'single') {
|
||||||
node.send(node, considerInfo, {});
|
node.send(node, considerInfo, {});
|
||||||
} else {
|
} else {
|
||||||
const peer = getPeer(node);
|
const peer = getPeer(node);
|
||||||
node.on(qualifiedOpinions, (_src, info) => {
|
node.on(qualifiedOpinions, (___src, info) => {
|
||||||
node.send(node, considerInfo, info);
|
node.send(node, considerInfo, info);
|
||||||
});
|
});
|
||||||
node.send(peer, seekTruth, { readConcern: 'single' });
|
node.send(peer, seekTruth, { readConcern: 'single' });
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { Actor } from './actor.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Purpose: Enable staking reputation to enter the pool of workers
|
||||||
|
*/
|
||||||
|
export class Availability extends Actor {}
|
|
@ -1,9 +1,12 @@
|
||||||
import { Actor } from "./actor.js";
|
import { Actor } from './actor.js';
|
||||||
import { Reputations } from "./reputation.js";
|
import { Reputations } from './reputation.js';
|
||||||
import { ValidationPool } from "./validation-pool.js";
|
import { ValidationPool } from './validation-pool.js';
|
||||||
import params from "./params.js";
|
import params from './params.js';
|
||||||
import { Action } from "./action.js";
|
import { Action } from './action.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Purpose: Keep track of reputation holders
|
||||||
|
*/
|
||||||
export class Bench extends Actor {
|
export class Bench extends Actor {
|
||||||
constructor(name, scene) {
|
constructor(name, scene) {
|
||||||
super(name, scene);
|
super(name, scene);
|
||||||
|
@ -12,7 +15,7 @@ export class Bench extends Actor {
|
||||||
this.reputations = new Reputations();
|
this.reputations = new Reputations();
|
||||||
|
|
||||||
this.actions = {
|
this.actions = {
|
||||||
createValidationPool: new Action("create validation pool", scene),
|
createValidationPool: new Action('create validation pool', scene),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.activate();
|
this.activate();
|
||||||
|
@ -27,8 +30,7 @@ export class Bench extends Actor {
|
||||||
const thresholdSet = !!params.activeVoterThreshold;
|
const thresholdSet = !!params.activeVoterThreshold;
|
||||||
return Array.from(this.voters.values()).filter((voter) => {
|
return Array.from(this.voters.values()).filter((voter) => {
|
||||||
const hasVoted = !!voter.dateLastVote;
|
const hasVoted = !!voter.dateLastVote;
|
||||||
const withinThreshold =
|
const withinThreshold = now - voter.dateLastVote >= params.activeVoterThreshold;
|
||||||
now - voter.dateLastVote >= params.activeVoterThreshold;
|
|
||||||
return hasVoted && (!thresholdSet || withinThreshold);
|
return hasVoted && (!thresholdSet || withinThreshold);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -43,17 +45,13 @@ export class Bench extends Actor {
|
||||||
|
|
||||||
getTotalActiveReputation() {
|
getTotalActiveReputation() {
|
||||||
return this.listActiveVoters()
|
return this.listActiveVoters()
|
||||||
.map(({ reputationPublicKey }) =>
|
.map(({ reputationPublicKey }) => this.reputations.getTokens(reputationPublicKey))
|
||||||
this.reputations.getTokens(reputationPublicKey)
|
|
||||||
)
|
|
||||||
.reduce((acc, cur) => (acc += cur), 0);
|
.reduce((acc, cur) => (acc += cur), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
getTotalActiveAvailableReputation() {
|
getTotalActiveAvailableReputation() {
|
||||||
return this.listActiveVoters()
|
return this.listActiveVoters()
|
||||||
.map(({ reputationPublicKey }) =>
|
.map(({ reputationPublicKey }) => this.reputations.getAvailableTokens(reputationPublicKey))
|
||||||
this.reputations.getAvailableTokens(reputationPublicKey)
|
|
||||||
)
|
|
||||||
.reduce((acc, cur) => (acc += cur), 0);
|
.reduce((acc, cur) => (acc += cur), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +64,7 @@ export class Bench extends Actor {
|
||||||
tokenLossRatio,
|
tokenLossRatio,
|
||||||
contentiousDebate,
|
contentiousDebate,
|
||||||
signingPublicKey,
|
signingPublicKey,
|
||||||
}
|
},
|
||||||
) {
|
) {
|
||||||
const validationPoolNumber = this.validationPools.size + 1;
|
const validationPoolNumber = this.validationPools.size + 1;
|
||||||
const validationPool = new ValidationPool(
|
const validationPool = new ValidationPool(
|
||||||
|
@ -81,7 +79,7 @@ export class Bench extends Actor {
|
||||||
signingPublicKey,
|
signingPublicKey,
|
||||||
},
|
},
|
||||||
`pool${validationPoolNumber}`,
|
`pool${validationPoolNumber}`,
|
||||||
scene
|
this.scene,
|
||||||
);
|
);
|
||||||
this.validationPools.set(validationPool.id, validationPool);
|
this.validationPools.set(validationPool.id, validationPool);
|
||||||
this.actions.createValidationPool.log(this, validationPool);
|
this.actions.createValidationPool.log(this, validationPool);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {DisplayValue} from "./display-value.js";
|
import { DisplayValue } from './display-value.js';
|
||||||
|
|
||||||
export class Box {
|
export class Box {
|
||||||
constructor(name, parentEl, elementType = 'div') {
|
constructor(name, parentEl, elementType = 'div') {
|
||||||
|
@ -47,7 +47,7 @@ export class Box {
|
||||||
}
|
}
|
||||||
|
|
||||||
setId(id) {
|
setId(id) {
|
||||||
this.el.id = (id || this.name).replace(/ /g, "");
|
this.el.id = (id || this.name).replace(/ /g, '');
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { Actor } from './actor.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Purpose: Enable fee-driven work requests, to be completed by workers from the availability pool
|
||||||
|
*/
|
||||||
|
export class Business extends Actor {
|
||||||
|
// constructor(name, scene) { super(name, scene); }
|
||||||
|
|
||||||
|
// submitRequest(fee) {}
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ export class CryptoUtil {
|
||||||
static hash = 'SHA-256';
|
static hash = 'SHA-256';
|
||||||
|
|
||||||
static async generateAsymmetricKey() {
|
static async generateAsymmetricKey() {
|
||||||
return await window.crypto.subtle.generateKey(
|
return window.crypto.subtle.generateKey(
|
||||||
{
|
{
|
||||||
name: CryptoUtil.algorithm,
|
name: CryptoUtil.algorithm,
|
||||||
hash: CryptoUtil.hash,
|
hash: CryptoUtil.hash,
|
||||||
|
@ -32,7 +32,7 @@ export class CryptoUtil {
|
||||||
// TODO: make a single TextEncoder instance and reuse it
|
// TODO: make a single TextEncoder instance and reuse it
|
||||||
const encoder = new TextEncoder();
|
const encoder = new TextEncoder();
|
||||||
const encoded = encoder.encode(content);
|
const encoded = encoder.encode(content);
|
||||||
return await window.crypto.subtle.verify(CryptoUtil.algorithm, publicKey, signature, encoded);
|
return window.crypto.subtle.verify(CryptoUtil.algorithm, publicKey, signature, encoded);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async exportKey(publicKey) {
|
static async exportKey(publicKey) {
|
||||||
|
@ -44,7 +44,7 @@ export class CryptoUtil {
|
||||||
static async importKey(b64jwk) {
|
static async importKey(b64jwk) {
|
||||||
// Convert base64 javascript web key to CryptoKey
|
// Convert base64 javascript web key to CryptoKey
|
||||||
const jwk = JSON.parse(atob(b64jwk));
|
const jwk = JSON.parse(atob(b64jwk));
|
||||||
return await window.crypto.subtle.importKey(
|
return window.crypto.subtle.importKey(
|
||||||
'jwk',
|
'jwk',
|
||||||
jwk,
|
jwk,
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { Actor } from './actor.js';
|
import { Actor } from './actor.js';
|
||||||
import { Action } from './action.js';
|
import { Action } from './action.js';
|
||||||
import { Message, PostMessage, PeerMessage } from './message.js';
|
import {
|
||||||
|
Message, PostMessage, PeerMessage, messageFromJSON,
|
||||||
|
} from './message.js';
|
||||||
import { CryptoUtil } from './crypto.js';
|
import { CryptoUtil } from './crypto.js';
|
||||||
import { ForumView } from './forum-view.js';
|
import { ForumView } from './forum-view.js';
|
||||||
import { PrioritizedQueue } from './prioritized-queue.js';
|
import { PrioritizedQueue } from './prioritized-queue.js';
|
||||||
|
@ -51,7 +53,7 @@ export class ForumNode extends Actor {
|
||||||
if (!messageJson) {
|
if (!messageJson) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
await this.processMessage(messageJson);
|
return this.processMessage(messageJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process a message from the queue
|
// Process a message from the queue
|
||||||
|
@ -65,7 +67,7 @@ export class ForumNode extends Actor {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { publicKey } = messageJson;
|
const { publicKey } = messageJson;
|
||||||
const message = Message.fromJSON(messageJson);
|
const message = messageFromJSON(messageJson);
|
||||||
console.log(`${this.name}: processMessage`, message);
|
console.log(`${this.name}: processMessage`, message);
|
||||||
|
|
||||||
if (message instanceof PostMessage) {
|
if (message instanceof PostMessage) {
|
||||||
|
|
|
@ -34,7 +34,7 @@ export class ForumView {
|
||||||
this.reputations.set(id, reputation);
|
this.reputations.set(id, reputation);
|
||||||
}
|
}
|
||||||
|
|
||||||
incrementReputation(publicKey, increment, reason) {
|
incrementReputation(publicKey, increment, _reason) {
|
||||||
const reputation = this.getReputation(publicKey) || 0;
|
const reputation = this.getReputation(publicKey) || 0;
|
||||||
return this.reputations.set(publicKey, reputation + increment);
|
return this.reputations.set(publicKey, reputation + increment);
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,8 @@ export class ForumView {
|
||||||
// in order to arrive at the current view.
|
// in order to arrive at the current view.
|
||||||
|
|
||||||
// When an author stakes reputation on a post, if it's a non-binding stake, then it merely expresses opinion.
|
// When an author stakes reputation on a post, if it's a non-binding stake, then it merely expresses opinion.
|
||||||
// If it's a binding stake, then they may lose the staked reputation as a result of other posts staking reputation against theirs.
|
// If it's a binding stake, then they may lose the staked reputation as a result of other posts
|
||||||
|
// staking reputation against theirs.
|
||||||
|
|
||||||
citationFraction = 0.3;
|
citationFraction = 0.3;
|
||||||
|
|
||||||
|
@ -104,7 +105,8 @@ export class ForumView {
|
||||||
// Some of the incoming reputation gets distributed among cited posts
|
// Some of the incoming reputation gets distributed among cited posts
|
||||||
const distributeAmongCitations = amount * this.citationFraction;
|
const distributeAmongCitations = amount * this.citationFraction;
|
||||||
|
|
||||||
// citation weights can be interpreted as a ratio, or we can somehow constrain the input to add up to some specified total.
|
// citation weights can be interpreted as a ratio, or we can somehow constrain the input
|
||||||
|
// to add up to some specified total.
|
||||||
// It's easy enough to let them be on any arbitrary scale and just compute the ratios here.
|
// It's easy enough to let them be on any arbitrary scale and just compute the ratios here.
|
||||||
const totalWeight = post.citations
|
const totalWeight = post.citations
|
||||||
?.map(({ weight }) => weight)
|
?.map(({ weight }) => weight)
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { Actor } from "./actor.js";
|
import { Actor } from './actor.js';
|
||||||
import { Graph, Vertex } from "./graph.js";
|
import { Graph, Vertex } from './graph.js';
|
||||||
import params from "./params.js";
|
import params from './params.js';
|
||||||
|
|
||||||
class Post extends Vertex {
|
class Post extends Vertex {
|
||||||
constructor(forum, id, authorId, citations) {
|
constructor(forum, id, authorId, citations) {
|
||||||
|
super();
|
||||||
this.forum = forum;
|
this.forum = forum;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.authorId = authorId;
|
this.authorId = authorId;
|
||||||
|
@ -17,6 +18,9 @@ class Post extends Vertex {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Purpose: Maintain a directed, acyclic, weighted graph of posts referencing other posts
|
||||||
|
*/
|
||||||
export class Forum extends Actor {
|
export class Forum extends Actor {
|
||||||
constructor(bench, name, scene) {
|
constructor(bench, name, scene) {
|
||||||
super(name, scene);
|
super(name, scene);
|
||||||
|
@ -24,11 +28,13 @@ export class Forum extends Actor {
|
||||||
this.posts = new Graph();
|
this.posts = new Graph();
|
||||||
}
|
}
|
||||||
|
|
||||||
async addPost({ authorId, postId, citations = [], poolParams }) {
|
async addPost({
|
||||||
|
authorId, postId, citations = [], poolParams,
|
||||||
|
}) {
|
||||||
const post = new Post(this, postId, authorId);
|
const post = new Post(this, postId, authorId);
|
||||||
this.posts.addVertex(postId, post);
|
this.posts.addVertex(postId, post);
|
||||||
for (const { postId: citedPostId, weight } of citations) {
|
for (const { postId: citedPostId, weight } of citations) {
|
||||||
this.posts.addEdge("citation", postId, citedPostId, { weight });
|
this.posts.addEdge('citation', postId, citedPostId, { weight });
|
||||||
}
|
}
|
||||||
// this.applyReputationEffects(post);
|
// this.applyReputationEffects(post);
|
||||||
// initiateValidationPool(authorId, {postId, fee, duration, tokenLossRatio, contentiousDebate, signingPublicKey}) {
|
// initiateValidationPool(authorId, {postId, fee, duration, tokenLossRatio, contentiousDebate, signingPublicKey}) {
|
||||||
|
@ -48,7 +54,7 @@ export class Forum extends Actor {
|
||||||
}
|
}
|
||||||
|
|
||||||
distributeReputation(post, amount, depth = 0) {
|
distributeReputation(post, amount, depth = 0) {
|
||||||
console.log("distributeReputation", { post, amount, depth });
|
console.log('distributeReputation', { post, amount, depth });
|
||||||
// Add the given value to the current post
|
// Add the given value to the current post
|
||||||
post.value += amount;
|
post.value += amount;
|
||||||
// Distribute a fraction of the added value among cited posts
|
// Distribute a fraction of the added value among cited posts
|
||||||
|
@ -63,17 +69,17 @@ export class Forum extends Actor {
|
||||||
for (const {
|
for (const {
|
||||||
to: citedPostId,
|
to: citedPostId,
|
||||||
data: { weight },
|
data: { weight },
|
||||||
} of post.getEdges("citation", true)) {
|
} of post.getEdges('citation', true)) {
|
||||||
const citedPost = this.getPost(citedPostId);
|
const citedPost = this.getPost(citedPostId);
|
||||||
if (!citedPost) {
|
if (!citedPost) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Post ${post.postId} cites unknown post ${citedPostId}`
|
`Post ${post.postId} cites unknown post ${citedPostId}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.distributeReputation(
|
this.distributeReputation(
|
||||||
citedPost,
|
citedPost,
|
||||||
(weight / totalWeight) * distributeAmongCitations,
|
(weight / totalWeight) * distributeAmongCitations,
|
||||||
depth + 1
|
depth + 1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,8 @@ export class Vertex {
|
||||||
}
|
}
|
||||||
|
|
||||||
getEdges(label, away) {
|
getEdges(label, away) {
|
||||||
return this.edges[away ? "from" : "to"].filter(
|
return this.edges[away ? 'from' : 'to'].filter(
|
||||||
(edge) => edge.label === label
|
(edge) => edge.label === label,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ export class Graph {
|
||||||
|
|
||||||
addVertex(id, data) {
|
addVertex(id, data) {
|
||||||
// Support simple case of auto-incremented numeric ids
|
// Support simple case of auto-incremented numeric ids
|
||||||
if (typeof id === "object") {
|
if (typeof id === 'object') {
|
||||||
data = id;
|
data = id;
|
||||||
id = this.nextVertexId++;
|
id = this.nextVertexId++;
|
||||||
}
|
}
|
||||||
|
@ -85,8 +85,7 @@ export class Graph {
|
||||||
return edgeLabels.flatMap((edgeLabel) => {
|
return edgeLabels.flatMap((edgeLabel) => {
|
||||||
const edges = this.edgeLabels.get(edgeLabel);
|
const edges = this.edgeLabels.get(edgeLabel);
|
||||||
return Array.from(edges?.values() || []).filter((edge) => {
|
return Array.from(edges?.values() || []).filter((edge) => {
|
||||||
const matchFrom =
|
const matchFrom = from === null || from === undefined || from === edge.from;
|
||||||
from === null || from === undefined || from === edge.from;
|
|
||||||
const matchTo = to === null || to === undefined || to === edge.to;
|
const matchTo = to === null || to === undefined || to === edge.to;
|
||||||
return matchFrom && matchTo;
|
return matchFrom && matchTo;
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
import { Actor } from "./actor.js";
|
import { Actor } from './actor.js';
|
||||||
import { Action } from "./action.js";
|
|
||||||
import { PostMessage } from "./message.js";
|
import { Action } from './action.js';
|
||||||
import { CryptoUtil } from "./crypto.js";
|
import { PostMessage } from './message.js';
|
||||||
|
import { CryptoUtil } from './crypto.js';
|
||||||
|
|
||||||
export class Member extends Actor {
|
export class Member extends Actor {
|
||||||
constructor(name, scene) {
|
constructor(name, scene) {
|
||||||
super(name, scene);
|
super(name, scene);
|
||||||
this.actions = {
|
this.actions = {
|
||||||
submitPost: new Action("submit post", scene),
|
submitPost: new Action('submit post', scene),
|
||||||
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),
|
||||||
};
|
};
|
||||||
this.validationPools = new Map();
|
this.validationPools = new Map();
|
||||||
}
|
}
|
||||||
|
@ -19,7 +20,7 @@ export class Member extends Actor {
|
||||||
this.reputationKey = await CryptoUtil.generateAsymmetricKey();
|
this.reputationKey = await CryptoUtil.generateAsymmetricKey();
|
||||||
// this.reputationPublicKey = await CryptoUtil.exportKey(this.reputationKey.publicKey);
|
// this.reputationPublicKey = await CryptoUtil.exportKey(this.reputationKey.publicKey);
|
||||||
this.reputationPublicKey = this.name;
|
this.reputationPublicKey = this.name;
|
||||||
this.status.set("Initialized");
|
this.status.set('Initialized');
|
||||||
this.activate();
|
this.activate();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -40,7 +41,7 @@ export class Member extends Actor {
|
||||||
this.actions.initiateValidationPool.log(
|
this.actions.initiateValidationPool.log(
|
||||||
this,
|
this,
|
||||||
bench,
|
bench,
|
||||||
`(fee: ${options.fee})`
|
`(fee: ${options.fee})`,
|
||||||
);
|
);
|
||||||
const pool = bench.initiateValidationPool(this.reputationPublicKey, {
|
const pool = bench.initiateValidationPool(this.reputationPublicKey, {
|
||||||
...options,
|
...options,
|
||||||
|
@ -59,7 +60,7 @@ export class Member extends Actor {
|
||||||
this.actions.castVote.log(
|
this.actions.castVote.log(
|
||||||
this,
|
this,
|
||||||
validationPool,
|
validationPool,
|
||||||
`(${position ? "for" : "against"}, stake: ${stake})`
|
`(${position ? 'for' : 'against'}, stake: ${stake})`,
|
||||||
);
|
);
|
||||||
validationPool.castVote(signingPublicKey, position, stake, lockingTime);
|
validationPool.castVote(signingPublicKey, position, stake, lockingTime);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,23 +14,17 @@ export class Message {
|
||||||
}
|
}
|
||||||
|
|
||||||
static async verify({ content, publicKey, signature }) {
|
static async verify({ content, publicKey, signature }) {
|
||||||
return await CryptoUtil.verify(content, publicKey, signature);
|
return CryptoUtil.verify(content, publicKey, signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
static contentFromJSON(data) {
|
static contentFromJSON(data) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
contentToJSON(content) {
|
static contentToJSON(content) {
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJSON({ type, content }) {
|
|
||||||
const messageType = messageTypes.get(type) || Message;
|
|
||||||
const messageContent = messageType.contentFromJSON(content);
|
|
||||||
return new messageType(messageContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJSON() {
|
toJSON() {
|
||||||
return {
|
return {
|
||||||
type: this.type,
|
type: this.type,
|
||||||
|
@ -51,7 +45,7 @@ export class PostMessage extends Message {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
contentToJSON({ post, stake }) {
|
static contentToJSON({ post, stake }) {
|
||||||
return {
|
return {
|
||||||
post: post.toJSON(),
|
post: post.toJSON(),
|
||||||
stake,
|
stake,
|
||||||
|
@ -67,3 +61,9 @@ const messageTypes = new Map([
|
||||||
['post', PostMessage],
|
['post', PostMessage],
|
||||||
['peer', PeerMessage],
|
['peer', PeerMessage],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
export const messageFromJSON = ({ type, content }) => {
|
||||||
|
const MessageType = messageTypes.get(type) || Message;
|
||||||
|
const messageContent = MessageType.contentFromJSON(content);
|
||||||
|
return new MessageType(messageContent);
|
||||||
|
};
|
||||||
|
|
|
@ -1,4 +1,22 @@
|
||||||
import { CryptoUtil } from "./crypto.js";
|
import { CryptoUtil } from './crypto.js';
|
||||||
|
|
||||||
|
export class Citation {
|
||||||
|
constructor(postId, weight) {
|
||||||
|
this.postId = postId;
|
||||||
|
this.weight = weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
return {
|
||||||
|
postId: this.postId,
|
||||||
|
weight: this.weight,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromJSON({ postId, weight }) {
|
||||||
|
return new Citation(postId, weight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class Post {
|
export class Post {
|
||||||
constructor(content) {
|
constructor(content) {
|
||||||
|
@ -28,21 +46,3 @@ export class Post {
|
||||||
return post;
|
return post;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Citation {
|
|
||||||
constructor(postId, weight) {
|
|
||||||
this.postId = postId;
|
|
||||||
this.weight = weight;
|
|
||||||
}
|
|
||||||
|
|
||||||
toJSON() {
|
|
||||||
return {
|
|
||||||
postId: this.postId,
|
|
||||||
weight: this.weight,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJSON({ postId, weight }) {
|
|
||||||
return new Citation(postId, weight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -14,14 +14,14 @@ class Reputation {
|
||||||
|
|
||||||
addTokens(tokens) {
|
addTokens(tokens) {
|
||||||
if (this.tokens + tokens < 0) {
|
if (this.tokens + tokens < 0) {
|
||||||
throw new Error(`Token balance can not become negative`);
|
throw new Error('Token balance can not become negative');
|
||||||
}
|
}
|
||||||
this.tokens += tokens;
|
this.tokens += tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
lockTokens(tokens, duration) {
|
lockTokens(tokens, duration) {
|
||||||
if (tokens > this.getAvailableTokens()) {
|
if (tokens > this.getAvailableTokens()) {
|
||||||
throw new Error("Can not lock more tokens than are available")
|
throw new Error('Can not lock more tokens than are available');
|
||||||
}
|
}
|
||||||
const lock = new Lock(tokens, duration);
|
const lock = new Lock(tokens, duration);
|
||||||
this.locks.add(lock);
|
this.locks.add(lock);
|
||||||
|
@ -35,10 +35,10 @@ class Reputation {
|
||||||
getAvailableTokens() {
|
getAvailableTokens() {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const tokensLocked = Array.from(this.locks.values())
|
const tokensLocked = Array.from(this.locks.values())
|
||||||
.filter(({dateCreated, duration}) => now - dateCreated < duration)
|
.filter(({ dateCreated, duration }) => now - dateCreated < duration)
|
||||||
.reduce((acc, cur) => acc += cur.tokens, 0);
|
.reduce((acc, cur) => acc += cur.tokens, 0);
|
||||||
if (tokensLocked > this.tokens) {
|
if (tokensLocked > this.tokens) {
|
||||||
throw new Error("Assertion failure. tokensLocked > tokens");
|
throw new Error('Assertion failure. tokensLocked > tokens');
|
||||||
}
|
}
|
||||||
return this.tokens - tokensLocked;
|
return this.tokens - tokensLocked;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
import { Actor } from "./actor.js";
|
import mermaid from 'https://unpkg.com/mermaid@9.2.2/dist/mermaid.esm.min.mjs';
|
||||||
import { Action } from "./action.js";
|
import { Actor } from './actor.js';
|
||||||
import { debounce } from "../util.js";
|
import { Action } from './action.js';
|
||||||
import mermaid from "https://unpkg.com/mermaid@9.2.2/dist/mermaid.esm.min.mjs";
|
import { debounce } from '../util.js';
|
||||||
|
|
||||||
export class Scene {
|
export class Scene {
|
||||||
constructor(name, rootBox) {
|
constructor(name, rootBox) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.box = rootBox.addBox(name);
|
this.box = rootBox.addBox(name);
|
||||||
this.titleBox = this.box.addBox().setInnerHTML(name);
|
this.titleBox = this.box.addBox().setInnerHTML(name);
|
||||||
this.box.addBox("Spacer").setInnerHTML(" ");
|
this.box.addBox('Spacer').setInnerHTML(' ');
|
||||||
this.displayValuesBox = this.box.addBox(`${this.name}-values`);
|
this.displayValuesBox = this.box.addBox(`${this.name}-values`);
|
||||||
this.box.addBox("Spacer").setInnerHTML(" ");
|
this.box.addBox('Spacer').setInnerHTML(' ');
|
||||||
this.actors = new Set();
|
this.actors = new Set();
|
||||||
this.seqDiagramContainer = this.box.addBox(
|
this.seqDiagramContainer = this.box.addBox(
|
||||||
`${this.name}-seq-diagram-container`
|
`${this.name}-seq-diagram-container`,
|
||||||
);
|
);
|
||||||
this.seqDiagramElement = this.box
|
this.seqDiagramElement = this.box
|
||||||
.addBox(`${this.name}-seq-diagram-element`)
|
.addBox(`${this.name}-seq-diagram-element`)
|
||||||
.setId();
|
.setId();
|
||||||
this.seqDiagramBox = this.box.addBox(`${this.name}-seq-diagram`);
|
this.seqDiagramBox = this.box.addBox(`${this.name}-seq-diagram`);
|
||||||
this.box.addBox("Spacer").setInnerHTML(" ");
|
this.box.addBox('Spacer').setInnerHTML(' ');
|
||||||
this.logBox = this.box.addBox(`${this.name}-log`);
|
this.logBox = this.box.addBox(`${this.name}-log`);
|
||||||
mermaid.mermaidAPI.initialize({ startOnLoad: false });
|
mermaid.mermaidAPI.initialize({ startOnLoad: false });
|
||||||
this.dateLastRender = null;
|
this.dateLastRender = null;
|
||||||
|
@ -63,7 +63,7 @@ export class Scene {
|
||||||
const dateStart = new Date();
|
const dateStart = new Date();
|
||||||
const graph = await mermaid.mermaidAPI.render(
|
const graph = await mermaid.mermaidAPI.render(
|
||||||
this.seqDiagramElement.getId(),
|
this.seqDiagramElement.getId(),
|
||||||
this.logBox.getInnerText()
|
this.logBox.getInnerText(),
|
||||||
);
|
);
|
||||||
this.seqDiagramBox.setInnerHTML(graph);
|
this.seqDiagramBox.setInnerHTML(graph);
|
||||||
if (!this.dateLastRender) {
|
if (!this.dateLastRender) {
|
||||||
|
@ -72,7 +72,7 @@ export class Scene {
|
||||||
console.log(
|
console.log(
|
||||||
`renderSequenceDiagram time: ${
|
`renderSequenceDiagram time: ${
|
||||||
new Date() - dateStart
|
new Date() - dateStart
|
||||||
} ms, time since last render: ${dateStart - this.dateLastRender}`
|
} ms, time since last render: ${dateStart - this.dateLastRender}`,
|
||||||
);
|
);
|
||||||
this.dateLastRender = dateStart;
|
this.dateLastRender = dateStart;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
import { CryptoUtil } from "./crypto.js";
|
import { CryptoUtil } from './crypto.js';
|
||||||
import { Vote } from "./vote.js";
|
import { Vote } from './vote.js';
|
||||||
import { Voter } from "./voter.js";
|
import { Voter } from './voter.js';
|
||||||
import { Actor } from "./actor.js";
|
import { Actor } from './actor.js';
|
||||||
import params from "./params.js";
|
import params from './params.js';
|
||||||
|
|
||||||
const ValidationPoolStates = Object.freeze({
|
const ValidationPoolStates = Object.freeze({
|
||||||
OPEN: "OPEN",
|
OPEN: 'OPEN',
|
||||||
CLOSED: "CLOSED",
|
CLOSED: 'CLOSED',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Purpose: Enable voting
|
||||||
|
*/
|
||||||
export class ValidationPool extends Actor {
|
export class ValidationPool extends Actor {
|
||||||
constructor(
|
constructor(
|
||||||
bench,
|
bench,
|
||||||
|
@ -22,34 +25,34 @@ export class ValidationPool extends Actor {
|
||||||
contentiousDebate = false,
|
contentiousDebate = false,
|
||||||
},
|
},
|
||||||
name,
|
name,
|
||||||
scene
|
scene,
|
||||||
) {
|
) {
|
||||||
super(name, scene);
|
super(name, scene);
|
||||||
// If contentiousDebate = true, we will follow the progression defined by getTokenLossRatio()
|
// If contentiousDebate = true, we will follow the progression defined by getTokenLossRatio()
|
||||||
if (
|
if (
|
||||||
!contentiousDebate &&
|
!contentiousDebate
|
||||||
(tokenLossRatio < 0 ||
|
&& (tokenLossRatio < 0
|
||||||
tokenLossRatio > 1 ||
|
|| tokenLossRatio > 1
|
||||||
[null, undefined].includes(tokenLossRatio))
|
|| [null, undefined].includes(tokenLossRatio))
|
||||||
) {
|
) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Token loss ratio must be in the range [0, 1]; got ${tokenLossRatio}`
|
`Token loss ratio must be in the range [0, 1]; got ${tokenLossRatio}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
duration < params.voteDuration.min ||
|
duration < params.voteDuration.min
|
||||||
(params.voteDuration.max && duration > params.voteDuration.max) ||
|
|| (params.voteDuration.max && duration > params.voteDuration.max)
|
||||||
[null, undefined].includes(duration)
|
|| [null, undefined].includes(duration)
|
||||||
) {
|
) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Duration must be in the range [${params.voteDuration.min}, ${
|
`Duration must be in the range [${params.voteDuration.min}, ${
|
||||||
params.voteDuration.max ?? "Inf"
|
params.voteDuration.max ?? 'Inf'
|
||||||
}]; got ${duration}`
|
}]; got ${duration}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.postId = postId;
|
this.postId = postId;
|
||||||
this.state = ValidationPoolStates.OPEN;
|
this.state = ValidationPoolStates.OPEN;
|
||||||
this.setStatus("Open");
|
this.setStatus('Open');
|
||||||
this.votes = new Map();
|
this.votes = new Map();
|
||||||
this.voters = new Map();
|
this.voters = new Map();
|
||||||
this.bench = bench;
|
this.bench = bench;
|
||||||
|
@ -76,7 +79,7 @@ export class ValidationPool extends Actor {
|
||||||
}
|
}
|
||||||
if (this.duration && new Date() - this.dateStart > this.duration) {
|
if (this.duration && new Date() - this.dateStart > this.duration) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Validation pool ${this.id} has expired, no new votes may be cast`
|
`Validation pool ${this.id} has expired, no new votes may be cast`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.votes.set(signingPublicKey, vote);
|
this.votes.set(signingPublicKey, vote);
|
||||||
|
@ -85,18 +88,17 @@ export class ValidationPool extends Actor {
|
||||||
listVotes(position) {
|
listVotes(position) {
|
||||||
return new Map(
|
return new Map(
|
||||||
Array.from(this.votes.entries()).filter(
|
Array.from(this.votes.entries()).filter(
|
||||||
([_, vote]) => vote.position === position
|
([_, vote]) => vote.position === position,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
revealIdentity(signingPublicKey, reputationPublicKey) {
|
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');
|
||||||
}
|
}
|
||||||
const voter =
|
const voter = this.bench.voters.get(reputationPublicKey)
|
||||||
this.bench.voters.get(reputationPublicKey) ??
|
?? new Voter(reputationPublicKey);
|
||||||
new Voter(reputationPublicKey);
|
|
||||||
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);
|
||||||
|
@ -104,14 +106,14 @@ export class ValidationPool extends Actor {
|
||||||
// All voters have revealed their reputation public keys
|
// All voters have revealed their reputation public keys
|
||||||
// Now we can evaluate winning conditions
|
// Now we can evaluate winning conditions
|
||||||
this.state = ValidationPoolStates.CLOSED;
|
this.state = ValidationPoolStates.CLOSED;
|
||||||
this.setStatus("Closed");
|
this.setStatus('Closed');
|
||||||
const result = this.evaluateWinningConditions();
|
const result = this.evaluateWinningConditions();
|
||||||
if (result === null) {
|
if (result === null) {
|
||||||
this.setStatus("Closed - Quorum not met");
|
this.setStatus('Closed - Quorum not met');
|
||||||
this.scene.log(`note over ${this.name} : Quorum not met`);
|
this.scene.log(`note over ${this.name} : Quorum not met`);
|
||||||
} else {
|
} else {
|
||||||
this.setStatus(`Closed - ${result ? "Won" : "Lost"}`);
|
this.setStatus(`Closed - ${result ? 'Won' : 'Lost'}`);
|
||||||
this.scene.log(`note over ${this.name} : ${result ? "Win" : "Lose"}`);
|
this.scene.log(`note over ${this.name} : ${result ? 'Win' : 'Lose'}`);
|
||||||
this.applyTokenLocking();
|
this.applyTokenLocking();
|
||||||
this.distributeTokens(result);
|
this.distributeTokens(result);
|
||||||
}
|
}
|
||||||
|
@ -153,27 +155,23 @@ export class ValidationPool extends Actor {
|
||||||
this.bench.reputations.lockTokens(
|
this.bench.reputations.lockTokens(
|
||||||
voter.reputationPublicKey,
|
voter.reputationPublicKey,
|
||||||
stake,
|
stake,
|
||||||
lockingTime
|
lockingTime,
|
||||||
);
|
);
|
||||||
// TODO: If there is an exception here, the voter may have voted incorrectly. Consider penalties.
|
// TODO: If there is an exception here, the voter may have voted incorrectly. Consider penalties.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
evaluateWinningConditions() {
|
evaluateWinningConditions() {
|
||||||
const getVoteValue = ({ stake, lockingTime }) =>
|
const getVoteValue = ({ stake, lockingTime }) => stake * lockingTime ** params.lockingTimeExponent;
|
||||||
stake * Math.pow(lockingTime, params.lockingTimeExponent);
|
const getTotalValue = (position) => Array.from(this.listVotes(position).values())
|
||||||
const getTotalValue = (position) =>
|
.map(getVoteValue)
|
||||||
Array.from(this.listVotes(position).values())
|
.reduce((acc, cur) => (acc += cur), 0);
|
||||||
.map(getVoteValue)
|
|
||||||
.reduce((acc, cur) => (acc += cur), 0);
|
|
||||||
|
|
||||||
const upvoteValue = getTotalValue(true);
|
const upvoteValue = getTotalValue(true);
|
||||||
const downvoteValue = getTotalValue(false);
|
const downvoteValue = getTotalValue(false);
|
||||||
const activeAvailableReputation =
|
const activeAvailableReputation = this.bench.getTotalActiveAvailableReputation();
|
||||||
this.bench.getTotalActiveAvailableReputation();
|
|
||||||
const votePasses = upvoteValue >= params.winningRatio * downvoteValue;
|
const votePasses = upvoteValue >= params.winningRatio * downvoteValue;
|
||||||
const quorumMet =
|
const quorumMet = upvoteValue + downvoteValue >= params.quorum * activeAvailableReputation;
|
||||||
upvoteValue + downvoteValue >= params.quorum * activeAvailableReputation;
|
|
||||||
|
|
||||||
return quorumMet ? votePasses : null;
|
return quorumMet ? votePasses : null;
|
||||||
}
|
}
|
||||||
|
@ -182,7 +180,6 @@ export class ValidationPool extends Actor {
|
||||||
// Reward the author
|
// Reward the author
|
||||||
// TODO: If the vote fails, distribute tokens.author among winning voters
|
// TODO: If the vote fails, distribute tokens.author among winning voters
|
||||||
if (result === true) {
|
if (result === true) {
|
||||||
// console.log("awarding to author", {id: this.authorId, tokens: this.tokens.for});
|
|
||||||
this.bench.reputations.addTokens(this.authorId, this.tokens.for);
|
this.bench.reputations.addTokens(this.authorId, this.tokens.for);
|
||||||
// Reward the vote winners, in proportion to their stakes
|
// Reward the vote winners, in proportion to their stakes
|
||||||
const tokensForWinners = this.tokens.against;
|
const tokensForWinners = this.tokens.against;
|
||||||
|
@ -196,7 +193,6 @@ export class ValidationPool extends Actor {
|
||||||
for (const [signingPublicKey, { stake }] of winningVotes.entries()) {
|
for (const [signingPublicKey, { stake }] of winningVotes.entries()) {
|
||||||
const { reputationPublicKey } = this.voters.get(signingPublicKey);
|
const { reputationPublicKey } = this.voters.get(signingPublicKey);
|
||||||
const reward = (tokensForWinners * stake) / totalStakes;
|
const reward = (tokensForWinners * stake) / totalStakes;
|
||||||
// console.log("awarding to winning voter", {id: reputationPublicKey, tokens: reward, stake, totalStakes, tokensForWinners});
|
|
||||||
this.bench.reputations.addTokens(reputationPublicKey, reward);
|
this.bench.reputations.addTokens(reputationPublicKey, reward);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { Box } from './classes/box.js';
|
import { Box } from './classes/box.js';
|
||||||
import { Scene } from './classes/scene.js';
|
import { Scene } from './classes/scene.js';
|
||||||
import { debounce, delay } from "./util.js";
|
import { debounce, delay } from './util.js';
|
||||||
|
|
||||||
const rootElement = document.getElementById('debounce-test');
|
const rootElement = document.getElementById('debounce-test');
|
||||||
const rootBox = new Box('rootBox', rootElement).flex();
|
const rootBox = new Box('rootBox', rootElement).flex();
|
||||||
|
|
||||||
const scene = window.scene = new Scene('Debounce test', rootBox);
|
const scene = window.scene = new Scene('Debounce test', rootBox);
|
||||||
|
|
||||||
const log = () => scene.log("event");
|
const log = () => scene.log('event');
|
||||||
debounce(log, 500);
|
debounce(log, 500);
|
||||||
debounce(log, 500);
|
debounce(log, 500);
|
||||||
await delay(500);
|
await delay(500);
|
||||||
|
|
|
@ -11,5 +11,7 @@ export const debounce = (fn, delay) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const delay = async (ms) => {
|
export const delay = async (ms) => {
|
||||||
await new Promise((resolve) => setTimeout(resolve, ms));
|
await new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, ms);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,9 +8,9 @@ const rootElement = document.getElementById('validation-pool');
|
||||||
const rootBox = new Box('rootBox', rootElement).flex();
|
const rootBox = new Box('rootBox', rootElement).flex();
|
||||||
|
|
||||||
const scene = window.scene = new Scene('Validation Pool test', rootBox).log('sequenceDiagram');
|
const scene = window.scene = new Scene('Validation Pool test', rootBox).log('sequenceDiagram');
|
||||||
const member1 = window.member1 = await new Member("Member1", scene).initialize();
|
const member1 = window.member1 = await new Member('Member1', scene).initialize();
|
||||||
const member2 = window.member2 = await new Member("Member2", scene).initialize();
|
const member2 = window.member2 = await new Member('Member2', scene).initialize();
|
||||||
const bench = window.bench = new Bench("Bench", scene);
|
const bench = window.bench = new Bench('Bench', scene);
|
||||||
|
|
||||||
const updateDisplayValues = async () => {
|
const updateDisplayValues = async () => {
|
||||||
member1.setValue('rep', bench.reputations.getTokens(member1.reputationPublicKey));
|
member1.setValue('rep', bench.reputations.getTokens(member1.reputationPublicKey));
|
||||||
|
@ -29,7 +29,7 @@ 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.castVote(pool, true, 0, 0);
|
||||||
await member1.revealIdentity(pool); // Vote passes
|
await member1.revealIdentity(pool); // Vote passes
|
||||||
await updateDisplayValues();
|
await updateDisplayValues();
|
||||||
|
@ -38,23 +38,23 @@ 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); // Quorum not met!
|
||||||
await updateDisplayValues();
|
await updateDisplayValues();
|
||||||
await delay(1000);
|
await delay(1000);
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
if (e.message.match(/Quorum is not met/)) {
|
if (e.message.match(/Quorum is not met/)) {
|
||||||
console.log("Caught expected error: Quorum not met")
|
console.log('Caught expected error: Quorum not met');
|
||||||
} else {
|
} else {
|
||||||
console.error("Unexpected error")
|
console.error('Unexpected error');
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Second member must be approved by first member
|
// Second member must be approved by first member
|
||||||
{
|
{
|
||||||
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); // Vote passes
|
||||||
await updateDisplayValues();
|
await updateDisplayValues();
|
||||||
|
|
Loading…
Reference in New Issue