From a481710cded93b2eb9fb9ad62f61319b1cf77542 Mon Sep 17 00:00:00 2001
From: Ladd Hoffman
Date: Mon, 14 Nov 2022 10:17:43 -0600
Subject: [PATCH] Automatic seq graph rendering, with debounce
---
forum-network/public/classes/box.js | 2 +-
forum-network/public/classes/reputation.js | 5 ++-
forum-network/public/classes/scene.js | 45 ++++++++++++-------
.../public/classes/validation-pool.js | 3 +-
forum-network/public/debounce-test.html | 9 ++++
forum-network/public/debounce-test.js | 15 +++++++
forum-network/public/forum-test.js | 12 ++---
forum-network/public/index.css | 3 ++
forum-network/public/index.html | 1 +
forum-network/public/mermaid-test.html | 20 ++++++++-
forum-network/public/util.js | 15 +++++++
forum-network/public/validation-pool-test.js | 22 +++++----
12 files changed, 115 insertions(+), 37 deletions(-)
create mode 100644 forum-network/public/debounce-test.html
create mode 100644 forum-network/public/debounce-test.js
create mode 100644 forum-network/public/util.js
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();