diff --git a/forum-network/src/classes/forum.js b/forum-network/src/classes/forum.js
index 3880421..cbd0629 100644
--- a/forum-network/src/classes/forum.js
+++ b/forum-network/src/classes/forum.js
@@ -1,5 +1,5 @@
import { Actor } from './actor.js';
-import { WDAG } from './wdag.js';
+import { WDAG, Vertex } from './wdag.js';
import { Action } from './action.js';
import { CryptoUtil } from './crypto.js';
import params from '../params.js';
@@ -14,7 +14,7 @@ class Post extends Actor {
const index = forum.posts.countVertices();
const name = `Post${index + 1}`;
super(name, forum.scene);
- this.id = postContent.id ?? `post_${CryptoUtil.randomUUID().slice(0, 4)}`;
+ this.id = postContent.id ?? name;
this.authorPublicKey = authorPublicKey;
this.value = 0;
this.initialValue = 0;
@@ -147,41 +147,48 @@ export class Forum extends ReputationHolder {
if (params.referenceChainLimit === null || depth <= params.referenceChainLimit) {
for (const citationEdge of postVertex.getEdges(CITATION, true)) {
const { to: citedPostVertex, weight } = citationEdge;
- const citedPost = citedPostVertex.data;
let outboundAmount = weight * increment;
- const balance = this.posts.getEdge(BALANCE, postVertex, citedPostVertex)?.data || 0;
+ const balance = this.posts.getEdge(BALANCE, postVertex, citedPostVertex)?.weight || 0;
console.log('Citation', {
- citationEdge, outboundAmount, balance, citedPostValue: citedPost.value,
+ citationEdge, outboundAmount, balance,
});
// We need to ensure that we propagate no more reputation than we leached
- if (outboundAmount < 0) {
- outboundAmount = Math.max(outboundAmount, -citedPost.value);
- if (depth > 0) {
- outboundAmount = Math.max(outboundAmount, -balance);
- }
+ if (depth > 0 && weight < 0) {
+ outboundAmount = outboundAmount < 0
+ ? Math.max(outboundAmount, -balance)
+ : Math.min(outboundAmount, -balance);
}
- increment -= outboundAmount * params.leachingValue;
- this.posts.setEdge(BALANCE, postVertex, citedPostVertex, balance + outboundAmount);
- await this.propagateValue(citationEdge, {
+ const refundFromOutbound = await this.propagateValue(citationEdge, {
rewardsAccumulator,
increment: outboundAmount,
depth: depth + 1,
});
+ outboundAmount -= refundFromOutbound;
+ this.posts.setEdge(BALANCE, postVertex, citedPostVertex, balance + outboundAmount);
+ increment -= outboundAmount * params.leachingValue;
}
}
- const newValue = post.value + increment;
+ const rawNewValue = post.value + increment;
+ const newValue = Math.max(0, rawNewValue);
+ const appliedIncrement = newValue - post.value;
+ const refundToInbound = increment - appliedIncrement;
console.log('propagateValue end', {
depth,
increment,
+ rawNewValue,
newValue,
+ appliedIncrement,
+ refundToInbound,
});
// Award reputation to post author
- rewardsAccumulator.set(post.tokenId, increment);
+ rewardsAccumulator.set(post.tokenId, appliedIncrement);
// Increment the value of the post
await this.setPostValue(post, newValue);
+
+ return refundToInbound;
}
}
diff --git a/forum-network/src/classes/wdag.js b/forum-network/src/classes/wdag.js
index 58000aa..2aca682 100644
--- a/forum-network/src/classes/wdag.js
+++ b/forum-network/src/classes/wdag.js
@@ -1,4 +1,4 @@
-class Vertex {
+export class Vertex {
constructor(id, data) {
this.id = id;
this.data = data;
@@ -15,7 +15,7 @@ class Vertex {
}
}
-class Edge {
+export class Edge {
constructor(label, from, to, weight) {
this.from = from;
this.to = to;
@@ -80,9 +80,7 @@ export class WDAG {
setEdge(label, from, to, edge) {
from = from instanceof Vertex ? from : this.getVertex(from);
to = to instanceof Vertex ? to : this.getVertex(to);
- if (!(edge instanceof Edge)) {
- edge = new Edge(edge);
- }
+ edge = typeof edge === 'number' ? new Edge(label, from, to, edge) : edge;
let edges = this.edgeLabels.get(label);
if (!edges) {
edges = new Map();
diff --git a/forum-network/src/index.html b/forum-network/src/index.html
index 6bdf9f7..9d68a12 100644
--- a/forum-network/src/index.html
+++ b/forum-network/src/index.html
@@ -9,7 +9,13 @@
Secondary
diff --git a/forum-network/src/tests/forum.test.html b/forum-network/src/tests/forum1.test.html
similarity index 91%
rename from forum-network/src/tests/forum.test.html
rename to forum-network/src/tests/forum1.test.html
index b6ba3ae..535190e 100644
--- a/forum-network/src/tests/forum.test.html
+++ b/forum-network/src/tests/forum1.test.html
@@ -1,6 +1,6 @@
- Forum test
+ Forum test 1
@@ -10,9 +10,9 @@
-
-
+
+
+
+
+
+
+
diff --git a/forum-network/src/tests/scripts/forum.test-util.js b/forum-network/src/tests/scripts/forum.test-util.js
new file mode 100644
index 0000000..c4df44f
--- /dev/null
+++ b/forum-network/src/tests/scripts/forum.test-util.js
@@ -0,0 +1,88 @@
+import { Box } from '../../classes/box.js';
+import { Scene } from '../../classes/scene.js';
+import { Expert } from '../../classes/expert.js';
+import { Bench } from '../../classes/bench.js';
+import { delay } from '../../util.js';
+import { Forum } from '../../classes/forum.js';
+import { PostContent } from '../../classes/post-content.js';
+import params from '../../params.js';
+
+const DEFAULT_DELAY_MS = 1;
+const POOL_DURATION_MS = 50;
+
+export class ForumTest {
+ constructor() {
+ this.scene = null;
+ this.forum = null;
+ this.bench = null;
+ this.experts = null;
+ this.posts = null;
+ }
+
+ async newExpert() {
+ const index = this.experts.length;
+ const name = `Expert${index + 1}`;
+ const expert = await new Expert(name, this.scene).initialize();
+ this.experts.push(expert);
+ // expert.addValue('rep', () => bench.reputation.valueOwnedBy(expert.reputationPublicKey))
+ return expert;
+ }
+
+ async addPost(author, fee, citations = []) {
+ const postIndex = this.posts.length;
+ const title = `posts[${postIndex}]`;
+ await this.scene.startSection();
+
+ const postContent = new PostContent({}).setTitle(title);
+ for (const { postId, weight } of citations) {
+ postContent.addCitation(postId, weight);
+ }
+
+ const { pool, postId } = await author.submitPostWithFee(
+ this.bench,
+ this.forum,
+ postContent,
+ {
+ fee,
+ duration: POOL_DURATION_MS,
+ tokenLossRatio: 1,
+ },
+ );
+ this.posts.push(postId);
+ await delay(POOL_DURATION_MS);
+ await pool.evaluateWinningConditions();
+ await this.scene.endSection();
+ await delay(DEFAULT_DELAY_MS);
+ return postId;
+ }
+
+ async setup() {
+ const rootElement = document.getElementById('scene');
+ const rootBox = new Box('rootBox', rootElement).flex();
+
+ this.scene = (window.scene = new Scene('Forum test', rootBox));
+ this.scene.withSequenceDiagram();
+ this.scene.withFlowchart();
+ this.scene.withTable();
+
+ this.scene.addDisplayValue('c3. stakeForAuthor').set(params.stakeForAuthor);
+ this.scene.addDisplayValue('q2. revaluationLimit').set(params.revaluationLimit);
+ this.scene
+ .addDisplayValue('q3. referenceChainLimit')
+ .set(params.referenceChainLimit);
+ this.scene.addDisplayValue('q4. leachingValue').set(params.leachingValue);
+ this.scene.addDisplayValue(' ');
+
+ this.forum = (window.forum = new Forum('Forum', this.scene));
+ this.bench = (window.bench = new Bench(this.forum, 'Bench', this.scene));
+ this.experts = (window.experts = []);
+ this.posts = (window.posts = []);
+
+ await this.newExpert();
+ // await newExpert();
+ // await newExpert();
+
+ // bench.addValue('total rep', () => bench.reputation.getTotal());
+ this.forum.addValue('total value', () => this.forum.getTotalValue());
+ }
+}
diff --git a/forum-network/src/tests/scripts/forum.test.js b/forum-network/src/tests/scripts/forum.test.js
deleted file mode 100644
index a0a2fc6..0000000
--- a/forum-network/src/tests/scripts/forum.test.js
+++ /dev/null
@@ -1,135 +0,0 @@
-import { Box } from '../../classes/box.js';
-import { Scene } from '../../classes/scene.js';
-import { Expert } from '../../classes/expert.js';
-import { Bench } from '../../classes/bench.js';
-import { delay } from '../../util.js';
-import { Forum } from '../../classes/forum.js';
-import { PostContent } from '../../classes/post-content.js';
-import params from '../../params.js';
-
-const DEFAULT_DELAY_MS = 1;
-const POOL_DURATION_MS = 50;
-
-let scene;
-let forum;
-let bench;
-let experts;
-let posts;
-
-async function newExpert() {
- const index = experts.length;
- const name = `Expert${index + 1}`;
- const expert = await new Expert(name, scene).initialize();
- experts.push(expert);
- return expert;
-}
-
-async function addPost(author, fee, citations = []) {
- const postIndex = posts.length;
- const title = `posts[${postIndex}]`;
- await scene.startSection();
-
- const postContent = new PostContent({}).setTitle(title);
- for (const { postId, weight } of citations) {
- postContent.addCitation(postId, weight);
- }
-
- const { pool, postId } = await author.submitPostWithFee(
- bench,
- forum,
- postContent,
- {
- fee,
- duration: POOL_DURATION_MS,
- tokenLossRatio: 1,
- },
- );
- posts.push(postId);
- await delay(POOL_DURATION_MS);
- await pool.evaluateWinningConditions();
- await scene.endSection();
- await delay(DEFAULT_DELAY_MS);
- return postId;
-}
-
-async function setup() {
- const rootElement = document.getElementById('scene');
- const rootBox = new Box('rootBox', rootElement).flex();
-
- scene = (window.scene = new Scene('Forum test', rootBox));
- scene.withSequenceDiagram();
- scene.withFlowchart();
- scene.withTable();
-
- scene.addDisplayValue('c3. stakeForAuthor').set(params.stakeForAuthor);
- scene.addDisplayValue('q2. revaluationLimit').set(params.revaluationLimit);
- scene
- .addDisplayValue('q3. referenceChainLimit')
- .set(params.referenceChainLimit);
- scene.addDisplayValue('q4. leachingValue').set(params.leachingValue);
- scene.addDisplayValue(' ');
-
- forum = (window.forum = new Forum('Forum', scene));
- bench = (window.bench = new Bench(forum, 'Bench', scene));
- experts = (window.experts = []);
- posts = (window.posts = []);
-
- await newExpert();
- // await newExpert();
- // await newExpert();
-
- // bench.addValue('total rep', () => bench.reputation.getTotal());
- forum.addValue('total value', () => forum.getTotalValue());
-
-// for (const expert of experts) {
-// expert.addValue('rep', () => bench.reputation.valueOwnedBy(expert.reputationPublicKey));
-// }
-}
-
-describe('Forum', () => {
- before(async () => {
- await setup();
- });
- context('Negative citation of a negative citation with max strength', async () => {
- it('Post1', async () => {
- await addPost(experts[0], 10);
- forum.getPost(posts[0]).value.should.equal(10);
- });
- it('Post2 negatively cites Post1', async () => {
- await addPost(experts[0], 10, [{ postId: posts[0], weight: -1 }]);
- forum.getPost(posts[0]).value.should.equal(0);
- forum.getPost(posts[1]).value.should.equal(20);
- });
- it('Post3 negatively cites Post2, restoring Post1 post to its initial value', async () => {
- await addPost(experts[0], 10, [{ postId: posts[1], weight: -1 }]);
- forum.getPost(posts[0]).value.should.equal(10);
- forum.getPost(posts[1]).value.should.equal(0);
- forum.getPost(posts[2]).value.should.equal(20);
- });
- });
-
- context('Negative citation of a weaker negative citation', async () => {
- it('Post4', async () => {
- await addPost(experts[0], 10);
- forum.getPost(posts[3]).value.should.equal(10);
- });
- it('Post5 negatively cites Post4', async () => {
- await addPost(experts[0], 10, [{ postId: posts[3], weight: -0.5 }]);
- forum.getPost(posts[3]).value.should.equal(5);
- forum.getPost(posts[4]).value.should.equal(15);
- });
- it('Post6 negatively cites Post5, restoring Post4 post to its initial value', async () => {
- await addPost(experts[0], 20, [{ postId: posts[4], weight: -1 }]);
- forum.getPost(posts[3]).value.should.equal(10);
- forum.getPost(posts[4]).value.should.equal(0);
- forum.getPost(posts[5]).value.should.equal(30);
- });
- });
-});
-
-// await addPost(experts[0], 10);
-// await addPost(experts[0], 10, [{ postId: posts[3], weight: -1 }]);
-// await addPost(experts[0], 10, [{ postId: posts[4], weight: -1 }]);
-
-// await addPost(expert3, 'Post 4', 100, [{ postId: postId2, weight: -1 }]);
-// await addPost(expert1, 'Post 5', 100, [{ postId: postId3, weight: -1 }]);
diff --git a/forum-network/src/tests/scripts/forum1.test.js b/forum-network/src/tests/scripts/forum1.test.js
new file mode 100644
index 0000000..d2c4eb2
--- /dev/null
+++ b/forum-network/src/tests/scripts/forum1.test.js
@@ -0,0 +1,39 @@
+import { ForumTest } from './forum.test-util.js';
+
+describe('Forum', () => {
+ const forumTest = new ForumTest();
+
+ before(async () => {
+ await forumTest.setup();
+ });
+
+ context('Negative citation of a negative citation with max strength', async () => {
+ it('Post1', async () => {
+ const { forum, experts, posts } = forumTest;
+ await forumTest.addPost(experts[0], 10);
+ forum.getPost(posts[0]).value.should.equal(10);
+ });
+
+ it('Post2 negatively cites Post1', async () => {
+ const { forum, experts, posts } = forumTest;
+ await forumTest.addPost(experts[0], 10, [{ postId: posts[0], weight: -1 }]);
+ forum.getPost(posts[0]).value.should.equal(0);
+ forum.getPost(posts[1]).value.should.equal(20);
+ });
+
+ it('Post3 negatively cites Post2, restoring Post1 post to its initial value', async () => {
+ const { forum, experts, posts } = forumTest;
+ await forumTest.addPost(experts[0], 10, [{ postId: posts[1], weight: -1 }]);
+ forum.getPost(posts[0]).value.should.equal(10);
+ forum.getPost(posts[1]).value.should.equal(0);
+ forum.getPost(posts[2]).value.should.equal(20);
+ });
+ });
+});
+
+// await addPost(experts[0], 10);
+// await addPost(experts[0], 10, [{ postId: posts[3], weight: -1 }]);
+// await addPost(experts[0], 10, [{ postId: posts[4], weight: -1 }]);
+
+// await addPost(expert3, 'Post 4', 100, [{ postId: postId2, weight: -1 }]);
+// await addPost(expert1, 'Post 5', 100, [{ postId: postId3, weight: -1 }]);
diff --git a/forum-network/src/tests/scripts/forum2.test.js b/forum-network/src/tests/scripts/forum2.test.js
new file mode 100644
index 0000000..b4737a3
--- /dev/null
+++ b/forum-network/src/tests/scripts/forum2.test.js
@@ -0,0 +1,39 @@
+import { ForumTest } from './forum.test-util.js';
+
+describe('Forum', () => {
+ const forumTest = new ForumTest();
+
+ before(async () => {
+ await forumTest.setup();
+ });
+
+ context('Negative citation of a weaker negative citation', async () => {
+ it('Post4', async () => {
+ const { forum, experts, posts } = forumTest;
+ await forumTest.addPost(experts[0], 10);
+ forum.getPost(posts[0]).value.should.equal(10);
+ });
+
+ it('Post5 negatively cites Post4', async () => {
+ const { forum, experts, posts } = forumTest;
+ await forumTest.addPost(experts[0], 10, [{ postId: posts[0], weight: -0.5 }]);
+ forum.getPost(posts[0]).value.should.equal(5);
+ forum.getPost(posts[1]).value.should.equal(15);
+ });
+
+ it('Post6 negatively cites Post5, restoring Post4 post to its initial value', async () => {
+ const { forum, experts, posts } = forumTest;
+ await forumTest.addPost(experts[0], 20, [{ postId: posts[1], weight: -1 }]);
+ forum.getPost(posts[0]).value.should.equal(10);
+ forum.getPost(posts[1]).value.should.equal(0);
+ forum.getPost(posts[2]).value.should.equal(30);
+ });
+ });
+});
+
+// await addPost(experts[0], 10);
+// await addPost(experts[0], 10, [{ postId: posts[3], weight: -1 }]);
+// await addPost(experts[0], 10, [{ postId: posts[4], weight: -1 }]);
+
+// await addPost(expert3, 'Post 4', 100, [{ postId: postId2, weight: -1 }]);
+// await addPost(expert1, 'Post 5', 100, [{ postId: postId3, weight: -1 }]);