Minor refactoring
This commit is contained in:
parent
e20a864738
commit
f475742cbf
|
@ -30,4 +30,18 @@ module.exports = {
|
||||||
'no-constant-condition': ['off'],
|
'no-constant-condition': ['off'],
|
||||||
'no-await-in-loop': ['off'],
|
'no-await-in-loop': ['off'],
|
||||||
},
|
},
|
||||||
|
globals: {
|
||||||
|
_: 'readonly',
|
||||||
|
chai: 'readonly',
|
||||||
|
expect: 'readonly',
|
||||||
|
mocha: 'readonly',
|
||||||
|
describe: 'readonly',
|
||||||
|
context: 'readonly',
|
||||||
|
it: 'readonly',
|
||||||
|
specify: 'readonly',
|
||||||
|
before: 'readonly',
|
||||||
|
after: 'readonly',
|
||||||
|
beforeEach: 'readonly',
|
||||||
|
afterEach: 'readonly',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Graph } from './graph.js';
|
import { WDAG } from './wdag.js';
|
||||||
|
|
||||||
class Author {
|
class Author {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -21,7 +21,7 @@ class PostVertex {
|
||||||
export class ForumView {
|
export class ForumView {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.reputations = new Map();
|
this.reputations = new Map();
|
||||||
this.posts = new Graph();
|
this.posts = new WDAG();
|
||||||
this.authors = new Map();
|
this.authors = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,8 +53,8 @@ export class ForumView {
|
||||||
const postVertex = new PostVertex(postId, author, stake, content, citations);
|
const postVertex = new PostVertex(postId, author, stake, content, citations);
|
||||||
console.log('addPost', { id: postId, postContent });
|
console.log('addPost', { id: postId, postContent });
|
||||||
this.posts.addVertex(postId, postVertex);
|
this.posts.addVertex(postId, postVertex);
|
||||||
for (const citation of citations) {
|
for (const { postId: citedPostId, weight } of citations) {
|
||||||
this.posts.addEdge('citation', postId, citation.postId, citation);
|
this.posts.addEdge('citation', postId, citedPostId, weight);
|
||||||
}
|
}
|
||||||
this.applyNonbindingReputationEffects(postVertex);
|
this.applyNonbindingReputationEffects(postVertex);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
import { Actor } from './actor.js';
|
import { Actor } from './actor.js';
|
||||||
import { Graph } from './graph.js';
|
import { WDAG } from './wdag.js';
|
||||||
import { Action } from './action.js';
|
import { Action } from './action.js';
|
||||||
import { CryptoUtil } from './crypto.js';
|
import { CryptoUtil } from './crypto.js';
|
||||||
import params from '../params.js';
|
import params from '../params.js';
|
||||||
import { ReputationHolder } from './reputation-holder.js';
|
import { ReputationHolder } from './reputation-holder.js';
|
||||||
import { displayNumber } from '../util.js';
|
import { displayNumber } from '../util.js';
|
||||||
|
|
||||||
|
const CITATION = 'citation';
|
||||||
|
const BALANCE = 'balance';
|
||||||
|
|
||||||
class Post extends Actor {
|
class Post extends Actor {
|
||||||
constructor(forum, authorPublicKey, postContent) {
|
constructor(forum, authorPublicKey, postContent) {
|
||||||
const index = forum.posts.countVertices();
|
const index = forum.posts.countVertices();
|
||||||
const name = `Post${index + 1}`;
|
const name = `Post${index + 1}`;
|
||||||
super(name, forum.scene);
|
super(name, forum.scene);
|
||||||
this.id = postContent.id ?? `post_${CryptoUtil.randomUUID()}`;
|
this.id = postContent.id ?? `post_${CryptoUtil.randomUUID().slice(0, 4)}`;
|
||||||
this.authorPublicKey = authorPublicKey;
|
this.authorPublicKey = authorPublicKey;
|
||||||
this.value = 0;
|
this.value = 0;
|
||||||
this.initialValue = 0;
|
this.initialValue = 0;
|
||||||
|
@ -47,7 +50,7 @@ export class Forum extends ReputationHolder {
|
||||||
constructor(name, scene) {
|
constructor(name, scene) {
|
||||||
super(`forum_${CryptoUtil.randomUUID()}`, name, scene);
|
super(`forum_${CryptoUtil.randomUUID()}`, name, scene);
|
||||||
this.id = this.reputationPublicKey;
|
this.id = this.reputationPublicKey;
|
||||||
this.posts = new Graph(scene);
|
this.posts = new WDAG(scene);
|
||||||
this.actions = {
|
this.actions = {
|
||||||
addPost: new Action('add post', scene),
|
addPost: new Action('add post', scene),
|
||||||
propagateValue: new Action('propagate value', this.scene),
|
propagateValue: new Action('propagate value', this.scene),
|
||||||
|
@ -60,7 +63,7 @@ export class Forum extends ReputationHolder {
|
||||||
await this.actions.addPost.log(this, post);
|
await this.actions.addPost.log(this, post);
|
||||||
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) {
|
||||||
this.posts.addEdge('citation', post.id, citedPostId, { weight });
|
this.posts.addEdge(CITATION, post.id, citedPostId, weight);
|
||||||
}
|
}
|
||||||
return post.id;
|
return post.id;
|
||||||
}
|
}
|
||||||
|
@ -88,7 +91,8 @@ export class Forum extends ReputationHolder {
|
||||||
}) {
|
}) {
|
||||||
this.activate();
|
this.activate();
|
||||||
const initialValue = bench.reputation.valueOf(tokenId);
|
const initialValue = bench.reputation.valueOf(tokenId);
|
||||||
const post = this.getPost(postId);
|
const postVertex = this.posts.getVertex(postId);
|
||||||
|
const post = postVertex.data;
|
||||||
post.setStatus('Validated');
|
post.setStatus('Validated');
|
||||||
post.initialValue = initialValue;
|
post.initialValue = initialValue;
|
||||||
this.posts.setVertexLabel(post.id, post.getLabel());
|
this.posts.setVertexLabel(post.id, post.getLabel());
|
||||||
|
@ -97,9 +101,13 @@ export class Forum extends ReputationHolder {
|
||||||
// so that its value can be updated by future validated posts.
|
// so that its value can be updated by future validated posts.
|
||||||
post.tokenId = tokenId;
|
post.tokenId = tokenId;
|
||||||
|
|
||||||
// Compute rewards
|
|
||||||
const rewardsAccumulator = new Map();
|
const rewardsAccumulator = new Map();
|
||||||
await this.propagateValue(rewardsAccumulator, this, post, initialValue);
|
|
||||||
|
// Compute rewards
|
||||||
|
await this.propagateValue(
|
||||||
|
{ to: postVertex, from: { data: this } },
|
||||||
|
{ rewardsAccumulator, increment: initialValue },
|
||||||
|
);
|
||||||
|
|
||||||
// Apply computed rewards to update values of tokens
|
// Apply computed rewards to update values of tokens
|
||||||
for (const [id, value] of rewardsAccumulator) {
|
for (const [id, value] of rewardsAccumulator) {
|
||||||
|
@ -118,46 +126,62 @@ export class Forum extends ReputationHolder {
|
||||||
this.deactivate();
|
this.deactivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
async propagateValue(rewardsAccumulator, fromActor, post, increment, depth = 0) {
|
/**
|
||||||
this.actions.propagateValue.log(fromActor, post, `(${increment})`);
|
* @param {Edge} edge
|
||||||
|
* @param {Object} opaqueData
|
||||||
|
*/
|
||||||
|
async propagateValue(edge, { rewardsAccumulator, increment, depth = 0 }) {
|
||||||
|
const postVertex = edge.to;
|
||||||
|
const post = postVertex?.data;
|
||||||
|
|
||||||
|
this.actions.propagateValue.log(edge.from.data, post, `(${increment})`);
|
||||||
|
|
||||||
|
console.log('propagateValue start', {
|
||||||
|
from: edge.from.id,
|
||||||
|
to: edge.to.id,
|
||||||
|
depth,
|
||||||
|
value: post.value,
|
||||||
|
increment,
|
||||||
|
});
|
||||||
|
|
||||||
// Recursively distribute reputation to citations, according to weights
|
|
||||||
let totalOutboundAmount = 0;
|
|
||||||
if (params.referenceChainLimit === null || depth <= params.referenceChainLimit) {
|
if (params.referenceChainLimit === null || depth <= params.referenceChainLimit) {
|
||||||
for (const { postId: citedPostId, weight } of post.citations) {
|
for (const citationEdge of this.posts.getEdges(CITATION, post)) {
|
||||||
const citedPost = this.getPost(citedPostId);
|
const { to: citedPostVertex, data: { weight } } = citationEdge;
|
||||||
|
const citedPost = citedPostVertex.data;
|
||||||
let outboundAmount = weight * increment;
|
let outboundAmount = weight * increment;
|
||||||
// If this is a negative citation, it must not bring the target below zero value.
|
const balance = this.posts.getEdge(BALANCE, postVertex, citedPostVertex)?.data || 0;
|
||||||
|
console.log('Citation', {
|
||||||
|
citationEdge, outboundAmount, balance, citedPostValue: citedPost.value,
|
||||||
|
});
|
||||||
|
// We need to ensure that we propagate no more reputation than we leached
|
||||||
if (outboundAmount < 0) {
|
if (outboundAmount < 0) {
|
||||||
const citedPostTotalCitationWeight = citedPost.citations.reduce((t, { weight: w }) => t += w, 0);
|
outboundAmount = Math.max(outboundAmount, -citedPost.value);
|
||||||
const citedPostCapacity = citedPost.value / (1 - citedPostTotalCitationWeight);
|
if (depth > 0) {
|
||||||
outboundAmount = Math.max(outboundAmount, -citedPostCapacity);
|
outboundAmount = Math.max(outboundAmount, -balance);
|
||||||
}
|
}
|
||||||
const refundFromOutbound = await this.propagateValue(
|
}
|
||||||
|
increment -= outboundAmount * params.leachingValue;
|
||||||
|
this.posts.setEdge(BALANCE, postVertex, citedPostVertex, balance + outboundAmount);
|
||||||
|
await this.propagateValue(citationEdge, {
|
||||||
rewardsAccumulator,
|
rewardsAccumulator,
|
||||||
post,
|
increment: outboundAmount,
|
||||||
citedPost,
|
depth: depth + 1,
|
||||||
outboundAmount,
|
});
|
||||||
depth + 1,
|
|
||||||
);
|
|
||||||
totalOutboundAmount += outboundAmount - refundFromOutbound;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply leaching value
|
const newValue = post.value + increment;
|
||||||
const incrementAfterLeaching = increment - totalOutboundAmount * params.leachingValue;
|
|
||||||
|
|
||||||
const rawNewValue = post.value + incrementAfterLeaching;
|
console.log('propagateValue end', {
|
||||||
const newValue = Math.max(0, rawNewValue);
|
depth,
|
||||||
const appliedIncrement = newValue - post.value;
|
increment,
|
||||||
const refundToInbound = increment - appliedIncrement;
|
newValue,
|
||||||
|
});
|
||||||
|
|
||||||
// Award reputation to post author
|
// Award reputation to post author
|
||||||
rewardsAccumulator.set(post.tokenId, appliedIncrement);
|
rewardsAccumulator.set(post.tokenId, increment);
|
||||||
|
|
||||||
// Increment the value of the post
|
// Increment the value of the post
|
||||||
await this.setPostValue(post, newValue);
|
await this.setPostValue(post, newValue);
|
||||||
|
|
||||||
return refundToInbound;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
export class List {
|
||||||
|
constructor() {
|
||||||
|
this.items = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
add(item) {
|
||||||
|
this.items.push(item);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ import mermaid from 'https://unpkg.com/mermaid@9.2.2/dist/mermaid.esm.min.mjs';
|
||||||
import { Actor } from './actor.js';
|
import { Actor } from './actor.js';
|
||||||
import { Action } from './action.js';
|
import { Action } from './action.js';
|
||||||
import { debounce, hexToRGB } from '../util.js';
|
import { debounce, hexToRGB } from '../util.js';
|
||||||
|
import { CryptoUtil } from './crypto.js';
|
||||||
|
|
||||||
class MermaidDiagram {
|
class MermaidDiagram {
|
||||||
constructor(box, logBox) {
|
constructor(box, logBox) {
|
||||||
|
@ -90,10 +91,11 @@ export class Scene {
|
||||||
this.box.addBox('Spacer').setInnerHTML(' ');
|
this.box.addBox('Spacer').setInnerHTML(' ');
|
||||||
this.topSection = this.box.addBox('Top section').flex();
|
this.topSection = this.box.addBox('Top section').flex();
|
||||||
this.displayValuesBox = this.topSection.addBox('Values');
|
this.displayValuesBox = this.topSection.addBox('Values');
|
||||||
this.middleSection = this.box.addBox('Middle section').flex();
|
this.middleSection = this.box.addBox('Middle section');
|
||||||
this.box.addBox('Spacer').setInnerHTML(' ');
|
this.box.addBox('Spacer').setInnerHTML(' ');
|
||||||
this.actors = new Set();
|
this.actors = new Set();
|
||||||
this.dateStart = new Date();
|
this.dateStart = new Date();
|
||||||
|
this.flowcharts = new Map();
|
||||||
|
|
||||||
mermaid.mermaidAPI.initialize({
|
mermaid.mermaidAPI.initialize({
|
||||||
startOnLoad: false,
|
startOnLoad: false,
|
||||||
|
@ -117,26 +119,49 @@ export class Scene {
|
||||||
withSequenceDiagram() {
|
withSequenceDiagram() {
|
||||||
const box = this.box.addBox('Sequence diagram');
|
const box = this.box.addBox('Sequence diagram');
|
||||||
this.box.addBox('Spacer').setInnerHTML(' ');
|
this.box.addBox('Spacer').setInnerHTML(' ');
|
||||||
const logBox = this.box.addBox('Sequence diagram text');
|
const logBox = this.box.addBox('Sequence diagram text').addClass('dim');
|
||||||
this.sequence = new MermaidDiagram(box, logBox);
|
this.sequence = new MermaidDiagram(box, logBox);
|
||||||
this.sequence.log('sequenceDiagram', false);
|
this.sequence.log('sequenceDiagram', false);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
withFlowchart(direction = 'BT') {
|
withFlowchart({ direction = 'BT' } = {}) {
|
||||||
const box = this.topSection.addBox('Flowchart');
|
const box = this.topSection.addBox('Flowchart').addClass('padded');
|
||||||
this.box.addBox('Spacer').setInnerHTML(' ');
|
const logBox = this.topSection.addBox('Flowchart text').addClass('dim');
|
||||||
const logBox = this.box.addBox('Flowchart text');
|
|
||||||
this.flowchart = new MermaidDiagram(box, logBox);
|
this.flowchart = new MermaidDiagram(box, logBox);
|
||||||
this.flowchart.log(`graph ${direction}`, false);
|
this.flowchart.log(`graph ${direction}`, false);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
withAdditionalFlowchart({ id, name, direction = 'BT' } = {}) {
|
||||||
|
const index = this.flowcharts.size;
|
||||||
|
name = name ?? `Flowchart ${index}`;
|
||||||
|
id = id ?? `flowchart_${CryptoUtil.randomUUID().slice(0, 4)}`;
|
||||||
|
const container = this.middleSection.addBox(name).flex();
|
||||||
|
const box = container.addBox('Flowchart').addClass('padded');
|
||||||
|
const logBox = container.addBox('Flowchart text').addClass('dim');
|
||||||
|
const flowchart = new MermaidDiagram(box, logBox);
|
||||||
|
flowchart.log(`graph ${direction}`, false);
|
||||||
|
this.flowcharts.set(id, flowchart);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastFlowchart() {
|
||||||
|
if (!this.flowcharts.size) {
|
||||||
|
if (this.flowchart) {
|
||||||
|
return this.flowchart;
|
||||||
|
}
|
||||||
|
throw new Error('lastFlowchart: No additional flowcharts have been added.');
|
||||||
|
}
|
||||||
|
const flowcharts = Array.from(this.flowcharts.values());
|
||||||
|
return flowcharts[flowcharts.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
withTable() {
|
withTable() {
|
||||||
if (this.table) {
|
if (this.table) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
const box = this.middleSection.addBox('Table');
|
const box = this.middleSection.addBox('Table').addClass('padded');
|
||||||
this.box.addBox('Spacer').setInnerHTML(' ');
|
this.box.addBox('Spacer').setInnerHTML(' ');
|
||||||
this.table = new Table(box);
|
this.table = new Table(box);
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -16,20 +16,27 @@ class Vertex {
|
||||||
}
|
}
|
||||||
|
|
||||||
class Edge {
|
class Edge {
|
||||||
constructor(label, from, to, data) {
|
constructor(label, from, to, weight) {
|
||||||
this.from = from;
|
this.from = from;
|
||||||
this.to = to;
|
this.to = to;
|
||||||
this.label = label;
|
this.label = label;
|
||||||
this.data = data;
|
this.weight = weight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Graph {
|
export class WDAG {
|
||||||
constructor(scene) {
|
constructor(scene) {
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
this.vertices = new Map();
|
this.vertices = new Map();
|
||||||
this.edgeLabels = new Map();
|
this.edgeLabels = new Map();
|
||||||
this.nextVertexId = 0;
|
this.nextVertexId = 0;
|
||||||
|
this.flowchart = scene?.flowchart ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
withFlowchart() {
|
||||||
|
this.scene.withAdditionalFlowchart();
|
||||||
|
this.flowchart = this.scene.lastFlowchart();
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
addVertex(id, data, label) {
|
addVertex(id, data, label) {
|
||||||
|
@ -43,16 +50,12 @@ export class Graph {
|
||||||
}
|
}
|
||||||
const vertex = new Vertex(id, data);
|
const vertex = new Vertex(id, data);
|
||||||
this.vertices.set(id, vertex);
|
this.vertices.set(id, vertex);
|
||||||
if (this.scene && this.scene.flowchart) {
|
this.flowchart?.log(`${id}[${label ?? id}]`);
|
||||||
this.scene.flowchart.log(`${id}[${label ?? id}]`);
|
|
||||||
}
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setVertexLabel(id, label) {
|
setVertexLabel(id, label) {
|
||||||
if (this.scene && this.scene.flowchart) {
|
this.flowchart?.log(`${id}[${label}]`);
|
||||||
this.scene.flowchart.log(`${id}[${label}]`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getVertex(id) {
|
getVertex(id) {
|
||||||
|
@ -68,36 +71,43 @@ export class Graph {
|
||||||
}
|
}
|
||||||
|
|
||||||
getEdge(label, from, to) {
|
getEdge(label, from, to) {
|
||||||
|
from = from instanceof Vertex ? from : this.getVertex(from);
|
||||||
|
to = to instanceof Vertex ? to : this.getVertex(to);
|
||||||
const edges = this.edgeLabels.get(label);
|
const edges = this.edgeLabels.get(label);
|
||||||
return edges?.get(JSON.stringify({ from, to }));
|
return edges?.get(JSON.stringify([from.id, to.id]));
|
||||||
}
|
}
|
||||||
|
|
||||||
setEdge(label, from, to, edge) {
|
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);
|
||||||
|
}
|
||||||
let edges = this.edgeLabels.get(label);
|
let edges = this.edgeLabels.get(label);
|
||||||
if (!edges) {
|
if (!edges) {
|
||||||
edges = new Map();
|
edges = new Map();
|
||||||
this.edgeLabels.set(label, edges);
|
this.edgeLabels.set(label, edges);
|
||||||
}
|
}
|
||||||
edges.set(JSON.stringify({ from, to }), edge);
|
edges.set(JSON.stringify([from.id, to.id]), edge);
|
||||||
}
|
}
|
||||||
|
|
||||||
addEdge(label, from, to, data) {
|
addEdge(label, from, to, weight) {
|
||||||
|
from = from instanceof Vertex ? from : this.getVertex(from);
|
||||||
|
to = to instanceof Vertex ? to : this.getVertex(to);
|
||||||
if (this.getEdge(label, from, to)) {
|
if (this.getEdge(label, from, to)) {
|
||||||
throw new Error(`Edge ${label} from ${from} to ${to} already exists`);
|
throw new Error(`Edge ${label} from ${from} to ${to} already exists`);
|
||||||
}
|
}
|
||||||
const edge = new Edge(label, from, to, data);
|
const edge = new Edge(label, from, to, weight);
|
||||||
this.setEdge(label, from, to, edge);
|
this.setEdge(label, from, to, edge);
|
||||||
const fromVertex = this.getVertex(from);
|
from.edges.from.push(edge);
|
||||||
fromVertex.edges.from.push(edge);
|
to.edges.to.push(edge);
|
||||||
const toVertex = this.getVertex(to);
|
this.flowchart?.log(`${from.id} -- ${weight} --> ${to.id}`);
|
||||||
toVertex.edges.to.push(edge);
|
|
||||||
if (this.scene && this.scene.flowchart) {
|
|
||||||
this.scene.flowchart.log(`${fromVertex.id} -- ${data.weight} --> ${toVertex.id}`);
|
|
||||||
}
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
getEdges(label, from, to) {
|
getEdges(label, from, to) {
|
||||||
|
from = from instanceof Vertex ? from : this.getVertex(from);
|
||||||
|
to = to instanceof Vertex ? to : this.getVertex(to);
|
||||||
const edgeLabels = label ? [label] : Array.from(this.edgeLabels.keys());
|
const edgeLabels = label ? [label] : Array.from(this.edgeLabels.keys());
|
||||||
return edgeLabels.flatMap((edgeLabel) => {
|
return edgeLabels.flatMap((edgeLabel) => {
|
||||||
const edges = this.edgeLabels.get(edgeLabel);
|
const edges = this.edgeLabels.get(edgeLabel);
|
|
@ -29,6 +29,12 @@ a:visited {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
font-size: 8pt;
|
font-size: 8pt;
|
||||||
}
|
}
|
||||||
|
.dim {
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
.padded {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
svg {
|
svg {
|
||||||
width: 800px;
|
width: 800px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,19 +7,19 @@
|
||||||
<h2>Tests</h2>
|
<h2>Tests</h2>
|
||||||
<h3>Primary</h3>
|
<h3>Primary</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="./tests/validation-pool.html">Validation Pool</a></li>
|
<li><a href="./tests/validation-pool.test.html">Validation Pool</a></li>
|
||||||
<li><a href="./tests/availability.html">Availability + Business</a></li>
|
<li><a href="./tests/availability.test.html">Availability + Business</a></li>
|
||||||
<li><a href="./tests/forum.html">Forum</a></li>
|
<li><a href="./tests/forum.test.html">Forum</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<h3>Secondary</h3>
|
<h3>Secondary</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="./tests/forum-network.html">Forum Network</a></li>
|
<li><a href="./tests/forum-network.test.html">Forum Network</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<h3>Tertiary</h3>
|
<h3>Tertiary</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="./tests/basic.html">Basic</a></li>
|
<li><a href="./tests/basic.test.html">Basic Sequencing</a></li>
|
||||||
<li><a href="./tests/graph.html">Graph</a></li>
|
<li><a href="./tests/wdag.test.html">WDAG</a></li>
|
||||||
<li><a href="./tests/debounce.html">Debounce</a></li>
|
<li><a href="./tests/debounce.test.html">Debounce</a></li>
|
||||||
<li><a href="./tests/flowchart.html">Flowchart</a></li>
|
<li><a href="./tests/flowchart.test.html">Flowchart</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<head>
|
|
||||||
<title>Forum test</title>
|
|
||||||
<link type="text/css" rel="stylesheet" href="../index.css" />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="forum-test"></div>
|
|
||||||
</body>
|
|
||||||
<script type="module" src="./forum/forum.test.js"></script>
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<head>
|
||||||
|
<title>Forum test</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/chai/chai.js"></script>
|
||||||
|
<script src="https://unpkg.com/mocha/mocha.js"></script>
|
||||||
|
<script class="mocha-init">
|
||||||
|
mocha.setup({
|
||||||
|
ui: 'bdd',
|
||||||
|
globals: ['scene', 'bench', 'forum', 'experts', 'posts', '__REACT_DEVTOOLS_*'],
|
||||||
|
});
|
||||||
|
mocha.checkLeaks();
|
||||||
|
chai.should();
|
||||||
|
</script>
|
||||||
|
<script type="module" src="./scripts/forum.test.js"></script>
|
||||||
|
<script class="mocha-exec">
|
||||||
|
mocha.run();
|
||||||
|
</script>
|
|
@ -1,79 +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_INTERVAL = 500;
|
|
||||||
|
|
||||||
const rootElement = document.getElementById('forum-test');
|
|
||||||
const rootBox = new Box('rootBox', rootElement).flex();
|
|
||||||
|
|
||||||
const 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(' ');
|
|
||||||
|
|
||||||
const experts = (window.experts = []);
|
|
||||||
const newExpert = async () => {
|
|
||||||
const index = experts.length;
|
|
||||||
const name = `Expert${index + 1}`;
|
|
||||||
const expert = await new Expert(name, scene).initialize();
|
|
||||||
experts.push(expert);
|
|
||||||
return expert;
|
|
||||||
};
|
|
||||||
|
|
||||||
const forum = (window.forum = new Forum('Forum', scene));
|
|
||||||
const bench = (window.bench = new Bench(forum, 'Bench', scene));
|
|
||||||
const expert1 = await newExpert();
|
|
||||||
const expert2 = await newExpert();
|
|
||||||
const expert3 = 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
const addPost = async (author, title, fee, citations = []) => {
|
|
||||||
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: 1000,
|
|
||||||
tokenLossRatio: 1,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
await delay(1000);
|
|
||||||
await pool.evaluateWinningConditions();
|
|
||||||
await scene.endSection();
|
|
||||||
await delay(DEFAULT_DELAY_INTERVAL);
|
|
||||||
return postId;
|
|
||||||
};
|
|
||||||
|
|
||||||
const postId1 = await addPost(expert1, 'Post 1', 20);
|
|
||||||
const postId2 = await addPost(expert2, 'Post 2', 10, [{ postId: postId1, weight: 0.5 }]);
|
|
||||||
const postId3 = await addPost(expert3, 'Post 3', 10, [{ postId: postId1, weight: -1 }]);
|
|
||||||
await addPost(expert3, 'Post 4', 100, [{ postId: postId2, weight: -1 }]);
|
|
||||||
await addPost(expert1, 'Post 5', 100, [{ postId: postId3, weight: -1 }]);
|
|
|
@ -1,33 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<head>
|
|
||||||
<title>Forum Graph</title>
|
|
||||||
<link type="text/css" rel="stylesheet" href="../index.css" />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="graph-test"></div>
|
|
||||||
</body>
|
|
||||||
<script type="module">
|
|
||||||
import { Box } from '../classes/box.js';
|
|
||||||
import { Scene } from '../classes/scene.js';
|
|
||||||
import { Graph } from '../classes/graph.js';
|
|
||||||
|
|
||||||
const rootElement = document.getElementById('graph-test');
|
|
||||||
const rootBox = new Box('rootBox', rootElement).flex();
|
|
||||||
|
|
||||||
window.scene = new Scene('Graph test', rootBox);
|
|
||||||
|
|
||||||
window.graph = new Graph();
|
|
||||||
|
|
||||||
window.v = [];
|
|
||||||
function addVertex() {
|
|
||||||
const vertex = window.graph.addVertex({ seq: window.v.length });
|
|
||||||
window.v.push(vertex);
|
|
||||||
}
|
|
||||||
addVertex();
|
|
||||||
addVertex();
|
|
||||||
addVertex();
|
|
||||||
addVertex();
|
|
||||||
addVertex();
|
|
||||||
|
|
||||||
window.graph.addEdge('e1', 0, 1);
|
|
||||||
</script>
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Mocha Tests</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/mocha/mocha.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="mocha"></div>
|
||||||
|
<div id="scene"></div>
|
||||||
|
|
||||||
|
<script src="https://unpkg.com/chai/chai.js"></script>
|
||||||
|
<script src="https://unpkg.com/mocha/mocha.js"></script>
|
||||||
|
|
||||||
|
<script class="mocha-init">
|
||||||
|
mocha.setup({
|
||||||
|
ui: 'bdd',
|
||||||
|
});
|
||||||
|
mocha.checkLeaks();
|
||||||
|
chai.should();
|
||||||
|
</script>
|
||||||
|
<script src="./scripts/mocha.test.js"></script>
|
||||||
|
<script class="mocha-exec">
|
||||||
|
mocha.run();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -9,9 +9,9 @@
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import { Box } from '../classes/box.js';
|
import { Box } from '../classes/box.js';
|
||||||
import { Scene } from '../classes/scene.js';
|
import { Scene } from '../classes/scene.js';
|
||||||
import { ValidationPool } from '../classes/validation-pool.js';
|
// import { ValidationPool } from '../classes/validation-pool.js';
|
||||||
import { TokenHolder } from '../classes/token-holder.js';
|
// import { TokenHolder } from '../classes/token-holder.js';
|
||||||
import { ReputationToken } from '../classes/reputation-token.js';
|
// import { ReputationToken } from '../classes/reputation-token.js';
|
||||||
import { delay } from '../util.js';
|
import { delay } from '../util.js';
|
||||||
|
|
||||||
const DEFAULT_DELAY_INTERVAL = 500;
|
const DEFAULT_DELAY_INTERVAL = 500;
|
||||||
|
@ -23,8 +23,8 @@ const scene = (window.scene = new Scene('Forum test', rootBox));
|
||||||
scene.withSequenceDiagram();
|
scene.withSequenceDiagram();
|
||||||
scene.withFlowchart();
|
scene.withFlowchart();
|
||||||
|
|
||||||
const pool = new ValidationPool();
|
// const pool = new ValidationPool();
|
||||||
const repToken = new ReputationToken();
|
// const repToken = new ReputationToken();
|
||||||
|
|
||||||
// const tokenMinter = new TokenHolder('TokenMinter', scene);
|
// const tokenMinter = new TokenHolder('TokenMinter', scene);
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
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 rootElement;
|
||||||
|
let rootBox;
|
||||||
|
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() {
|
||||||
|
rootElement = document.getElementById('scene');
|
||||||
|
rootBox = new Box('rootBox', rootElement).flex();
|
||||||
|
|
||||||
|
scene = (window.scene = new Scene('Forum test', rootBox));
|
||||||
|
scene.withSequenceDiagram();
|
||||||
|
|
||||||
|
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 = []);
|
||||||
|
|
||||||
|
forum.posts.withFlowchart();
|
||||||
|
|
||||||
|
scene.withTable();
|
||||||
|
|
||||||
|
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('Negative citation of a negative citation', async () => {
|
||||||
|
before(async () => {
|
||||||
|
await setup();
|
||||||
|
await addPost(experts[0], 10);
|
||||||
|
await addPost(experts[0], 10, [{ postId: posts[0], weight: -1 }]);
|
||||||
|
await addPost(experts[0], 10, [{ postId: posts[1], weight: -1 }]);
|
||||||
|
});
|
||||||
|
it('Should restore original post to its initial value', () => {
|
||||||
|
forum.getPost(posts[0]).value.should.equal(10);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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,23 @@
|
||||||
|
describe('Array', () => {
|
||||||
|
before(() => {
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#indexOf()', () => {
|
||||||
|
context('when not present', () => {
|
||||||
|
it('should not throw an error', () => {
|
||||||
|
(function aFunc() {
|
||||||
|
[1, 2, 3].indexOf(4);
|
||||||
|
}.should.not.throw());
|
||||||
|
});
|
||||||
|
it('should return -1', () => {
|
||||||
|
[1, 2, 3].indexOf(4).should.equal(-1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
context('when present', () => {
|
||||||
|
it('should return the index where the element first appears in the array', () => {
|
||||||
|
[1, 2, 3].indexOf(3).should.equal(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { Box } from '../../classes/box.js';
|
||||||
|
import { Scene } from '../../classes/scene.js';
|
||||||
|
import { WDAG } from '../../classes/wdag.js';
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('scene');
|
||||||
|
const rootBox = new Box('rootBox', rootElement).flex();
|
||||||
|
window.scene = new Scene('WDAG test', rootBox);
|
||||||
|
const graph = (window.graph = new WDAG(window.scene).withFlowchart());
|
||||||
|
const v = (window.v = []);
|
||||||
|
function addVertex() {
|
||||||
|
const vertex = graph.addVertex({ seq: window.v.length });
|
||||||
|
v.push(vertex);
|
||||||
|
}
|
||||||
|
addVertex();
|
||||||
|
addVertex();
|
||||||
|
addVertex();
|
||||||
|
addVertex();
|
||||||
|
addVertex();
|
||||||
|
|
||||||
|
graph.addEdge('e1', 0, 1, 1);
|
||||||
|
graph.addEdge('e1', 2, 1, 0.5);
|
||||||
|
graph.addEdge('e1', 3, 1, 0.25);
|
||||||
|
graph.addEdge('e1', 1, 4, 0.125);
|
|
@ -0,0 +1,9 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<head>
|
||||||
|
<title>Forum WDAG</title>
|
||||||
|
<link type="text/css" rel="stylesheet" href="../index.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="scene"></div>
|
||||||
|
</body>
|
||||||
|
<script type="module" src="./scripts/wdag.test.js"></script>
|
Loading…
Reference in New Issue