From 59c10f1ac24acfd5679db8f9efc441d793b5087d Mon Sep 17 00:00:00 2001 From: Ladd Hoffman Date: Sat, 31 Dec 2022 16:08:42 -0600 Subject: [PATCH] eslint fixes --- forum-network/.eslintrc.js | 18 +++- forum-network/notes.md | 5 ++ forum-network/public/basic.js | 16 ++-- forum-network/public/classes/availability.js | 6 ++ forum-network/public/classes/bench.js | 30 ++++--- forum-network/public/classes/box.js | 4 +- forum-network/public/classes/business.js | 10 +++ forum-network/public/classes/crypto.js | 6 +- forum-network/public/classes/forum-node.js | 8 +- forum-network/public/classes/forum-view.js | 8 +- forum-network/public/classes/forum.js | 24 ++++-- forum-network/public/classes/graph.js | 9 +- forum-network/public/classes/member.js | 23 +++--- forum-network/public/classes/message.js | 18 ++-- forum-network/public/classes/post.js | 38 ++++----- forum-network/public/classes/reputation.js | 8 +- forum-network/public/classes/scene.js | 20 ++--- .../public/classes/validation-pool.js | 82 +++++++++---------- forum-network/public/debounce-test.js | 4 +- forum-network/public/util.js | 4 +- forum-network/public/validation-pool-test.js | 20 ++--- 21 files changed, 202 insertions(+), 159 deletions(-) create mode 100644 forum-network/notes.md create mode 100644 forum-network/public/classes/availability.js create mode 100644 forum-network/public/classes/business.js diff --git a/forum-network/.eslintrc.js b/forum-network/.eslintrc.js index fba761d..43e2a93 100644 --- a/forum-network/.eslintrc.js +++ b/forum-network/.eslintrc.js @@ -3,13 +3,27 @@ module.exports = { browser: true, es2021: true, }, - extends: 'airbnb-base', + extends: ['airbnb-base'], overrides: [], parserOptions: { ecmaVersion: 'latest', sourceType: 'module', }, + // plugins: ["import"], 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'], }, }; diff --git a/forum-network/notes.md b/forum-network/notes.md new file mode 100644 index 0000000..3dc9c52 --- /dev/null +++ b/forum-network/notes.md @@ -0,0 +1,5 @@ +# Challenges + +- Receiving payments +- Distributing payments to participants +- Computing updates to forum graph diff --git a/forum-network/public/basic.js b/forum-network/public/basic.js index a4fb47b..48e587d 100644 --- a/forum-network/public/basic.js +++ b/forum-network/public/basic.js @@ -1,5 +1,5 @@ -import { Box } from "./classes/box.js"; -import { Scene } from "./classes/scene.js"; +import { Box } from './classes/box.js'; +import { Scene } from './classes/scene.js'; const rootElement = document.getElementById('basic'); const rootBox = new Box('rootBox', rootElement).flex(); @@ -11,7 +11,9 @@ function randomDelay(min, max) { function delay(min, 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) { @@ -46,7 +48,7 @@ if (true) { }, randomDelay(500, 1500)); }); - blockchain.on(readBlockchainData, (src, detail) => { + blockchain.on(readBlockchainData, (src, _detail) => { blockchainStatus.set('Processing request'); setTimeout(() => { blockchain.send(src, blockchainData, {}); @@ -64,7 +66,7 @@ if (true) { }, randomDelay(6000, 12000)); } -(async function () { +(async function run() { const scene = new Scene('Scene 2', rootBox); const webClient = scene.addActor('webClient'); @@ -162,12 +164,12 @@ if (true) { }); node.on(memoryData, (_src, _data) => { - node.on(storageData, (_src, _data) => { + node.on(storageData, (__src, __data) => { if (detail?.readConcern === 'single') { node.send(node, considerInfo, {}); } else { const peer = getPeer(node); - node.on(qualifiedOpinions, (_src, info) => { + node.on(qualifiedOpinions, (___src, info) => { node.send(node, considerInfo, info); }); node.send(peer, seekTruth, { readConcern: 'single' }); diff --git a/forum-network/public/classes/availability.js b/forum-network/public/classes/availability.js new file mode 100644 index 0000000..554e20e --- /dev/null +++ b/forum-network/public/classes/availability.js @@ -0,0 +1,6 @@ +import { Actor } from './actor.js'; + +/** + * Purpose: Enable staking reputation to enter the pool of workers + */ +export class Availability extends Actor {} diff --git a/forum-network/public/classes/bench.js b/forum-network/public/classes/bench.js index 07e849f..5595c02 100644 --- a/forum-network/public/classes/bench.js +++ b/forum-network/public/classes/bench.js @@ -1,9 +1,12 @@ -import { Actor } from "./actor.js"; -import { Reputations } from "./reputation.js"; -import { ValidationPool } from "./validation-pool.js"; -import params from "./params.js"; -import { Action } from "./action.js"; +import { Actor } from './actor.js'; +import { Reputations } from './reputation.js'; +import { ValidationPool } from './validation-pool.js'; +import params from './params.js'; +import { Action } from './action.js'; +/** + * Purpose: Keep track of reputation holders + */ export class Bench extends Actor { constructor(name, scene) { super(name, scene); @@ -12,7 +15,7 @@ export class Bench extends Actor { this.reputations = new Reputations(); this.actions = { - createValidationPool: new Action("create validation pool", scene), + createValidationPool: new Action('create validation pool', scene), }; this.activate(); @@ -27,8 +30,7 @@ export class Bench extends Actor { const thresholdSet = !!params.activeVoterThreshold; return Array.from(this.voters.values()).filter((voter) => { const hasVoted = !!voter.dateLastVote; - const withinThreshold = - now - voter.dateLastVote >= params.activeVoterThreshold; + const withinThreshold = now - voter.dateLastVote >= params.activeVoterThreshold; return hasVoted && (!thresholdSet || withinThreshold); }); } @@ -43,17 +45,13 @@ export class Bench extends Actor { getTotalActiveReputation() { return this.listActiveVoters() - .map(({ reputationPublicKey }) => - this.reputations.getTokens(reputationPublicKey) - ) + .map(({ reputationPublicKey }) => this.reputations.getTokens(reputationPublicKey)) .reduce((acc, cur) => (acc += cur), 0); } getTotalActiveAvailableReputation() { return this.listActiveVoters() - .map(({ reputationPublicKey }) => - this.reputations.getAvailableTokens(reputationPublicKey) - ) + .map(({ reputationPublicKey }) => this.reputations.getAvailableTokens(reputationPublicKey)) .reduce((acc, cur) => (acc += cur), 0); } @@ -66,7 +64,7 @@ export class Bench extends Actor { tokenLossRatio, contentiousDebate, signingPublicKey, - } + }, ) { const validationPoolNumber = this.validationPools.size + 1; const validationPool = new ValidationPool( @@ -81,7 +79,7 @@ export class Bench extends Actor { signingPublicKey, }, `pool${validationPoolNumber}`, - scene + this.scene, ); this.validationPools.set(validationPool.id, validationPool); this.actions.createValidationPool.log(this, validationPool); diff --git a/forum-network/public/classes/box.js b/forum-network/public/classes/box.js index 7961f58..a03dcd1 100644 --- a/forum-network/public/classes/box.js +++ b/forum-network/public/classes/box.js @@ -1,4 +1,4 @@ -import {DisplayValue} from "./display-value.js"; +import { DisplayValue } from './display-value.js'; export class Box { constructor(name, parentEl, elementType = 'div') { @@ -47,7 +47,7 @@ export class Box { } setId(id) { - this.el.id = (id || this.name).replace(/ /g, ""); + this.el.id = (id || this.name).replace(/ /g, ''); return this; } diff --git a/forum-network/public/classes/business.js b/forum-network/public/classes/business.js new file mode 100644 index 0000000..17c5236 --- /dev/null +++ b/forum-network/public/classes/business.js @@ -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) {} +} diff --git a/forum-network/public/classes/crypto.js b/forum-network/public/classes/crypto.js index 4d37394..c7c9e7f 100644 --- a/forum-network/public/classes/crypto.js +++ b/forum-network/public/classes/crypto.js @@ -4,7 +4,7 @@ export class CryptoUtil { static hash = 'SHA-256'; static async generateAsymmetricKey() { - return await window.crypto.subtle.generateKey( + return window.crypto.subtle.generateKey( { name: CryptoUtil.algorithm, hash: CryptoUtil.hash, @@ -32,7 +32,7 @@ export class CryptoUtil { // TODO: make a single TextEncoder instance and reuse it const encoder = new TextEncoder(); 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) { @@ -44,7 +44,7 @@ export class CryptoUtil { static async importKey(b64jwk) { // Convert base64 javascript web key to CryptoKey const jwk = JSON.parse(atob(b64jwk)); - return await window.crypto.subtle.importKey( + return window.crypto.subtle.importKey( 'jwk', jwk, { diff --git a/forum-network/public/classes/forum-node.js b/forum-network/public/classes/forum-node.js index a4a82d4..6ac85a2 100644 --- a/forum-network/public/classes/forum-node.js +++ b/forum-network/public/classes/forum-node.js @@ -1,6 +1,8 @@ import { Actor } from './actor.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 { ForumView } from './forum-view.js'; import { PrioritizedQueue } from './prioritized-queue.js'; @@ -51,7 +53,7 @@ export class ForumNode extends Actor { if (!messageJson) { return null; } - await this.processMessage(messageJson); + return this.processMessage(messageJson); } // Process a message from the queue @@ -65,7 +67,7 @@ export class ForumNode extends Actor { } const { publicKey } = messageJson; - const message = Message.fromJSON(messageJson); + const message = messageFromJSON(messageJson); console.log(`${this.name}: processMessage`, message); if (message instanceof PostMessage) { diff --git a/forum-network/public/classes/forum-view.js b/forum-network/public/classes/forum-view.js index 2e63cca..ff4256b 100644 --- a/forum-network/public/classes/forum-view.js +++ b/forum-network/public/classes/forum-view.js @@ -34,7 +34,7 @@ export class ForumView { this.reputations.set(id, reputation); } - incrementReputation(publicKey, increment, reason) { + incrementReputation(publicKey, increment, _reason) { const reputation = this.getReputation(publicKey) || 0; return this.reputations.set(publicKey, reputation + increment); } @@ -88,7 +88,8 @@ export class ForumView { // 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. - // 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; @@ -104,7 +105,8 @@ export class ForumView { // Some of the incoming reputation gets distributed among cited posts 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. const totalWeight = post.citations ?.map(({ weight }) => weight) diff --git a/forum-network/public/classes/forum.js b/forum-network/public/classes/forum.js index f9d78ea..955e038 100644 --- a/forum-network/public/classes/forum.js +++ b/forum-network/public/classes/forum.js @@ -1,9 +1,10 @@ -import { Actor } from "./actor.js"; -import { Graph, Vertex } from "./graph.js"; -import params from "./params.js"; +import { Actor } from './actor.js'; +import { Graph, Vertex } from './graph.js'; +import params from './params.js'; class Post extends Vertex { constructor(forum, id, authorId, citations) { + super(); this.forum = forum; this.id = id; 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 { constructor(bench, name, scene) { super(name, scene); @@ -24,11 +28,13 @@ export class Forum extends Actor { this.posts = new Graph(); } - async addPost({ authorId, postId, citations = [], poolParams }) { + async addPost({ + authorId, postId, citations = [], poolParams, + }) { const post = new Post(this, postId, authorId); this.posts.addVertex(postId, post); for (const { postId: citedPostId, weight } of citations) { - this.posts.addEdge("citation", postId, citedPostId, { weight }); + this.posts.addEdge('citation', postId, citedPostId, { weight }); } // this.applyReputationEffects(post); // initiateValidationPool(authorId, {postId, fee, duration, tokenLossRatio, contentiousDebate, signingPublicKey}) { @@ -48,7 +54,7 @@ export class Forum extends Actor { } 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 post.value += amount; // Distribute a fraction of the added value among cited posts @@ -63,17 +69,17 @@ export class Forum extends Actor { for (const { to: citedPostId, data: { weight }, - } of post.getEdges("citation", true)) { + } of post.getEdges('citation', true)) { const citedPost = this.getPost(citedPostId); if (!citedPost) { throw new Error( - `Post ${post.postId} cites unknown post ${citedPostId}` + `Post ${post.postId} cites unknown post ${citedPostId}`, ); } this.distributeReputation( citedPost, (weight / totalWeight) * distributeAmongCitations, - depth + 1 + depth + 1, ); } } diff --git a/forum-network/public/classes/graph.js b/forum-network/public/classes/graph.js index fcfe3ef..a53d67a 100644 --- a/forum-network/public/classes/graph.js +++ b/forum-network/public/classes/graph.js @@ -8,8 +8,8 @@ export class Vertex { } getEdges(label, away) { - return this.edges[away ? "from" : "to"].filter( - (edge) => edge.label === label + return this.edges[away ? 'from' : 'to'].filter( + (edge) => edge.label === label, ); } } @@ -34,7 +34,7 @@ export class Graph { addVertex(id, data) { // Support simple case of auto-incremented numeric ids - if (typeof id === "object") { + if (typeof id === 'object') { data = id; id = this.nextVertexId++; } @@ -85,8 +85,7 @@ export class Graph { return edgeLabels.flatMap((edgeLabel) => { const edges = this.edgeLabels.get(edgeLabel); return Array.from(edges?.values() || []).filter((edge) => { - const matchFrom = - from === null || from === undefined || from === edge.from; + const matchFrom = from === null || from === undefined || from === edge.from; const matchTo = to === null || to === undefined || to === edge.to; return matchFrom && matchTo; }); diff --git a/forum-network/public/classes/member.js b/forum-network/public/classes/member.js index 8d6b81a..32d2da7 100644 --- a/forum-network/public/classes/member.js +++ b/forum-network/public/classes/member.js @@ -1,16 +1,17 @@ -import { Actor } from "./actor.js"; -import { Action } from "./action.js"; -import { PostMessage } from "./message.js"; -import { CryptoUtil } from "./crypto.js"; +import { Actor } from './actor.js'; + +import { Action } from './action.js'; +import { PostMessage } from './message.js'; +import { CryptoUtil } from './crypto.js'; export class Member extends Actor { constructor(name, scene) { super(name, scene); this.actions = { - submitPost: new Action("submit post", scene), - initiateValidationPool: new Action("initiate validation pool", scene), - castVote: new Action("cast vote", scene), - revealIdentity: new Action("reveal identity", scene), + submitPost: new Action('submit post', scene), + initiateValidationPool: new Action('initiate validation pool', scene), + castVote: new Action('cast vote', scene), + revealIdentity: new Action('reveal identity', scene), }; this.validationPools = new Map(); } @@ -19,7 +20,7 @@ export class Member extends Actor { this.reputationKey = await CryptoUtil.generateAsymmetricKey(); // this.reputationPublicKey = await CryptoUtil.exportKey(this.reputationKey.publicKey); this.reputationPublicKey = this.name; - this.status.set("Initialized"); + this.status.set('Initialized'); this.activate(); return this; } @@ -40,7 +41,7 @@ export class Member extends Actor { this.actions.initiateValidationPool.log( this, bench, - `(fee: ${options.fee})` + `(fee: ${options.fee})`, ); const pool = bench.initiateValidationPool(this.reputationPublicKey, { ...options, @@ -59,7 +60,7 @@ export class Member extends Actor { this.actions.castVote.log( this, validationPool, - `(${position ? "for" : "against"}, stake: ${stake})` + `(${position ? 'for' : 'against'}, stake: ${stake})`, ); validationPool.castVote(signingPublicKey, position, stake, lockingTime); } diff --git a/forum-network/public/classes/message.js b/forum-network/public/classes/message.js index d505f9f..7e4c033 100644 --- a/forum-network/public/classes/message.js +++ b/forum-network/public/classes/message.js @@ -14,23 +14,17 @@ export class Message { } static async verify({ content, publicKey, signature }) { - return await CryptoUtil.verify(content, publicKey, signature); + return CryptoUtil.verify(content, publicKey, signature); } static contentFromJSON(data) { return data; } - contentToJSON(content) { + static contentToJSON(content) { return content; } - static fromJSON({ type, content }) { - const messageType = messageTypes.get(type) || Message; - const messageContent = messageType.contentFromJSON(content); - return new messageType(messageContent); - } - toJSON() { return { type: this.type, @@ -51,7 +45,7 @@ export class PostMessage extends Message { }; } - contentToJSON({ post, stake }) { + static contentToJSON({ post, stake }) { return { post: post.toJSON(), stake, @@ -67,3 +61,9 @@ const messageTypes = new Map([ ['post', PostMessage], ['peer', PeerMessage], ]); + +export const messageFromJSON = ({ type, content }) => { + const MessageType = messageTypes.get(type) || Message; + const messageContent = MessageType.contentFromJSON(content); + return new MessageType(messageContent); +}; diff --git a/forum-network/public/classes/post.js b/forum-network/public/classes/post.js index cd7d230..515a691 100644 --- a/forum-network/public/classes/post.js +++ b/forum-network/public/classes/post.js @@ -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 { constructor(content) { @@ -28,21 +46,3 @@ export class 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); - } -} diff --git a/forum-network/public/classes/reputation.js b/forum-network/public/classes/reputation.js index 00bab5b..7718825 100644 --- a/forum-network/public/classes/reputation.js +++ b/forum-network/public/classes/reputation.js @@ -14,14 +14,14 @@ class Reputation { addTokens(tokens) { 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; } lockTokens(tokens, duration) { 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); this.locks.add(lock); @@ -35,10 +35,10 @@ class Reputation { getAvailableTokens() { const now = new Date(); 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); if (tokensLocked > this.tokens) { - throw new Error("Assertion failure. tokensLocked > tokens"); + throw new Error('Assertion failure. tokensLocked > tokens'); } return this.tokens - tokensLocked; } diff --git a/forum-network/public/classes/scene.js b/forum-network/public/classes/scene.js index 63e5061..c4be663 100644 --- a/forum-network/public/classes/scene.js +++ b/forum-network/public/classes/scene.js @@ -1,25 +1,25 @@ -import { Actor } from "./actor.js"; -import { Action } from "./action.js"; -import { debounce } from "../util.js"; -import mermaid from "https://unpkg.com/mermaid@9.2.2/dist/mermaid.esm.min.mjs"; +import mermaid from 'https://unpkg.com/mermaid@9.2.2/dist/mermaid.esm.min.mjs'; +import { Actor } from './actor.js'; +import { Action } from './action.js'; +import { debounce } from '../util.js'; export class Scene { constructor(name, rootBox) { this.name = name; this.box = rootBox.addBox(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.box.addBox("Spacer").setInnerHTML(" "); + this.box.addBox('Spacer').setInnerHTML(' '); this.actors = new Set(); this.seqDiagramContainer = this.box.addBox( - `${this.name}-seq-diagram-container` + `${this.name}-seq-diagram-container`, ); this.seqDiagramElement = this.box .addBox(`${this.name}-seq-diagram-element`) .setId(); 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`); mermaid.mermaidAPI.initialize({ startOnLoad: false }); this.dateLastRender = null; @@ -63,7 +63,7 @@ export class Scene { const dateStart = new Date(); const graph = await mermaid.mermaidAPI.render( this.seqDiagramElement.getId(), - this.logBox.getInnerText() + this.logBox.getInnerText(), ); this.seqDiagramBox.setInnerHTML(graph); if (!this.dateLastRender) { @@ -72,7 +72,7 @@ export class Scene { console.log( `renderSequenceDiagram time: ${ new Date() - dateStart - } ms, time since last render: ${dateStart - this.dateLastRender}` + } ms, time since last render: ${dateStart - this.dateLastRender}`, ); this.dateLastRender = dateStart; }; diff --git a/forum-network/public/classes/validation-pool.js b/forum-network/public/classes/validation-pool.js index 4f61552..96b44d8 100644 --- a/forum-network/public/classes/validation-pool.js +++ b/forum-network/public/classes/validation-pool.js @@ -1,14 +1,17 @@ -import { CryptoUtil } from "./crypto.js"; -import { Vote } from "./vote.js"; -import { Voter } from "./voter.js"; -import { Actor } from "./actor.js"; -import params from "./params.js"; +import { CryptoUtil } from './crypto.js'; +import { Vote } from './vote.js'; +import { Voter } from './voter.js'; +import { Actor } from './actor.js'; +import params from './params.js'; const ValidationPoolStates = Object.freeze({ - OPEN: "OPEN", - CLOSED: "CLOSED", + OPEN: 'OPEN', + CLOSED: 'CLOSED', }); +/** + * Purpose: Enable voting + */ export class ValidationPool extends Actor { constructor( bench, @@ -22,34 +25,34 @@ export class ValidationPool extends Actor { contentiousDebate = false, }, name, - scene + scene, ) { super(name, scene); // If contentiousDebate = true, we will follow the progression defined by getTokenLossRatio() if ( - !contentiousDebate && - (tokenLossRatio < 0 || - tokenLossRatio > 1 || - [null, undefined].includes(tokenLossRatio)) + !contentiousDebate + && (tokenLossRatio < 0 + || tokenLossRatio > 1 + || [null, undefined].includes(tokenLossRatio)) ) { 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 ( - duration < params.voteDuration.min || - (params.voteDuration.max && duration > params.voteDuration.max) || - [null, undefined].includes(duration) + 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}` + params.voteDuration.max ?? 'Inf' + }]; got ${duration}`, ); } this.postId = postId; this.state = ValidationPoolStates.OPEN; - this.setStatus("Open"); + this.setStatus('Open'); this.votes = new Map(); this.voters = new Map(); this.bench = bench; @@ -76,7 +79,7 @@ export class ValidationPool extends Actor { } if (this.duration && new Date() - this.dateStart > this.duration) { 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); @@ -85,18 +88,17 @@ export class ValidationPool extends Actor { listVotes(position) { return new Map( Array.from(this.votes.entries()).filter( - ([_, vote]) => vote.position === position - ) + ([_, vote]) => vote.position === position, + ), ); } revealIdentity(signingPublicKey, reputationPublicKey) { if (!this.votes.get(signingPublicKey)) { - throw new Error("Must vote before revealing identity"); + throw new Error('Must vote before revealing identity'); } - const voter = - this.bench.voters.get(reputationPublicKey) ?? - new Voter(reputationPublicKey); + const voter = this.bench.voters.get(reputationPublicKey) + ?? new Voter(reputationPublicKey); voter.addVoteRecord(this); this.bench.voters.set(reputationPublicKey, voter); this.voters.set(signingPublicKey, voter); @@ -104,14 +106,14 @@ export class ValidationPool extends Actor { // All voters have revealed their reputation public keys // Now we can evaluate winning conditions this.state = ValidationPoolStates.CLOSED; - this.setStatus("Closed"); + this.setStatus('Closed'); const result = this.evaluateWinningConditions(); 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`); } else { - this.setStatus(`Closed - ${result ? "Won" : "Lost"}`); - this.scene.log(`note over ${this.name} : ${result ? "Win" : "Lose"}`); + this.setStatus(`Closed - ${result ? 'Won' : 'Lost'}`); + this.scene.log(`note over ${this.name} : ${result ? 'Win' : 'Lose'}`); this.applyTokenLocking(); this.distributeTokens(result); } @@ -153,27 +155,23 @@ export class ValidationPool extends Actor { this.bench.reputations.lockTokens( voter.reputationPublicKey, stake, - lockingTime + lockingTime, ); // TODO: If there is an exception here, the voter may have voted incorrectly. Consider penalties. } } evaluateWinningConditions() { - const getVoteValue = ({ stake, lockingTime }) => - stake * Math.pow(lockingTime, params.lockingTimeExponent); - const getTotalValue = (position) => - Array.from(this.listVotes(position).values()) - .map(getVoteValue) - .reduce((acc, cur) => (acc += cur), 0); + const getVoteValue = ({ stake, lockingTime }) => stake * lockingTime ** params.lockingTimeExponent; + const getTotalValue = (position) => Array.from(this.listVotes(position).values()) + .map(getVoteValue) + .reduce((acc, cur) => (acc += cur), 0); const upvoteValue = getTotalValue(true); const downvoteValue = getTotalValue(false); - const activeAvailableReputation = - this.bench.getTotalActiveAvailableReputation(); + const activeAvailableReputation = this.bench.getTotalActiveAvailableReputation(); const votePasses = upvoteValue >= params.winningRatio * downvoteValue; - const quorumMet = - upvoteValue + downvoteValue >= params.quorum * activeAvailableReputation; + const quorumMet = upvoteValue + downvoteValue >= params.quorum * activeAvailableReputation; return quorumMet ? votePasses : null; } @@ -182,7 +180,6 @@ export class ValidationPool extends Actor { // Reward the author // TODO: If the vote fails, distribute tokens.author among winning voters if (result === true) { - // console.log("awarding to author", {id: this.authorId, tokens: this.tokens.for}); this.bench.reputations.addTokens(this.authorId, this.tokens.for); // Reward the vote winners, in proportion to their stakes const tokensForWinners = this.tokens.against; @@ -196,7 +193,6 @@ export class ValidationPool extends Actor { for (const [signingPublicKey, { stake }] of winningVotes.entries()) { const { reputationPublicKey } = this.voters.get(signingPublicKey); const reward = (tokensForWinners * stake) / totalStakes; - // console.log("awarding to winning voter", {id: reputationPublicKey, tokens: reward, stake, totalStakes, tokensForWinners}); this.bench.reputations.addTokens(reputationPublicKey, reward); } } diff --git a/forum-network/public/debounce-test.js b/forum-network/public/debounce-test.js index 7258851..278becf 100644 --- a/forum-network/public/debounce-test.js +++ b/forum-network/public/debounce-test.js @@ -1,13 +1,13 @@ import { Box } from './classes/box.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 rootBox = new Box('rootBox', rootElement).flex(); 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); await delay(500); diff --git a/forum-network/public/util.js b/forum-network/public/util.js index 4607eab..a44dd7c 100644 --- a/forum-network/public/util.js +++ b/forum-network/public/util.js @@ -11,5 +11,7 @@ export const debounce = (fn, delay) => { }; export const delay = async (ms) => { - await new Promise((resolve) => setTimeout(resolve, ms)); + await new Promise((resolve) => { + setTimeout(resolve, ms); + }); }; diff --git a/forum-network/public/validation-pool-test.js b/forum-network/public/validation-pool-test.js index a3b878e..cfba35b 100644 --- a/forum-network/public/validation-pool-test.js +++ b/forum-network/public/validation-pool-test.js @@ -8,9 +8,9 @@ const rootElement = document.getElementById('validation-pool'); const rootBox = new Box('rootBox', rootElement).flex(); const scene = window.scene = new Scene('Validation Pool 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 bench = window.bench = new Bench("Bench", scene); +const member1 = window.member1 = await new Member('Member1', scene).initialize(); +const member2 = window.member2 = await new Member('Member2', scene).initialize(); +const bench = window.bench = new Bench('Bench', scene); const updateDisplayValues = async () => { member1.setValue('rep', bench.reputations.getTokens(member1.reputationPublicKey)); @@ -29,7 +29,7 @@ await delay(1000); // 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); // Vote passes await updateDisplayValues(); @@ -38,23 +38,23 @@ await delay(1000); // Failure example: second member can not self-approve 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 updateDisplayValues(); await delay(1000); -} catch(e) { +} catch (e) { 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 { - console.error("Unexpected error") + console.error('Unexpected error'); throw e; } } // Second member must be approved by first member { - const pool = await member2.initiateValidationPool(bench, {fee: 1, duration: 1000, tokenLossRatio: 1}); - await member1.castVote(pool, {position: true, stake: 4, lockingTime: 0}); + const pool = await member2.initiateValidationPool(bench, { fee: 1, duration: 1000, tokenLossRatio: 1 }); + await member1.castVote(pool, { position: true, stake: 4, lockingTime: 0 }); await member1.revealIdentity(pool); await member2.revealIdentity(pool); // Vote passes await updateDisplayValues();