Fix forum logic for negative citations
This commit is contained in:
parent
0c98ae2505
commit
7cda474d20
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -9,7 +9,13 @@
|
|||
<ul>
|
||||
<li><a href="./tests/validation-pool.test.html">Validation Pool</a></li>
|
||||
<li><a href="./tests/availability.test.html">Availability + Business</a></li>
|
||||
<li><a href="./tests/forum.test.html">Forum</a></li>
|
||||
<li>
|
||||
Forum
|
||||
<ul>
|
||||
<li><a href="./tests/forum1.test.html">1</a></li>
|
||||
<li><a href="./tests/forum2.test.html">2</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>Secondary</h3>
|
||||
<ul>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<head>
|
||||
<title>Forum test</title>
|
||||
<title>Forum test 1</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="https://unpkg.com/mocha/mocha.css" />
|
||||
<link type="text/css" rel="stylesheet" href="../index.css" />
|
||||
|
@ -10,9 +10,9 @@
|
|||
<div id="scene"></div>
|
||||
</body>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/radash/10.7.0/radash.js" integrity="sha512-S207zKWG3iqXqe6msO7/Mr8X3DzzF4u8meFlokHjGtBPTGUhgzVo0lpcqEy0GoiMUdcoct+H+SqzoLsxXbynzg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://unpkg.com/chai/chai.js"></script>
|
||||
<script src="https://unpkg.com/mocha/mocha.js"></script>
|
||||
<script type="module" src="./scripts/forum.test.js"></script>
|
||||
<script src="https://unpkg.com/chai/chai.js"></script>
|
||||
<script type="module" src="./scripts/forum1.test.js"></script>
|
||||
<script defer class="mocha-init">
|
||||
mocha.setup({
|
||||
ui: 'bdd',
|
|
@ -0,0 +1,27 @@
|
|||
<!DOCTYPE html>
|
||||
<head>
|
||||
<title>Forum test 2</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="https://unpkg.com/mocha/mocha.css" />
|
||||
<link type="text/css" rel="stylesheet" href="../index.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="mocha"></div>
|
||||
<div id="scene"></div>
|
||||
</body>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/radash/10.7.0/radash.js" integrity="sha512-S207zKWG3iqXqe6msO7/Mr8X3DzzF4u8meFlokHjGtBPTGUhgzVo0lpcqEy0GoiMUdcoct+H+SqzoLsxXbynzg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://unpkg.com/mocha/mocha.js"></script>
|
||||
<script src="https://unpkg.com/chai/chai.js"></script>
|
||||
<script type="module" src="./scripts/forum2.test.js"></script>
|
||||
<script defer class="mocha-init">
|
||||
mocha.setup({
|
||||
ui: 'bdd',
|
||||
globals: ['scene', 'bench', 'forum', 'experts', 'posts', '__REACT_DEVTOOLS_*'],
|
||||
});
|
||||
mocha.checkLeaks();
|
||||
chai.should();
|
||||
</script>
|
||||
<script defer class="mocha-exec">
|
||||
// TODO: Weird race condition -- resolve this in a better way
|
||||
setTimeout(() => mocha.run(), 1000);
|
||||
</script>
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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 }]);
|
|
@ -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 }]);
|
|
@ -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 }]);
|
Loading…
Reference in New Issue