Merge branch 'dev' into 'main'
Add special case: Incineration See merge request dao-governance-framework/science-publishing-dao!4
This commit is contained in:
commit
5ca884686b
|
@ -10,3 +10,11 @@ Effective power can be considered as a flow rate of posts; (value per post) / (d
|
||||||
Internal energy is similar to Forum total value / DAO total reputation
|
Internal energy is similar to Forum total value / DAO total reputation
|
||||||
|
|
||||||
Total available reputation is similar to thermodynamic free energy
|
Total available reputation is similar to thermodynamic free energy
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Examples to add:
|
||||||
|
|
||||||
|
- Incinerator
|
||||||
|
|
||||||
|
- Negatively cite a zero-value post -- intent is to show how governance might cite a post as a counter-example
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
Matrix is a communications network.
|
||||||
|
It has a client-server, server-server decentralized architecture.
|
||||||
|
Rooms are synced (eventually consistent) among all servers with clients participating in the room.
|
||||||
|
|
||||||
|
Matrix supports "Application Services", which are limited to funcion in a passive mode, meaning they only piggyback on top of the existing protocols.
|
||||||
|
|
||||||
|
Synapse, a Matrix server implementation, supports "Modules"
|
||||||
|
|
||||||
|
The Matrix devs recognize the need for a robust reputation system and are in pursuit of funding and development for that purpose.
|
|
@ -3,7 +3,7 @@ import { Action } from '../display/action.js';
|
||||||
import { Actor } from '../display/actor.js';
|
import { Actor } from '../display/actor.js';
|
||||||
import params from '../../params.js';
|
import params from '../../params.js';
|
||||||
import { ReputationHolder } from '../reputation/reputation-holder.js';
|
import { ReputationHolder } from '../reputation/reputation-holder.js';
|
||||||
import { displayNumber, EPSILON } from '../../util.js';
|
import { displayNumber, EPSILON, INCINERATOR_ADDRESS } from '../../util.js';
|
||||||
|
|
||||||
const CITATION = 'citation';
|
const CITATION = 'citation';
|
||||||
const BALANCE = 'balance';
|
const BALANCE = 'balance';
|
||||||
|
@ -27,11 +27,11 @@ class Post extends Actor {
|
||||||
.reduce((total, { weight }) => total += weight, 0);
|
.reduce((total, { weight }) => total += weight, 0);
|
||||||
if (leachingTotal > params.revaluationLimit) {
|
if (leachingTotal > params.revaluationLimit) {
|
||||||
throw new Error('Post leaching total exceeds revaluation limit '
|
throw new Error('Post leaching total exceeds revaluation limit '
|
||||||
+ `(${leachingTotal} > ${params.revaluationLimit})`);
|
+ `(${leachingTotal} > ${params.revaluationLimit})`);
|
||||||
}
|
}
|
||||||
if (donationTotal > params.revaluationLimit) {
|
if (donationTotal > params.revaluationLimit) {
|
||||||
throw new Error('Post donation total exceeds revaluation limit '
|
throw new Error('Post donation total exceeds revaluation limit '
|
||||||
+ `(${donationTotal} > ${params.revaluationLimit})`);
|
+ `(${donationTotal} > ${params.revaluationLimit})`);
|
||||||
}
|
}
|
||||||
if (this.citations.some(({ weight }) => Math.abs(weight) > params.revaluationLimit)) {
|
if (this.citations.some(({ weight }) => Math.abs(weight) > params.revaluationLimit)) {
|
||||||
throw new Error(`Each citation magnitude must not exceed revaluation limit ${params.revaluationLimit}`);
|
throw new Error(`Each citation magnitude must not exceed revaluation limit ${params.revaluationLimit}`);
|
||||||
|
@ -70,9 +70,14 @@ export class Forum extends ReputationHolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
async addPost(authorId, postContent) {
|
async addPost(authorId, postContent) {
|
||||||
|
console.log('addPost', { authorId, postContent });
|
||||||
const post = new Post(this, authorId, postContent);
|
const post = new Post(this, authorId, postContent);
|
||||||
this.posts.addVertex(post.id, post, post.getLabel());
|
this.posts.addVertex(post.id, post, post.getLabel());
|
||||||
for (const { postId: citedPostId, weight } of post.citations) {
|
for (const { postId: citedPostId, weight } of post.citations) {
|
||||||
|
// Special case: Incinerator
|
||||||
|
if (citedPostId === INCINERATOR_ADDRESS && !this.posts.getVertex(INCINERATOR_ADDRESS)) {
|
||||||
|
this.posts.addVertex(INCINERATOR_ADDRESS, { name: 'Incinerator' }, 'Incinerator');
|
||||||
|
}
|
||||||
this.posts.addEdge(CITATION, post.id, citedPostId, weight);
|
this.posts.addEdge(CITATION, post.id, citedPostId, weight);
|
||||||
}
|
}
|
||||||
return post;
|
return post;
|
||||||
|
@ -171,22 +176,44 @@ export class Forum extends ReputationHolder {
|
||||||
for (const citationEdge of citationEdges) {
|
for (const citationEdge of citationEdges) {
|
||||||
const { weight } = citationEdge;
|
const { weight } = citationEdge;
|
||||||
let outboundAmount = weight * increment;
|
let outboundAmount = weight * increment;
|
||||||
const balanceToOutbound = this.posts.getEdgeWeight(BALANCE, citationEdge.from, citationEdge.to) ?? 0;
|
|
||||||
// We need to ensure that we at most undo the prior effects of this post
|
|
||||||
if (initialNegative) {
|
|
||||||
outboundAmount = outboundAmount < 0
|
|
||||||
? Math.max(outboundAmount, -balanceToOutbound)
|
|
||||||
: Math.min(outboundAmount, -balanceToOutbound);
|
|
||||||
}
|
|
||||||
if (Math.abs(outboundAmount) > EPSILON) {
|
if (Math.abs(outboundAmount) > EPSILON) {
|
||||||
const refundFromOutbound = await this.propagateValue(citationEdge, {
|
const balanceToOutbound = this.posts.getEdgeWeight(BALANCE, citationEdge.from, citationEdge.to) ?? 0;
|
||||||
rewardsAccumulator,
|
let refundFromOutbound = 0;
|
||||||
increment: outboundAmount,
|
|
||||||
depth: depth + 1,
|
|
||||||
initialNegative: initialNegative || (depth === 0 && outboundAmount < 0),
|
|
||||||
});
|
|
||||||
|
|
||||||
outboundAmount -= refundFromOutbound;
|
// Special case: Incineration.
|
||||||
|
if (citationEdge.to.id === INCINERATOR_ADDRESS) {
|
||||||
|
// Only a positive amount may be incinerated! Otherwise the sink could be used as a source.
|
||||||
|
if (outboundAmount < 0) {
|
||||||
|
this.scene?.flowchart?.log(`style ${citationEdge.from.id} fill:#620000`);
|
||||||
|
throw new Error('Incinerator can only receive positive citations!');
|
||||||
|
}
|
||||||
|
// Reputation sent to the incinerator is burned! This means it is deducted from the sender,
|
||||||
|
// without increasing the value of any other token.
|
||||||
|
this.actions.propagate.log(citationEdge.from.data, { name: 'Incinerator' }, `(${increment})`);
|
||||||
|
} else {
|
||||||
|
// We need to ensure that we at most undo the prior effects of this post
|
||||||
|
if (initialNegative) {
|
||||||
|
outboundAmount = outboundAmount < 0
|
||||||
|
? Math.max(outboundAmount, -balanceToOutbound)
|
||||||
|
: Math.min(outboundAmount, -balanceToOutbound);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively propagate reputation effects
|
||||||
|
refundFromOutbound = await this.propagateValue(citationEdge, {
|
||||||
|
rewardsAccumulator,
|
||||||
|
increment: outboundAmount,
|
||||||
|
depth: depth + 1,
|
||||||
|
initialNegative: initialNegative || (depth === 0 && outboundAmount < 0),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Any excess (negative) amount that could not be propagated,
|
||||||
|
// i.e. because a cited post has been reduced to zero value,
|
||||||
|
// is retained by the citing post.
|
||||||
|
outboundAmount -= refundFromOutbound;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep a record of the effect of the reputation transferred along this edge in the graph,
|
||||||
|
// so that later, negative citations can be constrained to at most undo these effects.
|
||||||
this.posts.setEdgeWeight(BALANCE, citationEdge.from, citationEdge.to, balanceToOutbound + outboundAmount);
|
this.posts.setEdgeWeight(BALANCE, citationEdge.from, citationEdge.to, balanceToOutbound + outboundAmount);
|
||||||
totalOutboundAmount += outboundAmount;
|
totalOutboundAmount += outboundAmount;
|
||||||
|
|
||||||
|
|
|
@ -54,8 +54,7 @@ export class ValidationPool extends ReputationHolder {
|
||||||
|| [null, undefined].includes(duration)
|
|| [null, undefined].includes(duration)
|
||||||
) {
|
) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Duration must be in the range [${params.voteDuration.min}, ${
|
`Duration must be in the range [${params.voteDuration.min}, ${params.voteDuration.max ?? 'Inf'
|
||||||
params.voteDuration.max ?? 'Inf'
|
|
||||||
}]; got ${duration}`,
|
}]; got ${duration}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,9 @@
|
||||||
<li><a href="./tests/forum4.test.html">Redistribute power through subsequent support</a></li>
|
<li><a href="./tests/forum4.test.html">Redistribute power through subsequent support</a></li>
|
||||||
<li><a href="./tests/forum5.test.html">Destroy a post after it has received positive citations</a></li>
|
<li><a href="./tests/forum5.test.html">Destroy a post after it has received positive citations</a></li>
|
||||||
<li><a href="./tests/forum6.test.html">Initially zero-valued posts later receive citations</a></li>
|
<li><a href="./tests/forum6.test.html">Initially zero-valued posts later receive citations</a></li>
|
||||||
|
<li><a href="./tests/forum7.test.html">Negatively cite a zero-valued post</a></li>
|
||||||
|
<li><a href="./tests/forum8.test.html">Incinerate reputation</a></li>
|
||||||
|
<li><a href="./tests/forum9.test.html">Use incineration to achieve more balanced reweighting</a></li>
|
||||||
</ol>
|
</ol>
|
||||||
</ul>
|
</ul>
|
||||||
<ul>
|
<ul>
|
||||||
|
|
|
@ -33,6 +33,9 @@
|
||||||
<script type="module" src="./scripts/forum/forum4.test.js"></script>
|
<script type="module" src="./scripts/forum/forum4.test.js"></script>
|
||||||
<script type="module" src="./scripts/forum/forum5.test.js"></script>
|
<script type="module" src="./scripts/forum/forum5.test.js"></script>
|
||||||
<script type="module" src="./scripts/forum/forum6.test.js"></script>
|
<script type="module" src="./scripts/forum/forum6.test.js"></script>
|
||||||
|
<script type="module" src="./scripts/forum/forum7.test.js"></script>
|
||||||
|
<script type="module" src="./scripts/forum/forum8.test.js"></script>
|
||||||
|
<script type="module" src="./scripts/forum/forum9.test.js"></script>
|
||||||
<script defer class="mocha-init">
|
<script defer class="mocha-init">
|
||||||
mocha.setup({
|
mocha.setup({
|
||||||
ui: 'bdd',
|
ui: 'bdd',
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Forum test 7</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>
|
||||||
|
<h2><a href="../">DGF Tests</a></h2>
|
||||||
|
<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/forum/forum7.test.js"></script>
|
||||||
|
<script defer class="mocha-init">
|
||||||
|
mocha.setup({
|
||||||
|
ui: 'bdd',
|
||||||
|
});
|
||||||
|
chai.should();
|
||||||
|
</script>
|
|
@ -0,0 +1,26 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Forum test 8</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>
|
||||||
|
<h2><a href="../">DGF Tests</a></h2>
|
||||||
|
<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/forum/forum8.test.js"></script>
|
||||||
|
<script defer class="mocha-init">
|
||||||
|
mocha.setup({
|
||||||
|
ui: 'bdd',
|
||||||
|
});
|
||||||
|
chai.should();
|
||||||
|
</script>
|
|
@ -0,0 +1,26 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Forum test 9</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>
|
||||||
|
<h2><a href="../">DGF Tests</a></h2>
|
||||||
|
<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/forum/forum9.test.js"></script>
|
||||||
|
<script defer class="mocha-init">
|
||||||
|
mocha.setup({
|
||||||
|
ui: 'bdd',
|
||||||
|
});
|
||||||
|
chai.should();
|
||||||
|
</script>
|
|
@ -19,9 +19,9 @@ describe('Forum', function tests() {
|
||||||
|
|
||||||
it('Post2', async () => {
|
it('Post2', async () => {
|
||||||
const { forum, experts, posts } = forumTest;
|
const { forum, experts, posts } = forumTest;
|
||||||
await forumTest.addPost(experts[0], 10);
|
await forumTest.addPost(experts[0], 0);
|
||||||
forum.getPost(posts[0]).value.should.equal(10);
|
forum.getPost(posts[0]).value.should.equal(10);
|
||||||
forum.getPost(posts[1]).value.should.equal(10);
|
forum.getPost(posts[1]).value.should.equal(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Post3 cites Post2 and negatively cites Post1', async () => {
|
it('Post3 cites Post2 and negatively cites Post1', async () => {
|
||||||
|
@ -31,7 +31,7 @@ describe('Forum', function tests() {
|
||||||
{ postId: posts[1], weight: 1 },
|
{ postId: posts[1], weight: 1 },
|
||||||
]);
|
]);
|
||||||
forum.getPost(posts[0]).value.should.equal(0);
|
forum.getPost(posts[0]).value.should.equal(0);
|
||||||
forum.getPost(posts[1]).value.should.equal(30);
|
forum.getPost(posts[1]).value.should.equal(20);
|
||||||
forum.getPost(posts[2]).value.should.equal(0);
|
forum.getPost(posts[2]).value.should.equal(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,7 +10,7 @@ describe('Forum', function tests() {
|
||||||
await forumTest.setup();
|
await forumTest.setup();
|
||||||
});
|
});
|
||||||
|
|
||||||
context('Initially zero-valued post later receives citations', async () => {
|
context('Negatively citing a zero-valued post', async () => {
|
||||||
let forum;
|
let forum;
|
||||||
let experts;
|
let experts;
|
||||||
let posts;
|
let posts;
|
||||||
|
@ -66,11 +66,4 @@ describe('Forum', function tests() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 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 }]);
|
|
||||||
|
|
||||||
mochaRun();
|
mochaRun();
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
import { mochaRun } from '../../../util.js';
|
||||||
|
import { ForumTest } from './forum.test-util.js';
|
||||||
|
|
||||||
|
describe('Forum', function tests() {
|
||||||
|
this.timeout(0);
|
||||||
|
|
||||||
|
const forumTest = new ForumTest();
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
await forumTest.setup();
|
||||||
|
});
|
||||||
|
|
||||||
|
context('Initially zero-valued post later receives citations', async () => {
|
||||||
|
let forum;
|
||||||
|
let experts;
|
||||||
|
let posts;
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
forum = forumTest.forum;
|
||||||
|
experts = forumTest.experts;
|
||||||
|
posts = forumTest.posts;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Post1 has zero value', async () => {
|
||||||
|
await forumTest.addPost(experts[0], 0);
|
||||||
|
forum.getPost(posts[0]).value.should.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Post2 negatively cites Post1', async () => {
|
||||||
|
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(10);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
mochaRun();
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { INCINERATOR_ADDRESS, mochaRun } from '../../../util.js';
|
||||||
|
import { ForumTest } from './forum.test-util.js';
|
||||||
|
|
||||||
|
describe('Forum', function tests() {
|
||||||
|
this.timeout(0);
|
||||||
|
|
||||||
|
const forumTest = new ForumTest();
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
await forumTest.setup();
|
||||||
|
});
|
||||||
|
|
||||||
|
context('Incinerate reputation', async () => {
|
||||||
|
let forum;
|
||||||
|
let experts;
|
||||||
|
let posts;
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
forum = forumTest.forum;
|
||||||
|
experts = forumTest.experts;
|
||||||
|
posts = forumTest.posts;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Post1', async () => {
|
||||||
|
await forumTest.addPost(experts[0], 10);
|
||||||
|
forum.getPost(posts[0]).value.should.equal(10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Post2 burns reputation from Post1', async () => {
|
||||||
|
await forumTest.addPost(experts[0], 10, [
|
||||||
|
{ postId: posts[0], weight: -1 },
|
||||||
|
{ postId: INCINERATOR_ADDRESS, weight: 1 },
|
||||||
|
]);
|
||||||
|
forum.getPost(posts[0]).value.should.equal(0);
|
||||||
|
forum.getPost(posts[1]).value.should.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Reputation can not be sourced from the incinerator', async () => {
|
||||||
|
try {
|
||||||
|
await forumTest.addPost(experts[0], 10, [
|
||||||
|
{ postId: INCINERATOR_ADDRESS, weight: -1 },
|
||||||
|
]);
|
||||||
|
} catch (e) {
|
||||||
|
e.message.should.match(/Incinerator can only receive positive citations/);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
mochaRun();
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { INCINERATOR_ADDRESS, mochaRun } from '../../../util.js';
|
||||||
|
import { ForumTest } from './forum.test-util.js';
|
||||||
|
|
||||||
|
describe('Forum', function tests() {
|
||||||
|
this.timeout(0);
|
||||||
|
|
||||||
|
const forumTest = new ForumTest();
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
await forumTest.setup();
|
||||||
|
});
|
||||||
|
|
||||||
|
context('Use incineration to achieve more balanced reweighting', async () => {
|
||||||
|
let forum;
|
||||||
|
let experts;
|
||||||
|
let posts;
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
forum = forumTest.forum;
|
||||||
|
experts = forumTest.experts;
|
||||||
|
posts = forumTest.posts;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Post1', async () => {
|
||||||
|
await forumTest.addPost(experts[0], 10);
|
||||||
|
forum.getPost(posts[0]).value.should.equal(10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Post2', async () => {
|
||||||
|
await forumTest.addPost(experts[0], 0);
|
||||||
|
forum.getPost(posts[0]).value.should.equal(10);
|
||||||
|
forum.getPost(posts[1]).value.should.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Post3 transfers reputation from Post1 to Post2', async () => {
|
||||||
|
await forumTest.addPost(experts[0], 10, [
|
||||||
|
{ postId: posts[0], weight: -1 },
|
||||||
|
{ postId: posts[1], weight: 0.5 },
|
||||||
|
{ postId: INCINERATOR_ADDRESS, weight: 0.5 },
|
||||||
|
]);
|
||||||
|
forum.getPost(posts[0]).value.should.equal(0);
|
||||||
|
forum.getPost(posts[1]).value.should.equal(10);
|
||||||
|
forum.getPost(posts[2]).value.should.equal(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
mochaRun();
|
|
@ -4,6 +4,8 @@ const timers = new Map();
|
||||||
|
|
||||||
export const EPSILON = 2.23e-16;
|
export const EPSILON = 2.23e-16;
|
||||||
|
|
||||||
|
export const INCINERATOR_ADDRESS = 0;
|
||||||
|
|
||||||
export const debounce = async (fn, delayMs) => {
|
export const debounce = async (fn, delayMs) => {
|
||||||
const timer = timers.get(fn);
|
const timer = timers.get(fn);
|
||||||
if (timer) {
|
if (timer) {
|
||||||
|
|
Loading…
Reference in New Issue