eslint fixes

This commit is contained in:
Ladd Hoffman 2022-12-31 16:08:42 -06:00
parent d63a93562f
commit 59c10f1ac2
21 changed files with 202 additions and 159 deletions

View File

@ -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'],
},
};

5
forum-network/notes.md Normal file
View File

@ -0,0 +1,5 @@
# Challenges
- Receiving payments
- Distributing payments to participants
- Computing updates to forum graph

View File

@ -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' });

View File

@ -0,0 +1,6 @@
import { Actor } from './actor.js';
/**
* Purpose: Enable staking reputation to enter the pool of workers
*/
export class Availability extends Actor {}

View File

@ -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);

View File

@ -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;
}

View File

@ -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) {}
}

View File

@ -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,
{

View File

@ -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) {

View File

@ -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)

View File

@ -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,
);
}
}

View File

@ -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;
});

View File

@ -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);
}

View File

@ -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);
};

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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("&nbsp;");
this.box.addBox('Spacer').setInnerHTML('&nbsp;');
this.displayValuesBox = this.box.addBox(`${this.name}-values`);
this.box.addBox("Spacer").setInnerHTML("&nbsp;");
this.box.addBox('Spacer').setInnerHTML('&nbsp;');
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("&nbsp;");
this.box.addBox('Spacer').setInnerHTML('&nbsp;');
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;
};

View File

@ -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())
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);
}
}

View File

@ -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);

View File

@ -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);
});
};

View File

@ -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();