Merge branch 'dev' into 'main'
Add support for posts with multiple authors See merge request dao-governance-framework/science-publishing-dao!5
This commit is contained in:
commit
92bbab2a5b
|
@ -28,3 +28,19 @@ cardano -- "dynamic availability"?
|
|||
staking pools -- does it make sense with reputation?
|
||||
what about for governance voting --
|
||||
do we want a representative republic or a democracy?
|
||||
|
||||
---
|
||||
|
||||
# Protocol brainstorming
|
||||
|
||||
Each node must build/maintain a view of the history and/or state of the "smart contract" operations.
|
||||
|
||||
Nodes must sign messages to each other with asymmetric keys.
|
||||
|
||||
This is intended to be an open network that anyone can join.
|
||||
|
||||
Each node must verify the results reported by other nodes, and themselves report results to other nodes.
|
||||
|
||||
In order to receive payments, the network must solve the same problems that (other) block chains have solved, i.e. must prevent double-spend; must prevent tampering with the ledger.
|
||||
|
||||
Storage may be ranked into tiers, where there is core data essential to the integrity of the ledger; ancillary data that is important or desirable for review of the ledger; and supplementary data that is of variable importants for particular use cases, but does not compose the core fabric of the system.
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
# Ecosystem Health
|
||||
|
||||
How do we incentivize and reward the producers and maintainers of infrastructure? Of essential goods and services?
|
||||
|
||||
How do we reward pro-social behavior?
|
||||
|
||||
How do we encourage creativity?
|
||||
|
||||
Vision/Mission
|
||||
|
||||
Craig: Give people tools to enable them to better express their values by collaborating
|
||||
|
||||
truth
|
||||
good
|
||||
beauty
|
||||
|
||||
thought
|
||||
action
|
||||
perception
|
||||
|
||||
ideas
|
||||
knowledge
|
||||
beliefs
|
||||
|
||||
utility
|
||||
|
||||
evolution in the true sense -- most directions it can change will be detrimental
|
|
@ -0,0 +1,5 @@
|
|||
At the base layer, we need a peer to peer protocol that allows new nodes to join the network and send and receive messages. It must protect against denial of service attacks. It must support the establishment of consensus, to varying strengths.
|
||||
|
||||
We need a lightweight messaging solution to facilitate gathering information from the edges of the network, but we also need to protect against denial of service by malicious actors.
|
||||
|
||||
[gossipsub](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.1.md) looks like a good protocol for this.
|
|
@ -7,3 +7,28 @@ Matrix supports "Application Services", which are limited to funcion in a passiv
|
|||
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.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
|
||||
subgraph Matrix
|
||||
homeserver[Homeserver]
|
||||
chainClient[Blockchain<br/>connected<br/>client]
|
||||
publicClient[Public<br/>Matrix-only<br/>client]
|
||||
end
|
||||
|
||||
blockchain[Blockchain]
|
||||
%% subgraph Blockchain
|
||||
%% forum[Forum]
|
||||
%% post[Post]
|
||||
%% availability[Availability]
|
||||
%% wsc[WSC]
|
||||
%% pool[Validation<br/>Pool]
|
||||
%% end
|
||||
|
||||
publicClient --> homeserver
|
||||
chainClient --> homeserver
|
||||
chainClient --> blockchain
|
||||
homeserver --> blockchain
|
||||
|
||||
```
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# signature verification
|
||||
|
||||
# voting topologies
|
||||
|
||||
# client implementations
|
||||
|
||||
## example workflows
|
||||
|
||||
- retroactive sign flip
|
||||
|
||||
# storage scaling
|
||||
|
||||
# blockchain underpinnings
|
||||
|
||||
# reputation token lifetime / decay
|
||||
|
||||
- active/passive
|
|
@ -1,11 +1,13 @@
|
|||
import { Action } from '../display/action.js';
|
||||
import { PostMessage } from '../forum-network/message.js';
|
||||
import { CryptoUtil } from '../util/crypto.js';
|
||||
import { CryptoUtil } from '../supporting/crypto.js';
|
||||
import { ReputationHolder } from '../reputation/reputation-holder.js';
|
||||
import { EdgeTypes } from '../../util/constants.js';
|
||||
import { displayNumber } from '../../util/helpers.js';
|
||||
|
||||
export class Expert extends ReputationHolder {
|
||||
constructor(dao, name, scene) {
|
||||
super(name, scene);
|
||||
constructor(dao, name, scene, options) {
|
||||
super(name, scene, options);
|
||||
this.dao = dao;
|
||||
this.actions = {
|
||||
submitPostViaNetwork: new Action('submit post via network', scene),
|
||||
|
@ -20,6 +22,25 @@ export class Expert extends ReputationHolder {
|
|||
this.tokens = [];
|
||||
}
|
||||
|
||||
getReputation() {
|
||||
const authorVertex = this.dao.forum.graph.getVertex(this.reputationPublicKey);
|
||||
if (!authorVertex) {
|
||||
return 0;
|
||||
}
|
||||
const authorEdges = authorVertex.getEdges(EdgeTypes.AUTHOR, false);
|
||||
const tokenValues = authorEdges.map(({ data: { tokenId } }) => this.dao.reputation.valueOf(tokenId));
|
||||
return tokenValues.reduce((value, total) => total += value, 0);
|
||||
}
|
||||
|
||||
getLabel() {
|
||||
return `${this.name}
|
||||
<table><tr>
|
||||
<td>reputation</td>
|
||||
<td>${displayNumber(this.getReputation())}</td>
|
||||
</tr></table>`
|
||||
.replaceAll(/\n\s*/g, '');
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
this.reputationKey = await CryptoUtil.generateAsymmetricKey();
|
||||
// this.reputationPublicKey = await CryptoUtil.exportKey(this.reputationKey.publicKey);
|
||||
|
@ -28,15 +49,6 @@ export class Expert extends ReputationHolder {
|
|||
return this;
|
||||
}
|
||||
|
||||
async submitPostViaNetwork(forumNode, post, stake) {
|
||||
// TODO: Include fee
|
||||
const postMessage = new PostMessage({ post, stake });
|
||||
await postMessage.sign(this.reputationKey);
|
||||
await this.actions.submitPostViaNetwork.log(this, forumNode);
|
||||
// For now, directly call forumNode.receiveMessage();
|
||||
await forumNode.receiveMessage(JSON.stringify(postMessage.toJSON()));
|
||||
}
|
||||
|
||||
async submitPostWithFee(postContent, poolOptions) {
|
||||
const post = await this.dao.forum.addPost(this.reputationPublicKey, postContent);
|
||||
await this.actions.submitPost.log(this, post);
|
||||
|
@ -49,14 +61,9 @@ export class Expert extends ReputationHolder {
|
|||
async initiateValidationPool(poolOptions) {
|
||||
// For now, make direct call rather than network
|
||||
poolOptions.reputationPublicKey = this.reputationPublicKey;
|
||||
const pool = await this.dao.initiateValidationPool(poolOptions);
|
||||
const pool = await this.dao.initiateValidationPool(this, poolOptions);
|
||||
this.tokens.push(pool.tokenId);
|
||||
this.validationPools.set(pool.id, poolOptions);
|
||||
await this.actions.initiateValidationPool.log(
|
||||
this,
|
||||
pool,
|
||||
`(fee: ${poolOptions.fee}, stake: ${poolOptions.authorStakeAmount ?? 0})`,
|
||||
);
|
||||
return pool;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Action } from '../display/action.js';
|
||||
import { Actor } from '../display/actor.js';
|
||||
import { CryptoUtil } from '../util/crypto.js';
|
||||
import { CryptoUtil } from '../supporting/crypto.js';
|
||||
|
||||
class Worker {
|
||||
constructor(reputationPublicKey, tokenId, stakeAmount, duration) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { randomID } from '../../util.js';
|
||||
import { randomID } from '../../util/helpers.js';
|
||||
import { Action } from '../display/action.js';
|
||||
import { Actor } from '../display/actor.js';
|
||||
import { PostContent } from '../util/post-content.js';
|
||||
import { PostContent } from '../supporting/post-content.js';
|
||||
|
||||
class Request {
|
||||
static nextSeq = 0;
|
||||
|
@ -77,7 +77,7 @@ export class Business extends Actor {
|
|||
|
||||
// Initiate a validation pool for this work evidence.
|
||||
await this.actions.initiateValidationPool.log(this, this.dao);
|
||||
const pool = await this.dao.initiateValidationPool({
|
||||
const pool = await this.dao.initiateValidationPool(this, {
|
||||
postId,
|
||||
fee: request.fee,
|
||||
duration,
|
||||
|
|
|
@ -13,8 +13,8 @@ import { Actor } from '../display/actor.js';
|
|||
* - Reputation: Keep track of reputation accrued to each expert
|
||||
*/
|
||||
export class DAO extends Actor {
|
||||
constructor(name, scene) {
|
||||
super(name, scene);
|
||||
constructor(name, scene, options) {
|
||||
super(name, scene, options);
|
||||
|
||||
/* Contracts */
|
||||
this.forum = new Forum(this, 'Forum', scene);
|
||||
|
@ -55,10 +55,10 @@ export class DAO extends Actor {
|
|||
.reduce((acc, cur) => (acc += cur), 0);
|
||||
}
|
||||
|
||||
async initiateValidationPool(poolOptions, stakeOptions) {
|
||||
async initiateValidationPool(fromActor, poolOptions, stakeOptions) {
|
||||
const validationPoolNumber = this.validationPools.size + 1;
|
||||
const name = `Pool${validationPoolNumber}`;
|
||||
const pool = new ValidationPool(this, poolOptions, name, this.scene);
|
||||
const pool = new ValidationPool(this, poolOptions, name, this.scene, fromActor);
|
||||
this.validationPools.set(pool.id, pool);
|
||||
|
||||
if (stakeOptions) {
|
||||
|
|
|
@ -3,20 +3,22 @@ import { Action } from '../display/action.js';
|
|||
import { Actor } from '../display/actor.js';
|
||||
import params from '../../params.js';
|
||||
import { ReputationHolder } from '../reputation/reputation-holder.js';
|
||||
import { displayNumber, EPSILON, INCINERATOR_ADDRESS } from '../../util.js';
|
||||
|
||||
const CITATION = 'citation';
|
||||
const BALANCE = 'balance';
|
||||
import { displayNumber } from '../../util/helpers.js';
|
||||
import {
|
||||
EPSILON, INCINERATOR_ADDRESS, EdgeTypes, VertexTypes,
|
||||
} from '../../util/constants.js';
|
||||
|
||||
class Post extends Actor {
|
||||
constructor(forum, authorPublicKey, postContent) {
|
||||
const index = forum.posts.countVertices();
|
||||
constructor(forum, senderId, postContent) {
|
||||
const index = forum.graph.countVertices(VertexTypes.POST);
|
||||
const name = `Post${index + 1}`;
|
||||
super(name, forum.scene);
|
||||
this.forum = forum;
|
||||
this.id = postContent.id ?? name;
|
||||
this.authorPublicKey = authorPublicKey;
|
||||
this.senderId = senderId;
|
||||
this.value = 0;
|
||||
this.initialValue = 0;
|
||||
this.authors = postContent.authors;
|
||||
this.citations = postContent.citations;
|
||||
this.title = postContent.title;
|
||||
const leachingTotal = this.citations
|
||||
|
@ -25,6 +27,8 @@ class Post extends Actor {
|
|||
const donationTotal = this.citations
|
||||
.filter(({ weight }) => weight > 0)
|
||||
.reduce((total, { weight }) => total += weight, 0);
|
||||
|
||||
// TODO: Move evaluation of these parameters to Validation Pool
|
||||
if (leachingTotal > params.revaluationLimit) {
|
||||
throw new Error('Post leaching total exceeds revaluation limit '
|
||||
+ `(${leachingTotal} > ${params.revaluationLimit})`);
|
||||
|
@ -49,6 +53,12 @@ class Post extends Actor {
|
|||
</tr></table>`
|
||||
.replaceAll(/\n\s*/g, '');
|
||||
}
|
||||
|
||||
async setValue(value) {
|
||||
this.value = value;
|
||||
await this.setDisplayValue('value', value);
|
||||
this.forum.graph.getVertex(this.id).setDisplayLabel(this.getLabel());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -61,7 +71,7 @@ export class Forum extends ReputationHolder {
|
|||
super(name, scene);
|
||||
this.dao = dao;
|
||||
this.id = this.reputationPublicKey;
|
||||
this.posts = new WDAG(scene);
|
||||
this.graph = new WDAG(scene);
|
||||
this.actions = {
|
||||
propagate: new Action('propagate', scene),
|
||||
confirm: new Action('confirm', scene),
|
||||
|
@ -69,73 +79,120 @@ export class Forum extends ReputationHolder {
|
|||
};
|
||||
}
|
||||
|
||||
async addPost(authorId, postContent) {
|
||||
console.log('addPost', { authorId, postContent });
|
||||
const post = new Post(this, authorId, postContent);
|
||||
this.posts.addVertex(post.id, post, post.getLabel());
|
||||
async addPost(senderId, postContent) {
|
||||
console.log('addPost', { senderId, postContent });
|
||||
const post = new Post(this, senderId, postContent);
|
||||
this.graph.addVertex(VertexTypes.POST, post.id, post, post.getLabel());
|
||||
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');
|
||||
if (citedPostId === INCINERATOR_ADDRESS && !this.graph.getVertex(INCINERATOR_ADDRESS)) {
|
||||
this.graph.addVertex(VertexTypes.POST, INCINERATOR_ADDRESS, { name: 'Incinerator' }, 'Incinerator');
|
||||
}
|
||||
this.posts.addEdge(CITATION, post.id, citedPostId, weight);
|
||||
this.graph.addEdge(EdgeTypes.CITATION, post.id, citedPostId, weight);
|
||||
}
|
||||
return post;
|
||||
}
|
||||
|
||||
getPost(postId) {
|
||||
return this.posts.getVertexData(postId);
|
||||
return this.graph.getVertexData(postId);
|
||||
}
|
||||
|
||||
getPosts() {
|
||||
return this.posts.getVerticesData();
|
||||
}
|
||||
|
||||
async setPostValue(post, value) {
|
||||
post.value = value;
|
||||
await post.setValue('value', value);
|
||||
this.posts.setVertexLabel(post.id, post.getLabel());
|
||||
return this.graph.getVerticesData();
|
||||
}
|
||||
|
||||
getTotalValue() {
|
||||
return this.getPosts().reduce((total, { value }) => total += value, 0);
|
||||
}
|
||||
|
||||
// getLatestContract(type) { }
|
||||
|
||||
// getContract(type) { }
|
||||
|
||||
async onValidate({
|
||||
pool, postId, tokenId,
|
||||
}) {
|
||||
console.log('onValidate', { pool, postId, tokenId });
|
||||
const initialValue = this.dao.reputation.valueOf(tokenId);
|
||||
const postVertex = this.posts.getVertex(postId);
|
||||
const postVertex = this.graph.getVertex(postId);
|
||||
const post = postVertex.data;
|
||||
post.setStatus('Validated');
|
||||
post.initialValue = initialValue;
|
||||
this.posts.setVertexLabel(post.id, post.getLabel());
|
||||
postVertex.setDisplayLabel(post.getLabel());
|
||||
|
||||
// Store a reference to the reputation token associated with this post,
|
||||
// so that its value can be updated by future validated posts.
|
||||
post.tokenId = tokenId;
|
||||
const addAuthorToGraph = (publicKey, weight, authorTokenId) => {
|
||||
// For graph display purposes, we want to use the existing Expert actors from the current scene.
|
||||
const author = this.scene.findActor(({ reputationPublicKey }) => reputationPublicKey === publicKey);
|
||||
author.setDisplayValue('reputation', () => author.getReputation());
|
||||
const authorVertex = this.graph.getVertex(publicKey)
|
||||
?? this.graph.addVertex(VertexTypes.AUTHOR, publicKey, author, author.getLabel(), {
|
||||
hide: author.options.hide,
|
||||
});
|
||||
this.graph.addEdge(
|
||||
EdgeTypes.AUTHOR,
|
||||
postVertex,
|
||||
authorVertex,
|
||||
weight,
|
||||
{ tokenId: authorTokenId },
|
||||
{ hide: author.options.hide },
|
||||
);
|
||||
};
|
||||
|
||||
// In the case of multiple authors, mint additional (empty) tokens.
|
||||
// If no authors are specified, treat the sender as the sole author.
|
||||
// TODO: Verify that cumulative author weight == 1.
|
||||
if (!post.authors?.length) {
|
||||
addAuthorToGraph(post.senderId, 1, tokenId);
|
||||
} else {
|
||||
for (const { publicKey, weight } of post.authors) {
|
||||
// If the sender is also listed among the authors, do not mint them an additional token.
|
||||
const authorTokenId = (publicKey === post.senderId) ? tokenId : this.dao.reputation.mint(this.id, 0);
|
||||
addAuthorToGraph(publicKey, weight, authorTokenId);
|
||||
}
|
||||
// If the sender is not an author, they will end up with the minted token but with zero value.
|
||||
if (!post.authors.find(({ publicKey }) => publicKey === post.senderId)) {
|
||||
addAuthorToGraph(post.senderId, 0, tokenId);
|
||||
}
|
||||
}
|
||||
|
||||
const rewardsAccumulator = new Map();
|
||||
|
||||
// Compute rewards
|
||||
// Compute reputation rewards
|
||||
await this.propagateValue(
|
||||
{ to: postVertex, from: { data: pool } },
|
||||
{ rewardsAccumulator, increment: initialValue },
|
||||
);
|
||||
|
||||
// Apply computed rewards to update values of tokens
|
||||
for (const [id, value] of rewardsAccumulator) {
|
||||
if (value < 0) {
|
||||
this.dao.reputation.transferValueFrom(id, post.tokenId, -value);
|
||||
for (const [authorEdge, amount] of rewardsAccumulator) {
|
||||
const { to: authorVertex, data: { tokenId: authorTokenId } } = authorEdge;
|
||||
const { data: author } = authorVertex;
|
||||
// The primary author gets the validation pool minted token.
|
||||
// So we don't need to transfer any reputation to the primary author.
|
||||
// Their reward will be the remaining balance after all other transfers.
|
||||
if (authorTokenId !== tokenId) {
|
||||
if (amount < 0) {
|
||||
this.dao.reputation.transferValueFrom(authorTokenId, tokenId, -amount);
|
||||
} else {
|
||||
this.dao.reputation.transferValueFrom(post.tokenId, id, value);
|
||||
this.dao.reputation.transferValueFrom(tokenId, authorTokenId, amount);
|
||||
}
|
||||
await author.computeDisplayValues();
|
||||
authorVertex.setDisplayLabel(author.getLabel());
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer ownership of the minted/staked token, from the posts to the post author
|
||||
this.dao.reputation.transfer(this.id, post.authorPublicKey, post.tokenId);
|
||||
// const toActor = this.scene?.findActor((actor) => actor.reputationPublicKey === post.authorPublicKey);
|
||||
// const value = this.dao.reputation.valueOf(post.tokenId);
|
||||
const senderVertex = this.graph.getVertex(post.senderId);
|
||||
const { data: sender } = senderVertex;
|
||||
await sender.computeDisplayValues();
|
||||
senderVertex.setDisplayLabel(sender.getLabel());
|
||||
|
||||
// Transfer ownership of the minted tokens to the authors
|
||||
for (const authorEdge of postVertex.getEdges(EdgeTypes.AUTHOR, true)) {
|
||||
const authorVertex = authorEdge.to;
|
||||
const author = authorVertex.data;
|
||||
const { tokenId: authorTokenId } = authorEdge.data;
|
||||
this.dao.reputation.transfer(this.id, author.reputationPublicKey, authorTokenId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -146,7 +203,7 @@ export class Forum extends ReputationHolder {
|
|||
rewardsAccumulator, increment, depth = 0, initialNegative = false,
|
||||
}) {
|
||||
const postVertex = edge.to;
|
||||
const post = postVertex?.data;
|
||||
const post = postVertex.data;
|
||||
this.actions.propagate.log(edge.from.data, post, `(${increment})`);
|
||||
|
||||
if (!!params.referenceChainLimit && depth > params.referenceChainLimit) {
|
||||
|
@ -171,13 +228,14 @@ export class Forum extends ReputationHolder {
|
|||
|
||||
const propagate = async (positive) => {
|
||||
let totalOutboundAmount = 0;
|
||||
const citationEdges = postVertex.getEdges(CITATION, true)
|
||||
const citationEdges = postVertex.getEdges(EdgeTypes.CITATION, true)
|
||||
.filter(({ weight }) => (positive ? weight > 0 : weight < 0));
|
||||
for (const citationEdge of citationEdges) {
|
||||
const { weight } = citationEdge;
|
||||
let outboundAmount = weight * increment;
|
||||
if (Math.abs(outboundAmount) > EPSILON) {
|
||||
const balanceToOutbound = this.posts.getEdgeWeight(BALANCE, citationEdge.from, citationEdge.to) ?? 0;
|
||||
const balanceToOutbound = this.graph.getEdgeWeight(EdgeTypes.BALANCE, citationEdge.from, citationEdge.to)
|
||||
?? 0;
|
||||
let refundFromOutbound = 0;
|
||||
|
||||
// Special case: Incineration.
|
||||
|
@ -221,7 +279,12 @@ export class Forum extends ReputationHolder {
|
|||
|
||||
// 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.graph.setEdgeWeight(
|
||||
EdgeTypes.BALANCE,
|
||||
citationEdge.from,
|
||||
citationEdge.to,
|
||||
balanceToOutbound + outboundAmount,
|
||||
);
|
||||
totalOutboundAmount += outboundAmount;
|
||||
|
||||
this.actions.confirm.log(
|
||||
|
@ -250,6 +313,17 @@ export class Forum extends ReputationHolder {
|
|||
const appliedIncrement = newValue - post.value;
|
||||
const refundToInbound = increment - appliedIncrement;
|
||||
|
||||
// Apply reputation effects to post authors, not to the post directly
|
||||
for (const authorEdge of postVertex.getEdges(EdgeTypes.AUTHOR, true)) {
|
||||
const { weight, to: { data: author } } = authorEdge;
|
||||
const authorIncrement = weight * appliedIncrement;
|
||||
rewardsAccumulator.set(authorEdge, authorIncrement);
|
||||
this.actions.propagate.log(post, author, `(${authorIncrement})`);
|
||||
}
|
||||
|
||||
// Increment the value of the post
|
||||
await post.setValue(newValue);
|
||||
|
||||
console.log('propagateValue end', {
|
||||
depth,
|
||||
increment,
|
||||
|
@ -259,12 +333,6 @@ export class Forum extends ReputationHolder {
|
|||
refundToInbound,
|
||||
});
|
||||
|
||||
// Award reputation to post author
|
||||
rewardsAccumulator.set(post.tokenId, appliedIncrement);
|
||||
|
||||
// Increment the value of the post
|
||||
await this.setPostValue(post, newValue);
|
||||
|
||||
return refundToInbound;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Stake } from '../supporting/stake.js';
|
|||
import { Voter } from '../supporting/voter.js';
|
||||
import params from '../../params.js';
|
||||
import { Action } from '../display/action.js';
|
||||
import { displayNumber } from '../../util.js';
|
||||
import { displayNumber } from '../../util/helpers.js';
|
||||
|
||||
const ValidationPoolStates = Object.freeze({
|
||||
OPEN: 'OPEN',
|
||||
|
@ -27,16 +27,21 @@ export class ValidationPool extends ReputationHolder {
|
|||
},
|
||||
name,
|
||||
scene,
|
||||
fromActor,
|
||||
) {
|
||||
super(name, scene);
|
||||
this.id = this.reputationPublicKey;
|
||||
|
||||
this.actions = {
|
||||
initiate: new Action('initiate validation pool', scene),
|
||||
reward: new Action('reward', scene),
|
||||
transfer: new Action('transfer', scene),
|
||||
mint: new Action('mint', scene),
|
||||
};
|
||||
|
||||
this.actions.initiate.log(fromActor, this, `(fee: ${fee})`);
|
||||
this.activate();
|
||||
|
||||
// If contentiousDebate = true, we will follow the progression defined by getTokenLossRatio()
|
||||
if (
|
||||
!contentiousDebate
|
||||
|
@ -90,8 +95,6 @@ export class ValidationPool extends ReputationHolder {
|
|||
const voter = this.dao.experts.get(reputationPublicKey) ?? new Voter(reputationPublicKey);
|
||||
voter.addVoteRecord(this);
|
||||
this.dao.experts.set(reputationPublicKey, voter);
|
||||
|
||||
this.activate();
|
||||
}
|
||||
|
||||
getTokenLossRatio() {
|
||||
|
@ -180,7 +183,7 @@ export class ValidationPool extends ReputationHolder {
|
|||
|
||||
// Update computed display values
|
||||
const actor = this.scene?.findActor((a) => a.reputationPublicKey === voter.reputationPublicKey);
|
||||
await actor.computeValues();
|
||||
await actor.computeDisplayValues();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -235,9 +238,9 @@ export class ValidationPool extends ReputationHolder {
|
|||
if (!actor) {
|
||||
throw new Error('Actor not found!');
|
||||
}
|
||||
await actor.computeValues();
|
||||
await actor.computeDisplayValues();
|
||||
}
|
||||
await this.dao.computeValues();
|
||||
await this.dao.computeDisplayValues();
|
||||
|
||||
this.scene?.stateToTable(`validation pool ${this.name} complete`);
|
||||
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
import { displayNumber } from '../../util.js';
|
||||
import { displayNumber } from '../../util/helpers.js';
|
||||
|
||||
export class Actor {
|
||||
constructor(name, scene) {
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {Scene} scene
|
||||
* @param {boolean} options.announce
|
||||
* @param {boolean} options.hide
|
||||
*/
|
||||
constructor(name, scene, options = {}) {
|
||||
if (!scene) throw new Error('An actor without a scene!');
|
||||
this.name = name;
|
||||
this.scene = scene;
|
||||
|
@ -11,6 +17,7 @@ export class Actor {
|
|||
this.values = new Map();
|
||||
this.valueFunctions = new Map();
|
||||
this.active = 0;
|
||||
this.options = options;
|
||||
scene?.registerActor(this);
|
||||
}
|
||||
|
||||
|
@ -58,12 +65,12 @@ export class Actor {
|
|||
this.values.set(label, this.scene?.addDisplayValue(`${this.name} ${label}`));
|
||||
if (fn) {
|
||||
this.valueFunctions.set(label, fn);
|
||||
await this.computeValues();
|
||||
await this.computeDisplayValues();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
async setValue(label, value) {
|
||||
async setDisplayValue(label, value) {
|
||||
if (typeof value === 'function') {
|
||||
return this.addComputedValue(label, value);
|
||||
}
|
||||
|
@ -76,10 +83,10 @@ export class Actor {
|
|||
return this;
|
||||
}
|
||||
|
||||
async computeValues() {
|
||||
async computeDisplayValues() {
|
||||
for (const [label, fn] of this.valueFunctions.entries()) {
|
||||
const value = fn();
|
||||
await this.setValue(label, value);
|
||||
await this.setDisplayValue(label, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { DisplayValue } from './display-value.js';
|
||||
import { randomID } from '../../util.js';
|
||||
import { randomID } from '../../util/helpers.js';
|
||||
|
||||
export class Box {
|
||||
constructor(name, parentEl, options = {}) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { displayNumber } from '../../util.js';
|
||||
import { displayNumber } from '../../util/helpers.js';
|
||||
|
||||
export class DisplayValue {
|
||||
constructor(name, box) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import mermaid from 'https://unpkg.com/mermaid@9.2.2/dist/mermaid.esm.min.mjs';
|
||||
import { debounce } from '../../util.js';
|
||||
import { debounce } from '../../util/helpers.js';
|
||||
|
||||
export class MermaidDiagram {
|
||||
constructor(box, logBox) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Action } from './action.js';
|
||||
import { CryptoUtil } from '../util/crypto.js';
|
||||
import { CryptoUtil } from '../supporting/crypto.js';
|
||||
import { MermaidDiagram } from './mermaid.js';
|
||||
import { SequenceDiagram } from './sequence.js';
|
||||
import { Table } from './table.js';
|
||||
|
@ -88,7 +88,9 @@ export class Scene {
|
|||
|
||||
registerActor(actor) {
|
||||
this.actors.add(actor);
|
||||
// this.sequence?.log(`participant ${actor.name}`);
|
||||
if (actor.options.announce) {
|
||||
this.sequence?.log(`participant ${actor.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
findActor(fn) {
|
||||
|
@ -114,12 +116,14 @@ export class Scene {
|
|||
row.set('elapsedMs', new Date() - this.dateStart);
|
||||
row.set('label', label);
|
||||
for (const actor of this.actors) {
|
||||
if (!actor.options.hide) {
|
||||
for (const [aKey, { name, value }] of actor.getValuesMap()) {
|
||||
const key = `${actor.name}:${aKey}`;
|
||||
columns.push({ key, title: name });
|
||||
row.set(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
columns.push({ key: 'label', title: '' });
|
||||
this.table.setColumns(columns);
|
||||
this.table.addRow(row);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { hexToRGB } from '../../util.js';
|
||||
import { hexToRGB } from '../../util/helpers.js';
|
||||
import { MermaidDiagram } from './mermaid.js';
|
||||
|
||||
export class SequenceDiagram extends MermaidDiagram {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { displayNumber } from '../../util.js';
|
||||
import { displayNumber } from '../../util/helpers.js';
|
||||
|
||||
export class Table {
|
||||
constructor(box) {
|
||||
|
|
|
@ -2,14 +2,12 @@ import { Action } from '../display/action.js';
|
|||
import {
|
||||
Message, PostMessage, PeerMessage, messageFromJSON,
|
||||
} from './message.js';
|
||||
import { ForumView } from './forum-view.js';
|
||||
import { NetworkNode } from './network-node.js';
|
||||
import { randomID } from '../util/util.js';
|
||||
import { randomID } from '../../util/helpers.js';
|
||||
|
||||
export class ForumNode extends NetworkNode {
|
||||
constructor(name, scene) {
|
||||
super(name, scene);
|
||||
this.forumView = new ForumView();
|
||||
this.actions = {
|
||||
...this.actions,
|
||||
storePost: new Action('store post', scene),
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
import { WDAG } from '../supporting/wdag.js';
|
||||
|
||||
class Author {
|
||||
constructor() {
|
||||
this.posts = new Map();
|
||||
this.reputation = 0;
|
||||
}
|
||||
}
|
||||
|
||||
class PostVertex {
|
||||
constructor(id, author, stake, content, citations) {
|
||||
this.id = id;
|
||||
this.author = author;
|
||||
this.content = content;
|
||||
this.stake = stake;
|
||||
this.citations = citations;
|
||||
this.reputation = 0;
|
||||
}
|
||||
}
|
||||
|
||||
export class ForumView {
|
||||
constructor() {
|
||||
this.reputations = new Map();
|
||||
this.posts = new WDAG();
|
||||
this.authors = new Map();
|
||||
}
|
||||
|
||||
getReputation(id) {
|
||||
return this.reputations.get(id);
|
||||
}
|
||||
|
||||
setReputation(id, reputation) {
|
||||
this.reputations.set(id, reputation);
|
||||
}
|
||||
|
||||
incrementReputation(publicKey, increment, _reason) {
|
||||
const reputation = this.getReputation(publicKey) || 0;
|
||||
return this.reputations.set(publicKey, reputation + increment);
|
||||
}
|
||||
|
||||
getOrInitializeAuthor(authorId) {
|
||||
let author = this.authors.get(authorId);
|
||||
if (!author) {
|
||||
author = new Author(authorId);
|
||||
this.authors.set(authorId, author);
|
||||
}
|
||||
return author;
|
||||
}
|
||||
|
||||
addPost(authorId, postId, postContent, stake) {
|
||||
const { citations = [], content } = postContent;
|
||||
const author = this.getOrInitializeAuthor(authorId);
|
||||
const postVertex = new PostVertex(postId, author, stake, content, citations);
|
||||
this.posts.addVertex(postId, postVertex);
|
||||
for (const { postId: citedPostId, weight } of citations) {
|
||||
this.posts.addEdge('citation', postId, citedPostId, weight);
|
||||
}
|
||||
}
|
||||
|
||||
getPost(postId) {
|
||||
return this.posts.getVertexData(postId);
|
||||
}
|
||||
|
||||
getPosts() {
|
||||
return this.posts.getVertices();
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import { CryptoUtil } from '../util/crypto.js';
|
||||
import { PostContent } from '../util/post-content.js';
|
||||
import { CryptoUtil } from '../supporting/crypto.js';
|
||||
import { PostContent } from '../supporting/post-content.js';
|
||||
|
||||
export class Message {
|
||||
constructor(content) {
|
||||
|
|
|
@ -38,7 +38,8 @@ export class NetworkNode extends Actor {
|
|||
// Enqueue it for further processing.
|
||||
async receiveMessage(messageStr) {
|
||||
const messageJson = JSON.parse(messageStr);
|
||||
const senderReputation = this.forumView.getReputation(messageJson.publicKey) || 0;
|
||||
// const senderReputation = this.forumView.getReputation(messageJson.publicKey) || 0;
|
||||
const senderReputation = 0;
|
||||
this.queue.add(messageJson, senderReputation);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { randomID } from '../util/util.js';
|
||||
import { randomID } from '../util/util/helpers.js';
|
||||
|
||||
class Pledge {
|
||||
constructor({ stake, duration }) {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { randomID } from '../../util.js';
|
||||
import { randomID } from '../../util/helpers.js';
|
||||
import { Actor } from '../display/actor.js';
|
||||
|
||||
export class ReputationHolder extends Actor {
|
||||
constructor(name, scene) {
|
||||
super(name, scene);
|
||||
constructor(name, scene, options) {
|
||||
super(name, scene, options);
|
||||
this.reputationPublicKey = `${name}_${randomID()}`;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { ERC721 } from '../supporting/erc721.js';
|
||||
|
||||
import { EPSILON, randomID } from '../../util.js';
|
||||
import { randomID } from '../../util/helpers.js';
|
||||
import { EPSILON } from '../../util/constants.js';
|
||||
|
||||
class Lock {
|
||||
constructor(tokenId, amount, duration) {
|
||||
|
@ -36,6 +36,9 @@ export class ReputationTokenContract extends ERC721 {
|
|||
|
||||
incrementValue(tokenId, increment, context) {
|
||||
const value = this.values.get(tokenId);
|
||||
if (value === undefined) {
|
||||
throw new Error(`Token not found: ${tokenId}`);
|
||||
}
|
||||
const newValue = value + increment;
|
||||
const history = this.histories.get(tokenId) || [];
|
||||
|
||||
|
@ -76,7 +79,11 @@ export class ReputationTokenContract extends ERC721 {
|
|||
}
|
||||
|
||||
valueOf(tokenId) {
|
||||
return this.values.get(tokenId);
|
||||
const value = this.values.get(tokenId);
|
||||
if (value === undefined) {
|
||||
throw new Error(`Token not found: ${tokenId}`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
availableValueOf(tokenId) {
|
||||
|
|
|
@ -31,6 +31,7 @@ export class ERC721 {
|
|||
}
|
||||
|
||||
mint(to, tokenId) {
|
||||
console.log('ERC721.mint', { to, tokenId });
|
||||
if (this.owners.get(tokenId)) {
|
||||
throw new Error('ERC721: token already minted');
|
||||
}
|
||||
|
@ -60,9 +61,10 @@ export class ERC721 {
|
|||
}
|
||||
|
||||
transfer(from, to, tokenId) {
|
||||
console.log('ERC721.transfer', { from, to, tokenId });
|
||||
const owner = this.owners.get(tokenId);
|
||||
if (owner !== from) {
|
||||
throw new Error('ERC721: transfer from incorrect owner');
|
||||
throw new Error(`ERC721: transfer from incorrect owner ${from}; should be ${owner}`);
|
||||
}
|
||||
this.incrementBalance(from, -1);
|
||||
this.incrementBalance(to, 1);
|
||||
|
|
|
@ -1,4 +1,22 @@
|
|||
export class Citation {
|
||||
class Author {
|
||||
constructor(publicKey, weight) {
|
||||
this.publicKey = publicKey;
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
publicKey: this.publicKey,
|
||||
weight: this.weight,
|
||||
};
|
||||
}
|
||||
|
||||
static fromJSON({ publicKey, weight }) {
|
||||
return new Author(publicKey, weight);
|
||||
}
|
||||
}
|
||||
|
||||
class Citation {
|
||||
constructor(postId, weight) {
|
||||
this.postId = postId;
|
||||
this.weight = weight;
|
||||
|
@ -17,11 +35,18 @@ export class Citation {
|
|||
}
|
||||
|
||||
export class PostContent {
|
||||
constructor(content) {
|
||||
constructor(content = {}) {
|
||||
this.content = content;
|
||||
this.authors = [];
|
||||
this.citations = [];
|
||||
}
|
||||
|
||||
addAuthor(authorPublicKey, weight) {
|
||||
const author = new Author(authorPublicKey, weight);
|
||||
this.authors.push(author);
|
||||
return this;
|
||||
}
|
||||
|
||||
addCitation(postId, weight) {
|
||||
const citation = new Citation(postId, weight);
|
||||
this.citations.push(citation);
|
||||
|
@ -36,6 +61,7 @@ export class PostContent {
|
|||
toJSON() {
|
||||
return {
|
||||
content: this.content,
|
||||
authors: this.authors.map((author) => author.toJSON()),
|
||||
citations: this.citations.map((citation) => citation.toJSON()),
|
||||
...(this.id ? { id: this.id } : {}),
|
||||
title: this.title,
|
||||
|
@ -43,9 +69,10 @@ export class PostContent {
|
|||
}
|
||||
|
||||
static fromJSON({
|
||||
id, content, citations, title,
|
||||
id, content, authors, citations, title,
|
||||
}) {
|
||||
const post = new PostContent(content);
|
||||
post.authors = authors.map((author) => Author.fromJSON(author));
|
||||
post.citations = citations.map((citation) => Citation.fromJSON(citation));
|
||||
post.id = id;
|
||||
post.title = title;
|
|
@ -1,26 +1,69 @@
|
|||
const getEdgeKey = ({ from, to }) => btoa([from.id, to.id]).replaceAll(/[^A-Za-z0-9]+/g, '');
|
||||
|
||||
export class Vertex {
|
||||
constructor(id, data) {
|
||||
constructor(graph, type, id, data, options = {}) {
|
||||
this.graph = graph;
|
||||
this.type = type;
|
||||
this.id = id;
|
||||
this.data = data;
|
||||
this.options = options;
|
||||
this.edges = {
|
||||
from: [],
|
||||
to: [],
|
||||
};
|
||||
}
|
||||
|
||||
getEdges(label, away) {
|
||||
getEdges(type, away) {
|
||||
return this.edges[away ? 'from' : 'to'].filter(
|
||||
(edge) => edge.label === label,
|
||||
(edge) => edge.type === type,
|
||||
);
|
||||
}
|
||||
|
||||
setDisplayLabel(label) {
|
||||
if (this.options.hide) {
|
||||
return;
|
||||
}
|
||||
this.graph.flowchart?.log(`${this.id}[${label}]`);
|
||||
}
|
||||
}
|
||||
|
||||
export class Edge {
|
||||
constructor(label, from, to, weight) {
|
||||
constructor(graph, type, from, to, weight, data, options = {}) {
|
||||
this.graph = graph;
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.label = label;
|
||||
this.type = type;
|
||||
this.weight = weight;
|
||||
this.data = data;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
getHtml() {
|
||||
let html = '<table>';
|
||||
for (const { type, weight } of this.graph.getEdges(null, this.from, this.to)) {
|
||||
html += `<tr><td>${type}</td><td>${weight}</td></tr>`;
|
||||
}
|
||||
html += '</table>';
|
||||
return html;
|
||||
}
|
||||
|
||||
getFlowchartNode() {
|
||||
return `${getEdgeKey(this)}(${this.getHtml()})`;
|
||||
}
|
||||
|
||||
displayEdgeNode() {
|
||||
if (this.options.hide) {
|
||||
return;
|
||||
}
|
||||
this.graph.flowchart?.log(this.getFlowchartNode());
|
||||
}
|
||||
|
||||
displayEdge() {
|
||||
if (this.options.hide) {
|
||||
return;
|
||||
}
|
||||
this.graph.flowchart?.log(`${this.from.id} --- ${this.getFlowchartNode()} --> ${this.to.id}`);
|
||||
this.graph.flowchart?.log(`class ${getEdgeKey(this)} edge`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,7 +71,7 @@ export class WDAG {
|
|||
constructor(scene) {
|
||||
this.scene = scene;
|
||||
this.vertices = new Map();
|
||||
this.edgeLabels = new Map();
|
||||
this.edgeTypes = new Map();
|
||||
this.nextVertexId = 0;
|
||||
this.flowchart = scene?.flowchart;
|
||||
}
|
||||
|
@ -39,7 +82,7 @@ export class WDAG {
|
|||
return this;
|
||||
}
|
||||
|
||||
addVertex(id, data, label) {
|
||||
addVertex(type, id, data, label, options) {
|
||||
// Support simple case of auto-incremented numeric ids
|
||||
if (typeof id === 'object') {
|
||||
data = id;
|
||||
|
@ -48,14 +91,10 @@ export class WDAG {
|
|||
if (this.vertices.has(id)) {
|
||||
throw new Error(`Vertex already exists with id: ${id}`);
|
||||
}
|
||||
const vertex = new Vertex(id, data);
|
||||
const vertex = new Vertex(this, type, id, data, options);
|
||||
this.vertices.set(id, vertex);
|
||||
this.flowchart?.log(`${id}[${label ?? id}]`);
|
||||
return this;
|
||||
}
|
||||
|
||||
setVertexLabel(id, label) {
|
||||
this.flowchart?.log(`${id}[${label}]`);
|
||||
vertex.setDisplayLabel(label ?? id);
|
||||
return vertex;
|
||||
}
|
||||
|
||||
getVertex(id) {
|
||||
|
@ -70,74 +109,55 @@ export class WDAG {
|
|||
return Array.from(this.vertices.values()).map(({ data }) => data);
|
||||
}
|
||||
|
||||
static getEdgeKey({ from, to }) {
|
||||
return btoa([from.id, to.id]).replaceAll(/[^A-Za-z0-9]+/g, '');
|
||||
}
|
||||
|
||||
getEdge(label, from, to) {
|
||||
getEdge(type, from, to) {
|
||||
from = from instanceof Vertex ? from : this.getVertex(from);
|
||||
to = to instanceof Vertex ? to : this.getVertex(to);
|
||||
if (!from || !to) {
|
||||
return undefined;
|
||||
}
|
||||
const edges = this.edgeLabels.get(label);
|
||||
const edgeKey = WDAG.getEdgeKey({ from, to });
|
||||
const edges = this.edgeTypes.get(type);
|
||||
const edgeKey = getEdgeKey({ from, to });
|
||||
return edges?.get(edgeKey);
|
||||
}
|
||||
|
||||
getEdgeWeight(label, from, to) {
|
||||
return this.getEdge(label, from, to)?.weight;
|
||||
getEdgeWeight(type, from, to) {
|
||||
return this.getEdge(type, from, to)?.weight;
|
||||
}
|
||||
|
||||
getEdgeHtml({ from, to }) {
|
||||
let html = '<table>';
|
||||
for (const { label, weight } of this.getEdges(null, from, to)) {
|
||||
html += `<tr><td>${label}</td><td>${weight}</td></tr>`;
|
||||
}
|
||||
html += '</table>';
|
||||
return html;
|
||||
}
|
||||
|
||||
getEdgeFlowchartNode(edge) {
|
||||
const edgeKey = WDAG.getEdgeKey(edge);
|
||||
return `${edgeKey}(${this.getEdgeHtml(edge)})`;
|
||||
}
|
||||
|
||||
setEdgeWeight(label, from, to, weight) {
|
||||
setEdgeWeight(type, from, to, weight, data, options) {
|
||||
from = from instanceof Vertex ? from : this.getVertex(from);
|
||||
to = to instanceof Vertex ? to : this.getVertex(to);
|
||||
const edge = new Edge(label, from, to, weight);
|
||||
let edges = this.edgeLabels.get(label);
|
||||
const edge = new Edge(this, type, from, to, weight, data, options);
|
||||
let edges = this.edgeTypes.get(type);
|
||||
if (!edges) {
|
||||
edges = new Map();
|
||||
this.edgeLabels.set(label, edges);
|
||||
this.edgeTypes.set(type, edges);
|
||||
}
|
||||
const edgeKey = WDAG.getEdgeKey(edge);
|
||||
const edgeKey = getEdgeKey(edge);
|
||||
edges.set(edgeKey, edge);
|
||||
this.flowchart?.log(this.getEdgeFlowchartNode(edge));
|
||||
edge.displayEdgeNode();
|
||||
return edge;
|
||||
}
|
||||
|
||||
addEdge(label, from, to, weight) {
|
||||
addEdge(type, from, to, weight, data, options) {
|
||||
from = from instanceof Vertex ? from : this.getVertex(from);
|
||||
to = to instanceof Vertex ? to : this.getVertex(to);
|
||||
if (this.getEdge(label, from, to)) {
|
||||
throw new Error(`Edge ${label} from ${from.id} to ${to.id} already exists`);
|
||||
if (this.getEdge(type, from, to)) {
|
||||
throw new Error(`Edge ${type} from ${from.id} to ${to.id} already exists`);
|
||||
}
|
||||
const edge = this.setEdgeWeight(label, from, to, weight);
|
||||
const edge = this.setEdgeWeight(type, from, to, weight, data, options);
|
||||
from.edges.from.push(edge);
|
||||
to.edges.to.push(edge);
|
||||
this.flowchart?.log(`${from.id} --- ${this.getEdgeFlowchartNode(edge)} --> ${to.id}`);
|
||||
this.flowchart?.log(`class ${WDAG.getEdgeKey(edge)} edge`);
|
||||
return this;
|
||||
edge.displayEdge();
|
||||
return edge;
|
||||
}
|
||||
|
||||
getEdges(label, from, to) {
|
||||
getEdges(type, 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());
|
||||
return edgeLabels.flatMap((edgeLabel) => {
|
||||
const edges = this.edgeLabels.get(edgeLabel);
|
||||
const edgeTypes = type ? [type] : Array.from(this.edgeTypes.keys());
|
||||
return edgeTypes.flatMap((edgeType) => {
|
||||
const edges = this.edgeTypes.get(edgeType);
|
||||
return Array.from(edges?.values() || []).filter((edge) => {
|
||||
const matchFrom = from === null || from === undefined || from === edge.from;
|
||||
const matchTo = to === null || to === undefined || to === edge.to;
|
||||
|
@ -146,7 +166,10 @@ export class WDAG {
|
|||
});
|
||||
}
|
||||
|
||||
countVertices() {
|
||||
countVertices(type) {
|
||||
if (!type) {
|
||||
return this.vertices.size;
|
||||
}
|
||||
return Array.from(this.vertices.values()).filter((vertex) => vertex.type === type).length;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
export class PrioritizedQueue {
|
||||
constructor() {
|
||||
this.buffer = [];
|
||||
}
|
||||
|
||||
// Add an item to the buffer, ahead of the next lowest priority item
|
||||
add(message, priority) {
|
||||
const idx = this.buffer.findIndex((item) => item.priority < priority);
|
||||
if (idx < 0) {
|
||||
this.buffer.push({ message, priority });
|
||||
} else {
|
||||
this.buffer.splice(idx, 0, { message, priority });
|
||||
}
|
||||
}
|
||||
|
||||
// Return the highest priority item in the buffer
|
||||
pop() {
|
||||
if (!this.buffer.length) {
|
||||
return null;
|
||||
}
|
||||
const item = this.buffer.shift();
|
||||
return item.message;
|
||||
}
|
||||
}
|
|
@ -24,6 +24,8 @@
|
|||
<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>
|
||||
<li><a href="./tests/forum10.test.html">Post with multiple authors</a></li>
|
||||
<li><a href="./tests/forum11.test.html">Multiple posts with overlapping authors</a></li>
|
||||
</ol>
|
||||
</ul>
|
||||
<ul>
|
||||
|
|
|
@ -36,6 +36,8 @@
|
|||
<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 type="module" src="./scripts/forum/forum10.test.js"></script>
|
||||
<script type="module" src="./scripts/forum/forum11.test.js"></script>
|
||||
<script defer class="mocha-init">
|
||||
mocha.setup({
|
||||
ui: 'bdd',
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
import { Scene } from '../classes/display/scene.js';
|
||||
import { Actor } from '../classes/display/actor.js';
|
||||
import { Action } from '../classes/display/action.js';
|
||||
import { delay } from '../util.js';
|
||||
import { delay } from '../util/helpers.js';
|
||||
|
||||
const DEFAULT_DELAY_INTERVAL = 500;
|
||||
|
||||
|
@ -28,7 +28,7 @@
|
|||
const actor2 = new Actor('B', scene);
|
||||
const action1 = new Action('Action 1', scene);
|
||||
await action1.log(actor1, actor2);
|
||||
await actor1.setValue('value', 1);
|
||||
await actor1.setDisplayValue('value', 1);
|
||||
|
||||
await scene.withFlowchart();
|
||||
await scene.flowchart.log('A --> B');
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<head>
|
||||
<title>Forum test 10</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/forum10.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 11</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/forum11.test.js"></script>
|
||||
<script defer class="mocha-init">
|
||||
mocha.setup({
|
||||
ui: 'bdd',
|
||||
});
|
||||
chai.should();
|
||||
</script>
|
|
@ -15,7 +15,7 @@
|
|||
// import { ValidationPool } from '../classes/validation-pool.js';
|
||||
// import { TokenHolder } from '../classes/token-holder.js';
|
||||
// import { ReputationToken } from '../classes/reputation-token.js';
|
||||
import { delay } from '../util.js';
|
||||
import { delay } from '../util/helpers.js';
|
||||
|
||||
const DEFAULT_DELAY_INTERVAL = 500;
|
||||
|
||||
|
|
|
@ -3,9 +3,9 @@ import { Scene } from '../../classes/display/scene.js';
|
|||
import { Expert } from '../../classes/actors/expert.js';
|
||||
import { DAO } from '../../classes/dao/dao.js';
|
||||
import { Public } from '../../classes/actors/public.js';
|
||||
import { PostContent } from '../../classes/util/post-content.js';
|
||||
import { PostContent } from '../../classes/supporting/post-content.js';
|
||||
import { delayOrWait } from '../../classes/display/controls.js';
|
||||
import { mochaRun } from '../../util.js';
|
||||
import { mochaRun } from '../../util/helpers.js';
|
||||
|
||||
const DELAY_INTERVAL = 100;
|
||||
const POOL_DURATION = 200;
|
||||
|
@ -18,7 +18,7 @@ const newExpert = async () => {
|
|||
const index = experts.length;
|
||||
const name = `Expert${index + 1}`;
|
||||
const expert = await new Expert(dao, name, scene).initialize();
|
||||
expert.setValue(
|
||||
expert.setDisplayValue(
|
||||
'rep',
|
||||
() => dao.reputation.valueOwnedBy(expert.reputationPublicKey),
|
||||
);
|
||||
|
@ -36,7 +36,7 @@ const setup = async () => {
|
|||
scene.withTable();
|
||||
|
||||
dao = new DAO('DGF', scene);
|
||||
await dao.setValue('total rep', () => dao.reputation.getTotal());
|
||||
await dao.setDisplayValue('total rep', () => dao.reputation.getTotal());
|
||||
|
||||
experts = [];
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Business } from '../../classes/dao/business.js';
|
||||
import { Scene } from '../../classes/display/scene.js';
|
||||
import { Box } from '../../classes/display/box.js';
|
||||
import { mochaRun } from '../../util.js';
|
||||
import { mochaRun } from '../../util/helpers.js';
|
||||
|
||||
describe('Business', function tests() {
|
||||
this.timeout(0);
|
||||
|
|
|
@ -2,7 +2,7 @@ import { Box } from '../../classes/display/box.js';
|
|||
import { Scene } from '../../classes/display/scene.js';
|
||||
import { Action } from '../../classes/display/action.js';
|
||||
import { Actor } from '../../classes/display/actor.js';
|
||||
import { debounce, delay, mochaRun } from '../../util.js';
|
||||
import { debounce, delay, mochaRun } from '../../util/helpers.js';
|
||||
|
||||
describe('Debounce', () => {
|
||||
let scene;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { Box } from '../../classes/display/box.js';
|
||||
import { Scene } from '../../classes/display/scene.js';
|
||||
import { PostContent } from '../../classes/util/post-content.js';
|
||||
import { PostContent } from '../../classes/supporting/post-content.js';
|
||||
import { Expert } from '../../classes/actors/expert.js';
|
||||
import { ForumNode } from '../../classes/forum-network/forum-node.js';
|
||||
import { Network } from '../../classes/forum-network/network.js';
|
||||
import { mochaRun, randomID } from '../../util.js';
|
||||
import { mochaRun, randomID } from '../../util/helpers.js';
|
||||
import { delayOrWait } from '../../classes/display/controls.js';
|
||||
|
||||
describe('Forum Network', function tests() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Box } from '../../../classes/display/box.js';
|
||||
import { Scene } from '../../../classes/display/scene.js';
|
||||
import { Expert } from '../../../classes/actors/expert.js';
|
||||
import { PostContent } from '../../../classes/util/post-content.js';
|
||||
import { PostContent } from '../../../classes/supporting/post-content.js';
|
||||
import params from '../../../params.js';
|
||||
import { DAO } from '../../../classes/dao/dao.js';
|
||||
import { delayOrWait } from '../../../classes/display/controls.js';
|
||||
|
@ -19,17 +19,27 @@ export class ForumTest {
|
|||
};
|
||||
}
|
||||
|
||||
async addPost(author, fee, citations = []) {
|
||||
async addPost(authors, fee, citations = []) {
|
||||
const postIndex = this.posts.length;
|
||||
const title = `posts[${postIndex}]`;
|
||||
await this.scene.sequence.startSection();
|
||||
|
||||
const postContent = new PostContent({}).setTitle(title);
|
||||
const postContent = new PostContent().setTitle(title);
|
||||
|
||||
const submitter = Array.isArray(authors) ? authors[0].author : authors;
|
||||
|
||||
if (Array.isArray(authors)) {
|
||||
for (const { author, weight } of authors) {
|
||||
console.log('author', { author, weight });
|
||||
postContent.addAuthor(author.reputationPublicKey, weight);
|
||||
}
|
||||
}
|
||||
|
||||
for (const { postId, weight } of citations) {
|
||||
postContent.addCitation(postId, weight);
|
||||
}
|
||||
|
||||
const { pool, postId } = await author.submitPostWithFee(
|
||||
const { pool, postId } = await submitter.submitPostWithFee(
|
||||
postContent,
|
||||
{
|
||||
fee,
|
||||
|
@ -46,11 +56,15 @@ export class ForumTest {
|
|||
}
|
||||
|
||||
async newExpert() {
|
||||
// Hide by default, for simplicity of rendering first 9 forum tests
|
||||
const options = {
|
||||
hide: !this.options.displayAuthors,
|
||||
announce: this.options.displayAuthors,
|
||||
};
|
||||
const index = this.experts.length;
|
||||
const name = `Expert${index + 1}`;
|
||||
const expert = await new Expert(this.dao, name, this.scene).initialize();
|
||||
const expert = await new Expert(this.dao, name, this.scene, options).initialize();
|
||||
this.experts.push(expert);
|
||||
// await expert.addComputedValue('rep', () => this.dao.reputation.valueOwnedBy(expert.reputationPublicKey));
|
||||
return expert;
|
||||
}
|
||||
|
||||
|
@ -71,17 +85,14 @@ export class ForumTest {
|
|||
scene.addDisplayValue('q4. leachingValue').set(params.leachingValue);
|
||||
scene.addDisplayValue(' ');
|
||||
|
||||
this.dao = new DAO('DAO', scene);
|
||||
// If we're going to announce experts, announce the DAO so it appears first.
|
||||
this.dao = new DAO('DAO', scene, { announce: this.options.displayAuthors });
|
||||
this.forum = this.dao.forum;
|
||||
this.experts = [];
|
||||
this.posts = [];
|
||||
|
||||
await this.newExpert();
|
||||
// await newExpert();
|
||||
// await newExpert();
|
||||
|
||||
await this.dao.addComputedValue('total value', () => this.dao.reputation.getTotal());
|
||||
// await this.dao.addComputedValue('total reputation', () => this.dao.forum.getTotalValue());
|
||||
this.dao.computeValues();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
import { mochaRun } from '../../../util.js';
|
||||
import { mochaRun } from '../../../util/helpers.js';
|
||||
import { ForumTest } from './forum.test-util.js';
|
||||
|
||||
describe('Forum', function tests() {
|
||||
this.timeout(0);
|
||||
|
||||
const forumTest = new ForumTest();
|
||||
const forumTest = new ForumTest({ displayAuthors: false });
|
||||
|
||||
before(async () => {
|
||||
await forumTest.setup();
|
||||
await forumTest.newExpert();
|
||||
await forumTest.newExpert();
|
||||
});
|
||||
|
||||
context('Negative citation of a negative citation', async () => {
|
||||
|
@ -19,14 +21,14 @@ describe('Forum', function tests() {
|
|||
|
||||
it('Post2 negatively cites Post1', async () => {
|
||||
const { forum, experts, posts } = forumTest;
|
||||
await forumTest.addPost(experts[0], 10, [{ postId: posts[0], weight: -1 }]);
|
||||
await forumTest.addPost(experts[1], 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 }]);
|
||||
await forumTest.addPost(experts[2], 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);
|
||||
|
@ -34,11 +36,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();
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
import { mochaRun } from '../../../util/helpers.js';
|
||||
import { ForumTest } from './forum.test-util.js';
|
||||
|
||||
describe('Forum', function tests() {
|
||||
this.timeout(0);
|
||||
|
||||
const forumTest = new ForumTest({ displayAuthors: true });
|
||||
|
||||
before(async () => {
|
||||
await forumTest.setup();
|
||||
await forumTest.newExpert();
|
||||
await forumTest.newExpert();
|
||||
});
|
||||
|
||||
context('Post with multiple authors', async () => {
|
||||
let forum;
|
||||
let experts;
|
||||
let posts;
|
||||
let dao;
|
||||
|
||||
before(async () => {
|
||||
forum = forumTest.forum;
|
||||
experts = forumTest.experts;
|
||||
posts = forumTest.posts;
|
||||
dao = forumTest.dao;
|
||||
});
|
||||
|
||||
it('Post1 has three authors and reputation is distributed among them', async () => {
|
||||
const authors = [
|
||||
{ author: experts[0], weight: 0.5 },
|
||||
{ author: experts[1], weight: 0.25 },
|
||||
{ author: experts[2], weight: 0.25 },
|
||||
];
|
||||
await forumTest.addPost(authors, 10);
|
||||
forum.getPost(posts[0]).value.should.equal(10);
|
||||
|
||||
dao.reputation.valueOwnedBy(experts[0].reputationPublicKey).should.equal(5);
|
||||
dao.reputation.valueOwnedBy(experts[1].reputationPublicKey).should.equal(2.5);
|
||||
dao.reputation.valueOwnedBy(experts[2].reputationPublicKey).should.equal(2.5);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
mochaRun();
|
|
@ -0,0 +1,56 @@
|
|||
import { mochaRun } from '../../../util/helpers.js';
|
||||
import { ForumTest } from './forum.test-util.js';
|
||||
|
||||
describe('Forum', function tests() {
|
||||
this.timeout(0);
|
||||
|
||||
const forumTest = new ForumTest({ displayAuthors: true });
|
||||
|
||||
before(async () => {
|
||||
await forumTest.setup();
|
||||
await forumTest.newExpert();
|
||||
await forumTest.newExpert();
|
||||
});
|
||||
|
||||
context('Multiple posts with overlapping authors', async () => {
|
||||
let forum;
|
||||
let experts;
|
||||
let posts;
|
||||
let dao;
|
||||
|
||||
before(async () => {
|
||||
forum = forumTest.forum;
|
||||
experts = forumTest.experts;
|
||||
posts = forumTest.posts;
|
||||
dao = forumTest.dao;
|
||||
});
|
||||
|
||||
it('Post1 with two authors', async () => {
|
||||
const authors = [
|
||||
{ author: experts[0], weight: 0.5 },
|
||||
{ author: experts[1], weight: 0.5 },
|
||||
];
|
||||
await forumTest.addPost(authors, 10);
|
||||
forum.getPost(posts[0]).value.should.equal(10);
|
||||
|
||||
dao.reputation.valueOwnedBy(experts[0].reputationPublicKey).should.equal(5);
|
||||
dao.reputation.valueOwnedBy(experts[1].reputationPublicKey).should.equal(5);
|
||||
dao.reputation.valueOwnedBy(experts[2].reputationPublicKey).should.equal(0);
|
||||
});
|
||||
|
||||
it('Post2 with two authors, one shared with Post1', async () => {
|
||||
const authors = [
|
||||
{ author: experts[1], weight: 0.5 },
|
||||
{ author: experts[2], weight: 0.5 },
|
||||
];
|
||||
await forumTest.addPost(authors, 10);
|
||||
forum.getPost(posts[0]).value.should.equal(10);
|
||||
|
||||
dao.reputation.valueOwnedBy(experts[0].reputationPublicKey).should.equal(5);
|
||||
dao.reputation.valueOwnedBy(experts[1].reputationPublicKey).should.equal(10);
|
||||
dao.reputation.valueOwnedBy(experts[2].reputationPublicKey).should.equal(5);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
mochaRun();
|
|
@ -1,4 +1,4 @@
|
|||
import { mochaRun } from '../../../util.js';
|
||||
import { mochaRun } from '../../../util/helpers.js';
|
||||
import { ForumTest } from './forum.test-util.js';
|
||||
|
||||
describe('Forum', function tests() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { mochaRun } from '../../../util.js';
|
||||
import { mochaRun } from '../../../util/helpers.js';
|
||||
import { ForumTest } from './forum.test-util.js';
|
||||
|
||||
describe('Forum', function tests() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { mochaRun } from '../../../util.js';
|
||||
import { mochaRun } from '../../../util/helpers.js';
|
||||
import { ForumTest } from './forum.test-util.js';
|
||||
|
||||
describe('Forum', function tests() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { mochaRun } from '../../../util.js';
|
||||
import { mochaRun } from '../../../util/helpers.js';
|
||||
import { ForumTest } from './forum.test-util.js';
|
||||
|
||||
describe('Forum', function tests() {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { EPSILON, mochaRun } from '../../../util.js';
|
||||
import { mochaRun } from '../../../util/helpers.js';
|
||||
import { EPSILON } from '../../../util/constants.js';
|
||||
import { ForumTest } from './forum.test-util.js';
|
||||
|
||||
describe('Forum', function tests() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { mochaRun } from '../../../util.js';
|
||||
import { mochaRun } from '../../../util/helpers.js';
|
||||
import { ForumTest } from './forum.test-util.js';
|
||||
|
||||
describe('Forum', function tests() {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { INCINERATOR_ADDRESS, mochaRun } from '../../../util.js';
|
||||
import { mochaRun } from '../../../util/helpers.js';
|
||||
import { INCINERATOR_ADDRESS } from '../../../util/constants.js';
|
||||
import { ForumTest } from './forum.test-util.js';
|
||||
|
||||
describe('Forum', function tests() {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { INCINERATOR_ADDRESS, mochaRun } from '../../../util.js';
|
||||
import { mochaRun } from '../../../util/helpers.js';
|
||||
import { INCINERATOR_ADDRESS } from '../../../util/constants.js';
|
||||
import { ForumTest } from './forum.test-util.js';
|
||||
|
||||
describe('Forum', function tests() {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { Box } from '../../classes/display/box.js';
|
||||
import { Scene } from '../../classes/display/scene.js';
|
||||
import { Expert } from '../../classes/actors/expert.js';
|
||||
import { PostContent } from '../../classes/util/post-content.js';
|
||||
import { PostContent } from '../../classes/supporting/post-content.js';
|
||||
import { DAO } from '../../classes/dao/dao.js';
|
||||
import { delayOrWait } from '../../classes/display/controls.js';
|
||||
import { mochaRun } from '../../util.js';
|
||||
import { mochaRun } from '../../util/helpers.js';
|
||||
|
||||
const POOL_DURATION_MS = 100;
|
||||
const DEFAULT_DELAY_MS = 100;
|
||||
|
|
|
@ -2,7 +2,7 @@ import { Actor } from '../../classes/display/actor.js';
|
|||
import { Box } from '../../classes/display/box.js';
|
||||
import { Scene } from '../../classes/display/scene.js';
|
||||
import { VM } from '../../classes/supporting/vm.js';
|
||||
import { mochaRun } from '../../util.js';
|
||||
import { mochaRun } from '../../util/helpers.js';
|
||||
|
||||
const contractIds = ['contract-id-1', 'contract-id-2'];
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Box } from '../../classes/display/box.js';
|
||||
import { Scene } from '../../classes/display/scene.js';
|
||||
import { WDAG } from '../../classes/supporting/wdag.js';
|
||||
import { mochaRun } from '../../util.js';
|
||||
import { mochaRun } from '../../util/helpers.js';
|
||||
|
||||
const rootElement = document.getElementById('scene');
|
||||
const rootBox = new Box('rootBox', rootElement).flex();
|
||||
|
@ -15,11 +15,11 @@ describe('Query the graph', function tests() {
|
|||
before(() => {
|
||||
graph = (window.graph = new WDAG()).withFlowchart();
|
||||
|
||||
graph.addVertex({});
|
||||
graph.addVertex({});
|
||||
graph.addVertex({});
|
||||
graph.addVertex({});
|
||||
graph.addVertex({});
|
||||
graph.addVertex('v1', {});
|
||||
graph.addVertex('v1', {});
|
||||
graph.addVertex('v1', {});
|
||||
graph.addVertex('v1', {});
|
||||
graph.addVertex('v1', {});
|
||||
|
||||
graph.addEdge('e1', 0, 1, 1);
|
||||
graph.addEdge('e1', 2, 1, 0.5);
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
export const EPSILON = 2.23e-16;
|
||||
|
||||
export const INCINERATOR_ADDRESS = 0;
|
||||
|
||||
export const EdgeTypes = {
|
||||
CITATION: 'citation',
|
||||
BALANCE: 'balance',
|
||||
AUTHOR: 'author',
|
||||
};
|
||||
|
||||
export const VertexTypes = {
|
||||
POST: 'post',
|
||||
AUTHOR: 'author',
|
||||
};
|
|
@ -1,11 +1,5 @@
|
|||
import { CryptoUtil } from './classes/util/crypto.js';
|
||||
|
||||
const timers = new Map();
|
||||
|
||||
export const EPSILON = 2.23e-16;
|
||||
|
||||
export const INCINERATOR_ADDRESS = 0;
|
||||
|
||||
export const debounce = async (fn, delayMs) => {
|
||||
const timer = timers.get(fn);
|
||||
if (timer) {
|
||||
|
@ -39,7 +33,7 @@ export const displayNumber = (value, decimals = 2) => (value.toString().length >
|
|||
? value.toFixed(decimals)
|
||||
: value);
|
||||
|
||||
export const randomID = () => CryptoUtil.randomUUID().replaceAll('-', '').slice(0, 8);
|
||||
export const randomID = () => window.crypto.randomUUID().replaceAll('-', '').slice(0, 8);
|
||||
|
||||
export const mochaRun = () => {
|
||||
if (mocha._state !== 'running') {
|
Loading…
Reference in New Issue