diff --git a/forum-network/public/classes/box.js b/forum-network/public/classes/box.js index 399a6c1..7961f58 100644 --- a/forum-network/public/classes/box.js +++ b/forum-network/public/classes/box.js @@ -47,7 +47,7 @@ export class Box { } setId(id) { - this.el.id = id || this.name; + this.el.id = (id || this.name).replace(/ /g, ""); return this; } diff --git a/forum-network/public/classes/reputation.js b/forum-network/public/classes/reputation.js index 4eb019a..00bab5b 100644 --- a/forum-network/public/classes/reputation.js +++ b/forum-network/public/classes/reputation.js @@ -37,7 +37,10 @@ class Reputation { const tokensLocked = Array.from(this.locks.values()) .filter(({dateCreated, duration}) => now - dateCreated < duration) .reduce((acc, cur) => acc += cur.tokens, 0); - return Math.max(this.tokens - tokensLocked, 0); + if (tokensLocked > this.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 1c0f33c..7b2d33c 100644 --- a/forum-network/public/classes/scene.js +++ b/forum-network/public/classes/scene.js @@ -1,6 +1,7 @@ import { Actor } from './actor.js'; import { Action } from './action.js'; -// import mermaid from 'https://unpkg.com/mermaid@9.2.2/dist/mermaid.esm.mjs'; +import { debounce } from '../util.js'; +import mermaid from 'https://unpkg.com/mermaid@9.2.2/dist/mermaid.esm.mjs'; export class Scene { constructor(name, rootBox) { @@ -10,11 +11,14 @@ export class Scene { this.box.addBox('Spacer').setInnerHTML(' '); this.displayValuesBox = this.box.addBox(`${this.name}-values`); this.box.addBox('Spacer').setInnerHTML(' '); - this.logBox = this.box.addBox(`${this.name}-log`); this.actors = new Set(); - // this.seqDiagramContainer = this.box.addBox(`${this.name}-seq-diagram-container`); - // this.seqDiagramBox = this.box.addBox(`${this.name}-seq-diagram`); - // mermaid.mermaidAPI.initialize({ startOnLoad: false }); + 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 }); + this.dateLastRender = null; } addActor(name) { @@ -38,6 +42,7 @@ export class Scene { log(msg) { this.logBox.addBox().setInnerHTML(msg).monospace(); + this.renderSequenceDiagram(); return this; } @@ -50,16 +55,24 @@ export class Scene { } } - // async renderSequenceDiagram() { - // await mermaid.mermaidAPI.render( - // `${this.name}-seq-diagram-element`, - // this.logBox.getInnerText(), - // this.insertSvg, - // this.seqDiagramContainer.el - // ); - // } + 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(); + } + console.log(`renderSequenceDiagram time: ${new Date() - dateStart} ms, time since last render: ${dateStart - this.dateLastRender}`); + this.dateLastRender = dateStart; + }; + debounce(render, 100); + } - // insertSvg (svgCode) { - // this.seqDiagramBox.setInnerHTML(svgCode); - // }; + insertSvg (svgCode) { + this.seqDiagramElement.setInnerHTML(svgCode); + }; } diff --git a/forum-network/public/classes/validation-pool.js b/forum-network/public/classes/validation-pool.js index 25534b6..462da6c 100644 --- a/forum-network/public/classes/validation-pool.js +++ b/forum-network/public/classes/validation-pool.js @@ -35,6 +35,7 @@ export class ValidationPool extends Actor { against: fee * params.mintingRatio * (1 - params.stakeForWin), author: fee * params.mintingRatio * params.stakeForAuthor, } + // TODO: Consider availability stakes } castVote(signingPublicKey, position, stake, lockingTime) { @@ -126,7 +127,7 @@ export class ValidationPool extends Actor { distributeTokens(result) { // Reward the author - // TODO: Penalty to the author if the vote does not pass? + // TODO: If the vote fails, distribute tokens.author among winning voters this.bench.reputations.addTokens(this.authorId, this.tokens.author); // Reward the vote winners, in proportion to their stakes const tokensForWinners = result ? this.tokens.for : this.tokens.against; diff --git a/forum-network/public/debounce-test.html b/forum-network/public/debounce-test.html new file mode 100644 index 0000000..199e36d --- /dev/null +++ b/forum-network/public/debounce-test.html @@ -0,0 +1,9 @@ + + + Forum Graph + + + + +
+ diff --git a/forum-network/public/debounce-test.js b/forum-network/public/debounce-test.js new file mode 100644 index 0000000..7258851 --- /dev/null +++ b/forum-network/public/debounce-test.js @@ -0,0 +1,15 @@ +import { Box } from './classes/box.js'; +import { Scene } from './classes/scene.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"); +debounce(log, 500); +debounce(log, 500); +await delay(500); +debounce(log, 500); +debounce(log, 500); diff --git a/forum-network/public/forum-test.js b/forum-network/public/forum-test.js index 4957534..8d44023 100644 --- a/forum-network/public/forum-test.js +++ b/forum-network/public/forum-test.js @@ -4,10 +4,7 @@ import { Post } from './classes/post.js'; import { Member } from './classes/member.js'; import { ForumNode } from './classes/forum-node.js'; import { ForumNetwork } from './classes/forum-network.js'; - -const delay = async (ms) => { - await new Promise((resolve) => setTimeout(resolve, ms)); -}; +import { delay } from './util.js'; const rootElement = document.getElementById('forum-network'); const rootBox = new Box('rootBox', rootElement).flex(); @@ -23,10 +20,12 @@ window.forumNode1 = await new ForumNode('node1', window.scene).initialize(window window.forumNode2 = await new ForumNode('node2', window.scene).initialize(window.forumNetwork); window.forumNode3 = await new ForumNode('node3', window.scene).initialize(window.forumNetwork); -setInterval(async () => { +const processInterval = setInterval(async () => { await window.forumNode1.processNextMessage(); await window.forumNode2.processNextMessage(); await window.forumNode3.processNextMessage(); + + await window.scene.renderSequenceDiagram(); }, 100); // const blockchain = new Blockchain(); @@ -38,3 +37,6 @@ await delay(1000); await window.author1.submitPost(window.forumNode1, window.post1, 50); await delay(1000); await window.author2.submitPost(window.forumNode2, window.post2, 100); + +await delay(1000); +clearInterval(processInterval); diff --git a/forum-network/public/index.css b/forum-network/public/index.css index b987a08..e7f0898 100644 --- a/forum-network/public/index.css +++ b/forum-network/public/index.css @@ -22,3 +22,6 @@ font-family: monospace; font-size: 11pt; } +svg { + width: 800px; +} diff --git a/forum-network/public/index.html b/forum-network/public/index.html index 12a74be..0c52a8e 100644 --- a/forum-network/public/index.html +++ b/forum-network/public/index.html @@ -10,5 +10,6 @@
  • Graph test
  • Validation Pool test
  • Mermaid test
  • +
  • Debounce test
  • diff --git a/forum-network/public/mermaid-test.html b/forum-network/public/mermaid-test.html index f86538c..427e92c 100644 --- a/forum-network/public/mermaid-test.html +++ b/forum-network/public/mermaid-test.html @@ -2,7 +2,25 @@ Forum Network + + +
    - diff --git a/forum-network/public/util.js b/forum-network/public/util.js new file mode 100644 index 0000000..4607eab --- /dev/null +++ b/forum-network/public/util.js @@ -0,0 +1,15 @@ +const timeouts = 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 delay = async (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 2383d7f..f4410ea 100644 --- a/forum-network/public/validation-pool-test.js +++ b/forum-network/public/validation-pool-test.js @@ -2,28 +2,24 @@ import { Box } from './classes/box.js'; import { Scene } from './classes/scene.js'; import { Member } from './classes/member.js'; import { Bench } from './classes/bench.js'; - -const delay = async (ms) => { - await new Promise((resolve) => setTimeout(resolve, ms)); -}; +import { delay } from './util.js'; 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 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 = () => { +const updateDisplayValues = async () => { member1.setValue('rep', bench.reputations.getTokens(member1.reputationPublicKey)); member2.setValue('rep', bench.reputations.getTokens(member2.reputationPublicKey)); bench.setValue('total rep', bench.getTotalReputation()); bench.setValue('available rep', bench.getTotalAvailableReputation()); bench.setValue('active rep', bench.getTotalActiveReputation()); bench.setValue('active available rep', bench.getTotalActiveAvailableReputation()); + await scene.renderSequenceDiagram(); }; updateDisplayValues(); @@ -34,7 +30,7 @@ await delay(1000); const pool = member1.initiateValidationPool(bench, {fee: 1, duration: 1000, tokenLossRatio: 1}); await member1.castVote(pool, true, 0, 0); await member1.revealIdentity(pool); // Vote passes - updateDisplayValues(); + await updateDisplayValues(); await delay(1000); } @@ -43,7 +39,7 @@ try { const pool = member2.initiateValidationPool(bench, {fee: 1, duration: 1000, tokenLossRatio: 1}); await member2.castVote(pool, true, 0, 0); await member2.revealIdentity(pool); // Quorum not met! - updateDisplayValues(); + await updateDisplayValues(); await delay(1000); } catch(e) { if (e.message.match(/Quorum is not met/)) { @@ -59,10 +55,12 @@ try { const pool = member2.initiateValidationPool(bench, {fee: 1, duration: 1000, tokenLossRatio: 1}); await member1.castVote(pool, true, 0.5, 1); await member1.revealIdentity(pool); // Vote passes - updateDisplayValues(); + await updateDisplayValues(); await delay(1000); } -updateDisplayValues(); +await updateDisplayValues(); scene.deactivateAll(); + +await updateDisplayValues();