diff --git a/forum-network/src/classes/action.js b/forum-network/src/classes/action.js index 7f559bc..4eb9a61 100644 --- a/forum-network/src/classes/action.js +++ b/forum-network/src/classes/action.js @@ -4,9 +4,9 @@ export class Action { this.scene = scene; } - log(src, dest, msg, obj, symbol = '->>') { + async log(src, dest, msg, obj, symbol = '->>') { const logObj = false; - this.scene.log( + await this.scene.sequence.log( `${src.name} ${symbol} ${dest.name} : ${this.name} ${msg ?? ''} ${ logObj && obj ? JSON.stringify(obj) : '' }`, diff --git a/forum-network/src/classes/actor.js b/forum-network/src/classes/actor.js index 2aa1496..139213b 100644 --- a/forum-network/src/classes/actor.js +++ b/forum-network/src/classes/actor.js @@ -5,7 +5,6 @@ export class Actor { this.callbacks = new Map(); this.status = this.scene.addDisplayValue(`${this.name} status`); this.status.set('New'); - this.scene.log(`participant ${this.name}`); this.values = new Map(); this.active = 0; this.scene.registerActor(this); @@ -13,31 +12,31 @@ export class Actor { activate() { this.active += 1; - this.scene.log(`activate ${this.name}`); + this.scene.sequence.log(`activate ${this.name}`, false); } - deactivate() { + async deactivate() { if (!this.active) { throw new Error(`${this.name} is not active, can not deactivate`); } this.active -= 1; - this.scene.log(`deactivate ${this.name}`); + await this.scene.sequence.log(`deactivate ${this.name}`); } - send(dest, action, detail) { - action.log(this, dest, detail ? JSON.stringify(detail) : ''); - dest.recv(this, action, detail); + async send(dest, action, detail) { + await action.log(this, dest, detail ? JSON.stringify(detail) : ''); + await dest.recv(this, action, detail); return this; } - recv(src, action, detail) { + async recv(src, action, detail) { const cb = this.callbacks.get(action.name); if (!cb) { throw new Error( `[${this.scene.name} actor ${this.name} does not have a callback registered for ${action.name}`, ); } - cb(src, detail); + await cb(src, detail); return this; } @@ -56,7 +55,7 @@ export class Actor { return this; } - setValue(label, value) { + async setValue(label, value) { if (typeof value === 'number') { value = value.toFixed(2); } @@ -66,7 +65,7 @@ export class Actor { this.values.set(label, displayValue); } if (value !== displayValue.get()) { - this.scene.log(`note over ${this.name} : ${label} = ${value}`); + await this.scene.sequence.log(`note over ${this.name} : ${label} = ${value}`); } displayValue.set(value); return this; diff --git a/forum-network/src/classes/bench.js b/forum-network/src/classes/bench.js index 59fa8ad..679fc10 100644 --- a/forum-network/src/classes/bench.js +++ b/forum-network/src/classes/bench.js @@ -18,8 +18,6 @@ export class Bench extends Actor { this.actions = { createValidationPool: new Action('create validation pool', scene), }; - - this.activate(); } listValidationPools() { @@ -56,7 +54,7 @@ export class Bench extends Actor { .reduce((acc, cur) => (acc += cur), 0); } - initiateValidationPool({ + async initiateValidationPool({ postId, fee, duration, @@ -84,7 +82,7 @@ export class Bench extends Actor { this.scene, ); this.validationPools.set(validationPool.id, validationPool); - this.actions.createValidationPool.log(this, validationPool); + await this.actions.createValidationPool.log(this, validationPool); validationPool.activate(); return validationPool; } diff --git a/forum-network/src/classes/box.js b/forum-network/src/classes/box.js index a03dcd1..cb3af73 100644 --- a/forum-network/src/classes/box.js +++ b/forum-network/src/classes/box.js @@ -1,11 +1,15 @@ import { DisplayValue } from './display-value.js'; +import { CryptoUtil } from './crypto.js'; export class Box { constructor(name, parentEl, elementType = 'div') { this.name = name; this.el = document.createElement(elementType); + this.el.id = `box_${CryptoUtil.randomUUID().replaceAll('-', '').slice(0, 8)}`; this.el.classList.add('box'); - this.el.setAttribute('box-name', name); + if (name) { + this.el.setAttribute('box-name', name); + } if (parentEl) { parentEl.appendChild(this.el); } @@ -27,8 +31,7 @@ export class Box { } addBox(name, elementType) { - const box = new Box(name, null, elementType); - this.el.appendChild(box.el); + const box = new Box(name, this.el, elementType); return box; } @@ -46,11 +49,6 @@ export class Box { return this.el.innerText; } - setId(id) { - this.el.id = (id || this.name).replace(/ /g, ''); - return this; - } - getId() { return this.el.id; } diff --git a/forum-network/src/classes/business.js b/forum-network/src/classes/business.js index 6dbb104..37753af 100644 --- a/forum-network/src/classes/business.js +++ b/forum-network/src/classes/business.js @@ -33,7 +33,7 @@ export class Business extends Actor { async submitRequest(fee, content) { const request = new Request(fee, content); this.requests.set(request.id, request); - this.actions.assignWork.log(this, this.availability); + await this.actions.assignWork.log(this, this.availability); this.worker = await this.availability.assignWork(request.id); return request.id; } @@ -54,13 +54,13 @@ export class Business extends Actor { requestId, workEvidence, }); - this.actions.submitPost.log(this, this.forum); + await this.actions.submitPost.log(this, this.forum); const postId = await this.forum.addPost(reputationPublicKey, post); // Initiate a validation pool for this work evidence. // Validation pool supports secret ballots but we aren't using that here, since we want // the post to be attributable to the reputation holder. - this.actions.initiateValidationPool.log(this, this.bench); + await this.actions.initiateValidationPool.log(this, this.bench); const pool = await this.bench.initiateValidationPool({ postId, fee: request.fee, diff --git a/forum-network/src/classes/expert.js b/forum-network/src/classes/expert.js index 312585e..39b8483 100644 --- a/forum-network/src/classes/expert.js +++ b/forum-network/src/classes/expert.js @@ -1,5 +1,4 @@ import { Actor } from './actor.js'; - import { Action } from './action.js'; import { PostMessage } from './message.js'; import { CryptoUtil } from './crypto.js'; @@ -25,7 +24,6 @@ export class Expert extends Actor { // this.reputationPublicKey = await CryptoUtil.exportKey(this.reputationKey.publicKey); this.reputationPublicKey = this.name; this.status.set('Initialized'); - this.activate(); return this; } @@ -33,19 +31,19 @@ export class Expert extends Actor { // TODO: Include fee const postMessage = new PostMessage({ post, stake }); await postMessage.sign(this.reputationKey); - this.actions.submitPostViaNetwork.log(this, forumNode, null, { id: post.id }); + await this.actions.submitPostViaNetwork.log(this, forumNode, null, { id: post.id }); // For now, directly call forumNode.receiveMessage(); await forumNode.receiveMessage(JSON.stringify(postMessage.toJSON())); } async submitPost(forum, postContent) { // TODO: Include fee - this.actions.submitPost.log(this, forum); + await this.actions.submitPost.log(this, forum); return forum.addPost(this.reputationPublicKey, postContent); } async submitPostWithFee(bench, forum, postContent, poolOptions) { - this.actions.submitPost.log(this, forum); + await this.actions.submitPost.log(this, forum); const postId = await forum.addPost(this.reputationPublicKey, postContent); const pool = await this.initiateValidationPool(bench, { ...poolOptions, postId, anonymous: false }); return { postId, pool }; @@ -59,12 +57,12 @@ export class Expert extends Actor { } else { poolOptions.signingPublicKey = this.reputationPublicKey; } - this.actions.initiateValidationPool.log( + await this.actions.initiateValidationPool.log( this, bench, `(fee: ${poolOptions.fee})`, ); - const pool = bench.initiateValidationPool(poolOptions); + const pool = await bench.initiateValidationPool(poolOptions); this.validationPools.set(pool.id, poolOptions); return pool; } @@ -82,7 +80,7 @@ export class Expert extends Actor { } // TODO: encrypt vote // TODO: sign message - this.actions.castVote.log( + await this.actions.castVote.log( this, validationPool, `(${position ? 'for' : 'against'}, stake: ${stake}, anonymous: ${anonymous})`, @@ -95,12 +93,12 @@ export class Expert extends Actor { async revealIdentity(validationPool) { const { signingPublicKey } = this.validationPools.get(validationPool.id); // TODO: sign message - this.actions.revealIdentity.log(this, validationPool); + await this.actions.revealIdentity.log(this, validationPool); validationPool.revealIdentity(signingPublicKey, this.reputationPublicKey); } async registerAvailability(availability, stake) { - this.actions.registerAvailability.log(this, availability, `(stake: ${stake})`); + await this.actions.registerAvailability.log(this, availability, `(stake: ${stake})`); await availability.register(this.reputationPublicKey, stake); } @@ -111,7 +109,7 @@ export class Expert extends Actor { } async submitWork(business, requestId, evidence, { tokenLossRatio, duration }) { - this.actions.submitWork.log(this, business); + await this.actions.submitWork.log(this, business); return business.submitWork(this.reputationPublicKey, requestId, evidence, { tokenLossRatio, duration }); } } diff --git a/forum-network/src/classes/forum-node.js b/forum-network/src/classes/forum-node.js index e09b6fa..1528cd3 100644 --- a/forum-network/src/classes/forum-node.js +++ b/forum-network/src/classes/forum-node.js @@ -34,7 +34,7 @@ export class ForumNode extends Actor { .filter((forumNode) => forumNode.keyPair.publicKey !== this.keyPair.publicKey); for (const forumNode of otherForumNodes) { // For now just call receiveMessage on the target node - this.actions.peerMessage.log(this, forumNode, null, message.content); + await this.actions.peerMessage.log(this, forumNode, null, message.content); await forumNode.receiveMessage(JSON.stringify(message.toJSON())); } } @@ -61,7 +61,7 @@ export class ForumNode extends Actor { try { await Message.verify(messageJson); } catch (e) { - this.actions.processMessage.log(this, this, 'invalid signature', messageJson, '-x'); + await this.actions.processMessage.log(this, this, 'invalid signature', messageJson, '-x'); console.log(`${this.name}: received message with invalid signature`); return; } @@ -87,7 +87,7 @@ export class ForumNode extends Actor { if (!post.id) { post.id = CryptoUtil.randomUUID(); } - this.actions.storePost.log(this, this, null, { authorId, post, stake }); + await this.actions.storePost.log(this, this, null, { authorId, post, stake }); this.forumView.addPost(authorId, post.id, post, stake); } diff --git a/forum-network/src/classes/forum.js b/forum-network/src/classes/forum.js index 74cc183..36a2b31 100644 --- a/forum-network/src/classes/forum.js +++ b/forum-network/src/classes/forum.js @@ -13,6 +13,7 @@ class Post extends Actor { this.authorPublicKey = authorPublicKey; this.value = 0; this.citations = postContent.citations; + this.title = postContent.title; this.totalCitationWeight = this.citations.reduce((total, { weight }) => total += weight, 0); if (this.totalCitationWeight > params.revaluationLimit) { throw new Error('Post total citation weight exceeds revaluation limit ' @@ -23,8 +24,8 @@ class Post extends Actor { } } - setPostValue(value) { - this.setValue('value', value); + async setPostValue(value) { + await this.setValue('value', value); this.value = value; } @@ -39,7 +40,7 @@ class Post extends Actor { export class Forum extends Actor { constructor(name, scene) { super(name, scene); - this.posts = new Graph(); + this.posts = new Graph(scene); this.actions = { addPost: new Action('add post', scene), }; @@ -47,9 +48,9 @@ export class Forum extends Actor { async addPost(authorId, postContent) { const post = new Post(this, authorId, postContent); - this.actions.addPost.log(this, post); - this.posts.addVertex(post.id, post); - for (const { postId: citedPostId, weight } of postContent.citations) { + await this.actions.addPost.log(this, post); + this.posts.addVertex(post.id, post, post.title); + for (const { postId: citedPostId, weight } of post.citations) { this.posts.addEdge('citation', post.id, citedPostId, { weight }); } return post.id; @@ -63,7 +64,7 @@ export class Forum extends Actor { return this.posts.getVertices(); } - propagateValue(postId, increment, depth = 0) { + async propagateValue(postId, increment, depth = 0) { if (depth > params.maxPropagationDepth) { return []; } @@ -78,7 +79,7 @@ export class Forum extends Actor { // Increment the value of the given post const postValue = post.getPostValue(); - post.setPostValue(postValue + increment); + await post.setPostValue(postValue + increment); // Award reputation to post author console.log('reward for post author', post.authorPublicKey, increment); @@ -86,7 +87,7 @@ export class Forum extends Actor { // Recursively distribute reputation to citations, according to weights for (const { postId: citedPostId, weight } of post.citations) { - addRewards(this.propagateValue(citedPostId, weight * increment, depth + 1)); + addRewards(await this.propagateValue(citedPostId, weight * increment, depth + 1)); } return rewards; diff --git a/forum-network/src/classes/graph.js b/forum-network/src/classes/graph.js index 303c2c1..84bbd74 100644 --- a/forum-network/src/classes/graph.js +++ b/forum-network/src/classes/graph.js @@ -1,6 +1,3 @@ -import mermaid from 'https://unpkg.com/mermaid@9.2.2/dist/mermaid.esm.min.mjs'; -import { debounce } from '../util.js'; - export class Vertex { constructor(data) { this.data = data; @@ -29,13 +26,14 @@ export class Edge { export class CategorizedEdges {} export class Graph { - constructor() { + constructor(scene) { + this.scene = scene; this.vertices = new Map(); this.edgeLabels = new Map(); this.nextVertexId = 0; } - addVertex(id, data) { + addVertex(id, data, label) { // Support simple case of auto-incremented numeric ids if (typeof id === 'object') { data = id; @@ -46,6 +44,9 @@ export class Graph { } const vertex = new Vertex(data); this.vertices.set(id, vertex); + if (this.scene.flowchart) { + this.scene.flowchart.log(`${id}[${label ?? id}]`); + } return this; } @@ -83,6 +84,9 @@ export class Graph { this.setEdge(label, from, to, edge); this.getVertex(from).edges.from.push(edge); this.getVertex(to).edges.to.push(edge); + if (this.scene.flowchart) { + this.scene.flowchart.log(`${from} --> ${to}`); + } return this; } @@ -101,20 +105,4 @@ export class Graph { countVertices() { return this.vertices.size; } - - async renderGraph() { - const render = async () => { - const dateStart = new Date(); - const graph = await mermaid.mermaidAPI.render( - this.seqDiagramElement.getId(), - this.logBox.getInnerText(), - ); - this.seqDiagramBox.setInnerHTML(graph); - if (!this.dateLastRender) { - this.dateLastRender = new Date(); - } - this.dateLastRender = dateStart; - }; - debounce(render, 100); - } } diff --git a/forum-network/src/classes/post-content.js b/forum-network/src/classes/post-content.js index f85e32f..8512f3f 100644 --- a/forum-network/src/classes/post-content.js +++ b/forum-network/src/classes/post-content.js @@ -28,18 +28,27 @@ export class PostContent { return this; } + setTitle(title) { + this.title = title; + return this; + } + toJSON() { return { content: this.content, citations: this.citations.map((citation) => citation.toJSON()), ...(this.id ? { id: this.id } : {}), + title: this.title, }; } - static fromJSON({ id, content, citations }) { + static fromJSON({ + id, content, citations, title, + }) { const post = new PostContent(content); post.citations = citations.map((citation) => Citation.fromJSON(citation)); post.id = id; + post.title = title; return post; } } diff --git a/forum-network/src/classes/scene.js b/forum-network/src/classes/scene.js index a8db134..7e4be90 100644 --- a/forum-network/src/classes/scene.js +++ b/forum-network/src/classes/scene.js @@ -3,24 +3,47 @@ import { Actor } from './actor.js'; import { Action } from './action.js'; import { debounce } from '../util.js'; +class MermaidDiagram { + constructor(box) { + this.box = box; + this.container = this.box.addBox('Container'); + this.element = this.box.addBox('Element'); + this.renderBox = this.box.addBox('Render'); + this.box.addBox('Spacer').setInnerHTML(' '); + this.logBox = this.box.addBox('Log'); + } + + async log(msg, render = true) { + this.logBox.addBox().setInnerHTML(msg).monospace(); + if (render) { + await this.render(); + } + return this; + } + + async render() { + const render = async () => { + const innerText = this.logBox.getInnerText(); + const graph = await mermaid.mermaidAPI.render( + this.element.getId(), + innerText, + ); + this.renderBox.setInnerHTML(graph); + }; + await debounce(render, 100); + } +} + export class Scene { constructor(name, rootBox) { this.name = name; this.box = rootBox.addBox(name); - this.titleBox = this.box.addBox().setInnerHTML(name); + this.titleBox = this.box.addBox('Title').setInnerHTML(name); this.box.addBox('Spacer').setInnerHTML(' '); - this.displayValuesBox = this.box.addBox(`${this.name}-values`); + this.displayValuesBox = this.box.addBox('Values'); this.box.addBox('Spacer').setInnerHTML(' '); this.actors = new Set(); - this.seqDiagramContainer = this.box.addBox( - `${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.logBox = this.box.addBox(`${this.name}-log`); + mermaid.mermaidAPI.initialize({ startOnLoad: false, theme: 'base', @@ -28,18 +51,37 @@ export class Scene { darkMode: true, primaryColor: '#2a5b6c', primaryTextColor: '#b6b6b6', + // lineColor: '#349cbd', + lineColor: '#57747d', + signalColor: '#57747d', + // signalColor: '#349cbd', noteBkgColor: '#516f77', noteTextColor: '#cecece', activationBkgColor: '#1d3f49', activationBorderColor: '#569595', - signalColor: '#57747d', }, }); - this.dateLastRender = null; } - addActor(name) { + withSequenceDiagram() { + const box = this.box.addBox('Sequence diagram'); + this.sequence = new MermaidDiagram(box); + this.sequence.log('sequenceDiagram', false); + return this; + } + + withFlowchart(direction = 'BT') { + const box = this.box.addBox('Flowchart'); + this.flowchart = new MermaidDiagram(box); + this.flowchart.log(`graph ${direction}`, false); + return this; + } + + async addActor(name) { const actor = new Actor(name, this); + if (this.sequence) { + await this.scene.sequence.log(`participant ${name}`); + } return actor; } @@ -57,12 +99,6 @@ export class Scene { return dv; } - log(msg) { - this.logBox.addBox().setInnerHTML(msg).monospace(); - this.renderSequenceDiagram(); - return this; - } - deactivateAll() { for (const actor of this.actors.values()) { while (actor.active) { @@ -70,20 +106,4 @@ export class Scene { } } } - - async renderSequenceDiagram() { - const render = async () => { - const dateStart = new Date(); - const graph = await mermaid.mermaidAPI.render( - this.seqDiagramElement.getId(), - this.logBox.getInnerText(), - ); - this.seqDiagramBox.setInnerHTML(graph); - if (!this.dateLastRender) { - this.dateLastRender = new Date(); - } - this.dateLastRender = dateStart; - }; - debounce(render, 100); - } } diff --git a/forum-network/src/classes/validation-pool.js b/forum-network/src/classes/validation-pool.js index 0c2a4c0..84fc053 100644 --- a/forum-network/src/classes/validation-pool.js +++ b/forum-network/src/classes/validation-pool.js @@ -193,12 +193,12 @@ export class ValidationPool extends Actor { if (quorumMet) { this.setStatus(`Resolved - ${result ? 'Won' : 'Lost'}`); - this.scene.log(`note over ${this.name} : ${result ? 'Win' : 'Lose'}`); + this.scene.sequence.log(`note over ${this.name} : ${result ? 'Win' : 'Lose'}`); this.applyTokenLocking(); - this.distributeTokens(result); + await this.distributeTokens(result); } else { this.setStatus('Resolved - Quorum not met'); - this.scene.log(`note over ${this.name} : Quorum not met`); + this.scene.sequence.log(`note over ${this.name} : Quorum not met`); } this.deactivate(); @@ -206,7 +206,7 @@ export class ValidationPool extends Actor { return result; } - distributeTokens({ votePasses }) { + async distributeTokens({ votePasses }) { const rewards = new Map(); const addReward = (id, value) => rewards.set(id, (rewards.get(id) ?? 0) + value); @@ -231,7 +231,7 @@ export class ValidationPool extends Actor { if (votePasses && !!this.forum) { // Recurse through forum to determine reputation effects - const forumReputationEffects = this.forum.propagateValue(this.postId, this.tokensMinted); + const forumReputationEffects = await this.forum.propagateValue(this.postId, this.tokensMinted); for (const [id, value] of forumReputationEffects) { addReward(id, value); } diff --git a/forum-network/src/index.html b/forum-network/src/index.html index fb3d946..96eafef 100644 --- a/forum-network/src/index.html +++ b/forum-network/src/index.html @@ -14,5 +14,6 @@
  • Debounce
  • Availability
  • Forum
  • +
  • Flowchart
  • diff --git a/forum-network/src/tests/availability.html b/forum-network/src/tests/availability.html index bcda9f5..0ffce02 100644 --- a/forum-network/src/tests/availability.html +++ b/forum-network/src/tests/availability.html @@ -56,13 +56,13 @@ const updateDisplayValues = async () => { for (const expert of experts) { - expert.setValue( + await expert.setValue( "rep", bench.reputations.getTokens(expert.reputationPublicKey) ); } - bench.setValue("total rep", bench.getTotalReputation()); - await scene.renderSequenceDiagram(); + await bench.setValue("total rep", bench.getTotalReputation()); + await scene.sequence.render(); }; const updateDisplayValuesAndDelay = async () => { @@ -77,7 +77,7 @@ request = await expert.getAssignedWork(availability, business); if (request) { worker = expert; - worker.actions.getAssignedWork.log(worker, availability); + await worker.actions.getAssignedWork.log(worker, availability); worker.activate(); break; } diff --git a/forum-network/src/tests/debounce.html b/forum-network/src/tests/debounce.html new file mode 100644 index 0000000..f1aff20 --- /dev/null +++ b/forum-network/src/tests/debounce.html @@ -0,0 +1,33 @@ + + + Debounce test + + + +
    + + diff --git a/forum-network/src/tests/flowchart.html b/forum-network/src/tests/flowchart.html new file mode 100644 index 0000000..1330faa --- /dev/null +++ b/forum-network/src/tests/flowchart.html @@ -0,0 +1,39 @@ + + + Flowchart test + + + +
    + + diff --git a/forum-network/src/tests/forum-network.html b/forum-network/src/tests/forum-network.html index 9ff7814..4e528a7 100644 --- a/forum-network/src/tests/forum-network.html +++ b/forum-network/src/tests/forum-network.html @@ -43,7 +43,7 @@ await window.forumNode2.processNextMessage(); await window.forumNode3.processNextMessage(); - await window.scene.renderSequenceDiagram(); + await window.scene.sequence.render(); }, 100); // const blockchain = new Blockchain(); diff --git a/forum-network/src/tests/forum.html b/forum-network/src/tests/forum.html index 11faa2f..c17bd37 100644 --- a/forum-network/src/tests/forum.html +++ b/forum-network/src/tests/forum.html @@ -23,9 +23,9 @@ const rootElement = document.getElementById("forum-test"); const rootBox = new Box("rootBox", rootElement).flex(); - const scene = (window.scene = new Scene("Forum test", rootBox).log( - "sequenceDiagram" - )); + const scene = (window.scene = new Scene("Forum test", rootBox)); + scene.withSequenceDiagram(); + scene.withFlowchart(); const experts = (window.experts = []); const newExpert = async () => { @@ -44,13 +44,12 @@ const updateDisplayValues = async () => { for (const expert of experts) { - expert.setValue( + await expert.setValue( "rep", bench.reputations.getTokens(expert.reputationPublicKey) ); } - bench.setValue("total rep", bench.getTotalReputation()); - await scene.renderSequenceDiagram(); + await bench.setValue("total rep", bench.getTotalReputation()); }; const updateDisplayValuesAndDelay = async (delayMs) => { @@ -63,7 +62,7 @@ const { postId: postId1, pool: pool1 } = await expert1.submitPostWithFee( bench, forum, - new PostContent({ hello: "there" }), + new PostContent({ hello: "there" }).setTitle("Post 1"), { fee: 10, duration: 1000, @@ -81,7 +80,9 @@ const { postId: postId2, pool: pool2 } = await expert2.submitPostWithFee( bench, forum, - new PostContent({ hello: "to you as well" }).addCitation(postId1, 0.5), + new PostContent({ hello: "to you as well" }) + .setTitle("Post 2") + .addCitation(postId1, 0.5), { fee: 10, duration: 1000, @@ -99,7 +100,9 @@ const { pool: pool3 } = await expert3.submitPostWithFee( bench, forum, - new PostContent({ hello: "y'all" }).addCitation(postId2, 0.5), + new PostContent({ hello: "y'all" }) + .setTitle("Post 3") + .addCitation(postId2, 0.5), { fee: 10, duration: 1000, diff --git a/forum-network/src/tests/graph.html b/forum-network/src/tests/graph.html new file mode 100644 index 0000000..d905ce2 --- /dev/null +++ b/forum-network/src/tests/graph.html @@ -0,0 +1,33 @@ + + + Forum Graph + + + +
    + + diff --git a/forum-network/src/tests/validation-pool.html b/forum-network/src/tests/validation-pool.html index c018e61..c5cb21e 100644 --- a/forum-network/src/tests/validation-pool.html +++ b/forum-network/src/tests/validation-pool.html @@ -17,9 +17,8 @@ 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 scene = (window.scene = new Scene("Validation Pool test", rootBox)); + await scene.withSequenceDiagram(); const expert1 = (window.expert1 = await new Expert( "Expert1", scene @@ -31,21 +30,21 @@ const bench = (window.bench = new Bench(undefined, "Bench", scene)); const updateDisplayValues = async () => { - expert1.setValue( + await expert1.setValue( "rep", bench.reputations.getTokens(expert1.reputationPublicKey) ); - expert2.setValue( + await expert2.setValue( "rep", bench.reputations.getTokens(expert2.reputationPublicKey) ); - bench.setValue("total rep", bench.getTotalReputation()); + await bench.setValue("total rep", bench.getTotalReputation()); // With params.lockingTimeExponent = 0 and params.activeVoterThreshold = null, // these next 3 propetries are all equal to total rep - // bench.setValue('available rep', bench.getTotalAvailableReputation()); - // bench.setValue('active rep', bench.getTotalActiveReputation()); - // bench.setValue('active available rep', bench.getTotalActiveAvailableReputation()); - await scene.renderSequenceDiagram(); + // await bench.setValue('available rep', bench.getTotalAvailableReputation()); + // await bench.setValue('active rep', bench.getTotalActiveReputation()); + // await bench.setValue('active available rep', bench.getTotalActiveAvailableReputation()); + await scene.sequence.render(); }; updateDisplayValues(); diff --git a/forum-network/src/util.js b/forum-network/src/util.js index a44dd7c..3422ba8 100644 --- a/forum-network/src/util.js +++ b/forum-network/src/util.js @@ -1,17 +1,20 @@ -const timeouts = new Map(); +const timers = new Map(); -export const debounce = (fn, delay) => { - const key = fn.toString(); - if (!timeouts.get(key)) { - timeouts.set(key, setTimeout(async () => { - timeouts.delete(key); - await fn(); - }, delay)); +export const debounce = async (fn, delayMs) => { + const timer = timers.get(fn); + if (timer) { + return timer.result; } + const result = await fn(); + timers.set(fn, { result }); + setTimeout(() => { + timers.delete(fn); + }, delayMs); + return result; }; -export const delay = async (ms) => { +export const delay = async (delayMs) => { await new Promise((resolve) => { - setTimeout(resolve, ms); + setTimeout(resolve, delayMs); }); };