From 48491751dc1d8d449449244761ecb83c6c0fecc9 Mon Sep 17 00:00:00 2001 From: Ladd Hoffman Date: Sat, 15 Apr 2023 11:50:57 -0500 Subject: [PATCH] Add support for posts with multiple authors --- forum-network/src/classes/dao/forum.js | 144 ++++++++++++------ .../src/classes/dao/validation-pool.js | 6 +- forum-network/src/classes/display/actor.js | 8 +- .../src/classes/forum-network/forum-node.js | 4 +- .../src/classes/forum-network/forum-view.js | 67 -------- .../src/classes/forum-network/network-node.js | 3 +- .../classes/reputation/reputation-token.js | 5 +- .../src/classes/supporting/erc721.js | 4 +- forum-network/src/classes/supporting/wdag.js | 76 ++++----- .../src/classes/util/post-content.js | 33 +++- forum-network/src/tests/flowchart.test.html | 2 +- .../src/tests/scripts/availability.test.js | 4 +- .../tests/scripts/forum/forum.test-util.js | 7 +- forum-network/src/tests/scripts/wdag.test.js | 10 +- 14 files changed, 202 insertions(+), 171 deletions(-) delete mode 100644 forum-network/src/classes/forum-network/forum-view.js diff --git a/forum-network/src/classes/dao/forum.js b/forum-network/src/classes/dao/forum.js index 4a87a26..4b3dc9d 100644 --- a/forum-network/src/classes/dao/forum.js +++ b/forum-network/src/classes/dao/forum.js @@ -5,18 +5,28 @@ 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'; +const EdgeTypes = { + CITATION: 'citation', + BALANCE: 'balance', + AUTHORED_BY: 'authored by', +}; + +const VertexTypes = { + POST: 'post', + AUTHOR: 'author', +}; 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 +35,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 +61,12 @@ class Post extends Actor { ` .replaceAll(/\n\s*/g, ''); } + + async setValue(value) { + this.value = value; + await this.setDisplayValue('value', value); + this.forum.graph.setVertexDisplayLabel(this.id, this.getLabel()); + } } /** @@ -61,7 +79,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,32 +87,26 @@ 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() { @@ -108,38 +120,74 @@ export class Forum extends ReputationHolder { 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()); + this.graph.setVertexDisplayLabel(post.id, 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) => { + const authorVertex = this.graph.getVertex(publicKey) + ?? this.graph.addVertex(VertexTypes.AUTHOR, publicKey, { name: publicKey, publicKey }, publicKey); + const authorEdge = this.graph.addEdge( + EdgeTypes.AUTHORED_BY, + postVertex, + authorVertex, + weight, + { tokenId: authorTokenId }, + ); + console.log('addAuthorToGraph', { authorVertex, authorEdge }); + }; + + // In the case of multiple authors, mint additional (empty) tokens. + // If no authors are specified, treat the sender as the sole author. + 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. + if (publicKey === post.senderId) { + addAuthorToGraph(publicKey, weight, tokenId); + } else { + addAuthorToGraph(publicKey, weight, this.dao.reputation.mint(this.id, 0)); + } + } + } + + // TODO: Verify that cumulative author weight === 1 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); - } else { - this.dao.reputation.transferValueFrom(post.tokenId, id, value); + for (const [authorTokenId, amount] of rewardsAccumulator) { + console.log('reward', { authorTokenId, amount }); + // 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(tokenId, authorTokenId, amount); + } } } - // 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); + // Transfer ownership of the minted tokens to the authors + for (const authorEdge of postVertex.getEdges(EdgeTypes.AUTHORED_BY, true)) { + const authorVertex = authorEdge.to; + const { publicKey } = authorVertex.data; + const { tokenId: authorTokenId } = authorEdge.data; + this.dao.reputation.transfer(this.id, publicKey, authorTokenId); + } } /** @@ -150,7 +198,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) { @@ -175,13 +223,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. @@ -225,7 +274,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( @@ -263,11 +317,15 @@ export class Forum extends ReputationHolder { refundToInbound, }); - // Award reputation to post author - rewardsAccumulator.set(post.tokenId, appliedIncrement); + // Apply reputation effects to post authors, not to the post directly + for (const authorEdge of postVertex.getEdges(EdgeTypes.AUTHORED_BY, true)) { + const { weight, data: { tokenId } } = authorEdge; + const authorIncrement = weight * appliedIncrement; + rewardsAccumulator.set(tokenId, authorIncrement); + } // Increment the value of the post - await this.setPostValue(post, newValue); + await post.setValue(newValue); return refundToInbound; } diff --git a/forum-network/src/classes/dao/validation-pool.js b/forum-network/src/classes/dao/validation-pool.js index d288b29..6348cfa 100644 --- a/forum-network/src/classes/dao/validation-pool.js +++ b/forum-network/src/classes/dao/validation-pool.js @@ -180,7 +180,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 +235,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`); diff --git a/forum-network/src/classes/display/actor.js b/forum-network/src/classes/display/actor.js index efdd08c..046f923 100644 --- a/forum-network/src/classes/display/actor.js +++ b/forum-network/src/classes/display/actor.js @@ -58,12 +58,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 +76,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); } } diff --git a/forum-network/src/classes/forum-network/forum-node.js b/forum-network/src/classes/forum-network/forum-node.js index 49ae7c5..b262357 100644 --- a/forum-network/src/classes/forum-network/forum-node.js +++ b/forum-network/src/classes/forum-network/forum-node.js @@ -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.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), diff --git a/forum-network/src/classes/forum-network/forum-view.js b/forum-network/src/classes/forum-network/forum-view.js deleted file mode 100644 index a7e19a0..0000000 --- a/forum-network/src/classes/forum-network/forum-view.js +++ /dev/null @@ -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(); - } -} diff --git a/forum-network/src/classes/forum-network/network-node.js b/forum-network/src/classes/forum-network/network-node.js index 72072b9..345e3ad 100644 --- a/forum-network/src/classes/forum-network/network-node.js +++ b/forum-network/src/classes/forum-network/network-node.js @@ -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); } diff --git a/forum-network/src/classes/reputation/reputation-token.js b/forum-network/src/classes/reputation/reputation-token.js index 6a479e1..a48b00e 100644 --- a/forum-network/src/classes/reputation/reputation-token.js +++ b/forum-network/src/classes/reputation/reputation-token.js @@ -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) || []; @@ -60,7 +63,7 @@ export class ReputationTokenContract extends ERC721 { const sourceAvailable = this.availableValueOf(fromTokenId); if (sourceAvailable < amount - EPSILON) { throw new Error('Token value transfer: source has insufficient available value. ' - + `Needs ${amount}; has ${sourceAvailable}.`); + + `Needs ${amount}; has ${sourceAvailable}.`); } this.incrementValue(fromTokenId, -amount); this.incrementValue(toTokenId, amount); diff --git a/forum-network/src/classes/supporting/erc721.js b/forum-network/src/classes/supporting/erc721.js index 5b2b215..9a68166 100644 --- a/forum-network/src/classes/supporting/erc721.js +++ b/forum-network/src/classes/supporting/erc721.js @@ -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); diff --git a/forum-network/src/classes/supporting/wdag.js b/forum-network/src/classes/supporting/wdag.js index bec7a34..1b96124 100644 --- a/forum-network/src/classes/supporting/wdag.js +++ b/forum-network/src/classes/supporting/wdag.js @@ -1,5 +1,7 @@ export class Vertex { - constructor(id, data) { + constructor(graph, type, id, data) { + this.graph = graph; + this.type = type; this.id = id; this.data = data; this.edges = { @@ -8,19 +10,20 @@ export class Vertex { }; } - getEdges(label, away) { + getEdges(type, away) { return this.edges[away ? 'from' : 'to'].filter( - (edge) => edge.label === label, + (edge) => edge.type === type, ); } } export class Edge { - constructor(label, from, to, weight) { + constructor(type, from, to, weight, data) { this.from = from; this.to = to; - this.label = label; + this.type = type; this.weight = weight; + this.data = data; } } @@ -28,7 +31,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 +42,11 @@ export class WDAG { return this; } - addVertex(id, data, label) { + setVertexDisplayLabel(id, label) { + this.flowchart?.log(`${id}[${label}]`); + } + + addVertex(type, id, data, label) { // Support simple case of auto-incremented numeric ids if (typeof id === 'object') { data = id; @@ -48,14 +55,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); this.vertices.set(id, vertex); - this.flowchart?.log(`${id}[${label ?? id}]`); - return this; - } - - setVertexLabel(id, label) { - this.flowchart?.log(`${id}[${label}]`); + this.setVertexDisplayLabel(id, label ?? id); + return vertex; } getVertex(id) { @@ -74,25 +77,25 @@ export class WDAG { 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 edges = this.edgeTypes.get(type); const edgeKey = WDAG.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 = ''; - for (const { label, weight } of this.getEdges(null, from, to)) { - html += ``; + for (const { type, weight } of this.getEdges(null, from, to)) { + html += ``; } html += '
${label}${weight}
${type}${weight}
'; return html; @@ -103,14 +106,14 @@ export class WDAG { return `${edgeKey}(${this.getEdgeHtml(edge)})`; } - setEdgeWeight(label, from, to, weight) { + setEdgeWeight(type, from, to, weight, data) { 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(type, from, to, weight, data); + 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); edges.set(edgeKey, edge); @@ -118,26 +121,26 @@ export class WDAG { return edge; } - addEdge(label, from, to, weight) { + addEdge(type, from, to, weight, data) { 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); 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; + 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 +149,10 @@ export class WDAG { }); } - countVertices() { - return this.vertices.size; + countVertices(type) { + if (!type) { + return this.vertices.size; + } + return Array.from(this.vertices.values()).filter((vertex) => vertex.type === type).length; } } diff --git a/forum-network/src/classes/util/post-content.js b/forum-network/src/classes/util/post-content.js index 8512f3f..5308d36 100644 --- a/forum-network/src/classes/util/post-content.js +++ b/forum-network/src/classes/util/post-content.js @@ -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; diff --git a/forum-network/src/tests/flowchart.test.html b/forum-network/src/tests/flowchart.test.html index a252871..97f9021 100644 --- a/forum-network/src/tests/flowchart.test.html +++ b/forum-network/src/tests/flowchart.test.html @@ -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'); diff --git a/forum-network/src/tests/scripts/availability.test.js b/forum-network/src/tests/scripts/availability.test.js index 556da23..b4e1d37 100644 --- a/forum-network/src/tests/scripts/availability.test.js +++ b/forum-network/src/tests/scripts/availability.test.js @@ -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 = []; diff --git a/forum-network/src/tests/scripts/forum/forum.test-util.js b/forum-network/src/tests/scripts/forum/forum.test-util.js index fea2e8a..10973ec 100644 --- a/forum-network/src/tests/scripts/forum/forum.test-util.js +++ b/forum-network/src/tests/scripts/forum/forum.test-util.js @@ -24,7 +24,10 @@ export class ForumTest { const title = `posts[${postIndex}]`; await this.scene.sequence.startSection(); - const postContent = new PostContent({}).setTitle(title); + const postContent = new PostContent().setTitle(title); + + postContent.addAuthor(author.reputationPublicKey, 1); + for (const { postId, weight } of citations) { postContent.addCitation(postId, weight); } @@ -82,6 +85,6 @@ export class ForumTest { await this.dao.addComputedValue('total value', () => this.dao.reputation.getTotal()); // await this.dao.addComputedValue('total reputation', () => this.dao.forum.getTotalValue()); - this.dao.computeValues(); + this.dao.computeDisplayValues(); } } diff --git a/forum-network/src/tests/scripts/wdag.test.js b/forum-network/src/tests/scripts/wdag.test.js index 4fd1243..439dd17 100644 --- a/forum-network/src/tests/scripts/wdag.test.js +++ b/forum-network/src/tests/scripts/wdag.test.js @@ -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);