diff --git a/forum-network/notes/rep.md b/forum-network/notes/rep.md index 0574402..018ca2c 100644 --- a/forum-network/notes/rep.md +++ b/forum-network/notes/rep.md @@ -50,9 +50,3 @@ At a minimum this can be a list where each item includes the identifier of the c Each validation pool can then keep a record of the reputation staked by each voter, and the identifier of the corresponding post. --- - -So: If some incoming reputation can’t be propagated due to a constraint like this, the question is what should happen with that unspent power. I think right now, I have it so when that limit is reached on leaching from PostA, the extra reputation accrues evenly (because of +0.5 citation) equally to PostX and PostZ. - -Intuitively, we might want less value accruing to PostX, as it seems intended as a small utility post to transfer power. - -However, we can combine these mechanisms. PostX diff --git a/forum-network/src/classes/action.js b/forum-network/src/classes/action.js index ad2e628..0d9c5c4 100644 --- a/forum-network/src/classes/action.js +++ b/forum-network/src/classes/action.js @@ -5,11 +5,10 @@ export class Action { } async log(src, dest, msg, obj, symbol = '->>') { - const logObj = false; if (this.scene.sequence) { await this.scene.sequence.log( `${src.name} ${symbol} ${dest.name} : ${this.name} ${msg ?? ''} ${ - logObj && obj ? JSON.stringify(obj) : '' + JSON.stringify(obj) ?? '' }`, ); } diff --git a/forum-network/src/classes/forum.js b/forum-network/src/classes/forum.js index d5010e0..c2081b4 100644 --- a/forum-network/src/classes/forum.js +++ b/forum-network/src/classes/forum.js @@ -4,7 +4,7 @@ import { Action } from './action.js'; import { CryptoUtil } from './crypto.js'; import params from '../params.js'; import { ReputationHolder } from './reputation-holder.js'; -import { displayNumber } from '../util.js'; +import { displayNumber, EPSILON } from '../util.js'; const CITATION = 'citation'; const BALANCE = 'balance'; @@ -20,10 +20,19 @@ class Post extends Actor { this.initialValue = 0; this.citations = postContent.citations; this.title = postContent.title; - const revaluationTotal = this.citations.reduce((total, { weight }) => total += Math.abs(weight), 0); - if (revaluationTotal > params.revaluationLimit) { - throw new Error('Post revaluation total exceeds revaluation limit ' - + `(${revaluationTotal} > ${params.revaluationLimit})`); + const leachingTotal = this.citations + .filter(({ weight }) => weight < 0) + .reduce((total, { weight }) => total += -weight, 0); + const donationTotal = this.citations + .filter(({ weight }) => weight > 0) + .reduce((total, { weight }) => total += weight, 0); + if (leachingTotal > params.revaluationLimit) { + throw new Error('Post leaching total exceeds revaluation limit ' + + `(${leachingTotal} > ${params.revaluationLimit})`); + } + if (donationTotal > params.revaluationLimit) { + throw new Error('Post donation total exceeds revaluation limit ' + + `(${donationTotal} > ${params.revaluationLimit})`); } if (this.citations.some(({ weight }) => Math.abs(weight) > 1)) { throw new Error('Each citation weight must be in the range [-1, 1]'); @@ -130,11 +139,24 @@ export class Forum extends ReputationHolder { * @param {Edge} edge * @param {Object} opaqueData */ - async propagateValue(edge, { rewardsAccumulator, increment, depth = 0 }) { + async propagateValue(edge, { + rewardsAccumulator, increment, depth = 0, initialNegative = false, + }) { const postVertex = edge.to; const post = postVertex?.data; + const balanceFromInbound = this.posts.getEdgeWeight(BALANCE, edge.from, edge.to) ?? 0; + this.actions.propagateValue.log(edge.from.data, post, `(${increment}) balance (${balanceFromInbound})`); - this.actions.propagateValue.log(edge.from.data, post, `(${increment})`); + if (!!params.referenceChainLimit && depth > params.referenceChainLimit) { + this.actions.propagateValue.log( + edge.from.data, + post, + `referenceChainLimit (${params.referenceChainLimit}) reached`, + null, + '-x', + ); + return increment; + } console.log('propagateValue start', { from: edge.from.id, @@ -142,58 +164,37 @@ export class Forum extends ReputationHolder { depth, value: post.value, increment, + balanceFromInbound, + initialNegative, }); - if (params.referenceChainLimit === null || depth <= params.referenceChainLimit) { - let totalOutboundAmount = 0; - let totalRefundFromOutbound = 0; - for (const citationEdge of postVertex.getEdges(CITATION, true)) { - const { to: citedPostVertex, weight } = citationEdge; - let outboundAmount = weight * increment; - const balanceEdge = this.posts.getEdge(BALANCE, postVertex, citedPostVertex); - const balance = balanceEdge?.weight ?? 0; - // We need to ensure that we propagate no more reputation than we leached - if (depth > 0 && weight < 0) { - outboundAmount = outboundAmount < 0 - ? Math.max(outboundAmount, -balance) - : Math.min(outboundAmount, -balance); - } + let totalOutboundAmount = 0; + + for (const citationEdge of postVertex.getEdges(CITATION, true)) { + const { weight } = citationEdge; + let outboundAmount = weight * increment; + const balanceToOutbound = this.posts.getEdgeWeight(BALANCE, citationEdge.from, citationEdge.to) ?? 0; + // We need to ensure that we propagate no more reputation than we leached + if (initialNegative) { + outboundAmount = outboundAmount < 0 + ? Math.max(outboundAmount, -balanceToOutbound) + : Math.min(outboundAmount, -balanceToOutbound); + } + if (Math.abs(outboundAmount) > EPSILON) { const refundFromOutbound = await this.propagateValue(citationEdge, { rewardsAccumulator, increment: outboundAmount, depth: depth + 1, + initialNegative: initialNegative || (depth === 0 && outboundAmount < 0), }); - totalRefundFromOutbound += Math.abs(refundFromOutbound); outboundAmount -= refundFromOutbound; - this.posts.setEdgeWeight(BALANCE, postVertex, citedPostVertex, balance + outboundAmount); - + this.posts.setEdgeWeight(BALANCE, citationEdge.from, citationEdge.to, balanceToOutbound + outboundAmount); totalOutboundAmount += outboundAmount; } - - // Now that we know what value could not be propagated as negative citations, - // Let's see what happens if we redistribute it among the positive citations :) - - const positiveCitations = postVertex.getEdges(CITATION, true).filter(({ weight }) => weight > 0); - const totalPositiveCitationWeight = positiveCitations.reduce((total, { weight }) => total += weight, 0); - - for (const citationEdge of positiveCitations) { - const { to: citedPostVertex, weight } = citationEdge; - const outboundAmount = totalRefundFromOutbound * (weight / totalPositiveCitationWeight); - const balance = this.posts.getEdgeWeight(BALANCE, postVertex, citedPostVertex) ?? 0; - await this.propagateValue(citationEdge, { - rewardsAccumulator, - increment: outboundAmount, - depth: depth + 1, - }); - this.posts.setEdgeWeight(BALANCE, postVertex, citedPostVertex, balance + outboundAmount); - totalOutboundAmount += outboundAmount; - } - - // And what if they don't have any positive citations? The value should just accrue to this post. - - increment -= totalOutboundAmount * params.leachingValue; } + increment -= totalOutboundAmount * params.leachingValue; + const rawNewValue = post.value + increment; const newValue = Math.max(0, rawNewValue); const appliedIncrement = newValue - post.value; diff --git a/forum-network/src/classes/reputation-token.js b/forum-network/src/classes/reputation-token.js index e86d33c..76ab770 100644 --- a/forum-network/src/classes/reputation-token.js +++ b/forum-network/src/classes/reputation-token.js @@ -1,7 +1,7 @@ import { ERC721 } from './erc721.js'; import { CryptoUtil } from './crypto.js'; -const EPSILON = 2.23e-16; +import { EPSILON } from '../util.js'; class Lock { constructor(tokenId, amount, duration) { diff --git a/forum-network/src/classes/scene.js b/forum-network/src/classes/scene.js index 7afd507..0b0f082 100644 --- a/forum-network/src/classes/scene.js +++ b/forum-network/src/classes/scene.js @@ -135,7 +135,6 @@ export class Scene { const logBox = this.box.addBox('Flowchart text').addClass('dim'); this.flowchart = new MermaidDiagram(box, logBox); this.flowchart.log(`graph ${direction}`, false); - this.flowchart.log(`classDef edge fill:${this.options.edgeNodeColor}`, false); return this; } @@ -148,7 +147,6 @@ export class Scene { const logBox = container.addBox('Flowchart text').addClass('dim'); const flowchart = new MermaidDiagram(box, logBox); flowchart.log(`graph ${direction}`, false); - this.flowchart.log(`classDef edge fill:${this.options.edgeNodeColor}`, false); this.flowcharts.set(id, flowchart); return this; } diff --git a/forum-network/src/classes/wdag.js b/forum-network/src/classes/wdag.js index ace5beb..06e3dc0 100644 --- a/forum-network/src/classes/wdag.js +++ b/forum-network/src/classes/wdag.js @@ -70,34 +70,25 @@ export class WDAG { return Array.from(this.vertices.values()).map(({ data }) => data); } - getEdgeLabel(label) { - return this.edgeLabels.get(label); - } - - getOrCreateEdgeLabel(label) { - let edges = this.getEdgeLabel(label); - if (!edges) { - edges = new Map(); - this.edgeLabels.set(label, edges); - } - return edges; + static getEdgeKey({ from, to }) { + return btoa([from.id, to.id]).replaceAll(/[^A-Z]+/g, ''); } getEdge(label, 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); - return edges?.get(JSON.stringify([from.id, to.id])); + const edgeKey = WDAG.getEdgeKey({ from, to }); + return edges?.get(edgeKey); } getEdgeWeight(label, from, to) { return this.getEdge(label, from, to)?.weight; } - static getEdgeKey({ from, to }) { - return btoa([from.id, to.id]).replaceAll(/[^A-Z]+/g, ''); - } - getEdgeHtml({ from, to }) { let html = ''; for (const { label, weight } of this.getEdges(null, from, to)) { @@ -109,7 +100,7 @@ export class WDAG { getEdgeFlowchartNode(edge) { const edgeKey = WDAG.getEdgeKey(edge); - return `${edgeKey}(${this.getEdgeHtml(edge)}):::edge`; + return `${edgeKey}(${this.getEdgeHtml(edge)})`; } setEdgeWeight(label, from, to, weight) { @@ -137,6 +128,7 @@ export class WDAG { 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; } diff --git a/forum-network/src/index.css b/forum-network/src/index.css index 262dc7c..bb9c03f 100644 --- a/forum-network/src/index.css +++ b/forum-network/src/index.css @@ -45,3 +45,6 @@ th { td { background-color: #0c2025; } +.edge > rect { + fill: #216262 !important; +} diff --git a/forum-network/src/index.html b/forum-network/src/index.html index 2cc69ea..e4bd968 100644 --- a/forum-network/src/index.html +++ b/forum-network/src/index.html @@ -12,7 +12,7 @@
  • Forum