eslint fixes
This commit is contained in:
parent
d63a93562f
commit
59c10f1ac2
|
@ -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'],
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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 { 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' });
|
||||
|
|
|
@ -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 { 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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 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,
|
||||
{
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue