Add support for posts with multiple authors
This commit is contained in:
		
							parent
							
								
									0a954a01f3
								
							
						
					
					
						commit
						48491751dc
					
				| 
						 | 
					@ -5,18 +5,28 @@ import params from '../../params.js';
 | 
				
			||||||
import { ReputationHolder } from '../reputation/reputation-holder.js';
 | 
					import { ReputationHolder } from '../reputation/reputation-holder.js';
 | 
				
			||||||
import { displayNumber, EPSILON, INCINERATOR_ADDRESS } from '../../util.js';
 | 
					import { displayNumber, EPSILON, INCINERATOR_ADDRESS } from '../../util.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const CITATION = 'citation';
 | 
					const EdgeTypes = {
 | 
				
			||||||
const BALANCE = 'balance';
 | 
					  CITATION: 'citation',
 | 
				
			||||||
 | 
					  BALANCE: 'balance',
 | 
				
			||||||
 | 
					  AUTHORED_BY: 'authored by',
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const VertexTypes = {
 | 
				
			||||||
 | 
					  POST: 'post',
 | 
				
			||||||
 | 
					  AUTHOR: 'author',
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Post extends Actor {
 | 
					class Post extends Actor {
 | 
				
			||||||
  constructor(forum, authorPublicKey, postContent) {
 | 
					  constructor(forum, senderId, postContent) {
 | 
				
			||||||
    const index = forum.posts.countVertices();
 | 
					    const index = forum.graph.countVertices(VertexTypes.POST);
 | 
				
			||||||
    const name = `Post${index + 1}`;
 | 
					    const name = `Post${index + 1}`;
 | 
				
			||||||
    super(name, forum.scene);
 | 
					    super(name, forum.scene);
 | 
				
			||||||
 | 
					    this.forum = forum;
 | 
				
			||||||
    this.id = postContent.id ?? name;
 | 
					    this.id = postContent.id ?? name;
 | 
				
			||||||
    this.authorPublicKey = authorPublicKey;
 | 
					    this.senderId = senderId;
 | 
				
			||||||
    this.value = 0;
 | 
					    this.value = 0;
 | 
				
			||||||
    this.initialValue = 0;
 | 
					    this.initialValue = 0;
 | 
				
			||||||
 | 
					    this.authors = postContent.authors;
 | 
				
			||||||
    this.citations = postContent.citations;
 | 
					    this.citations = postContent.citations;
 | 
				
			||||||
    this.title = postContent.title;
 | 
					    this.title = postContent.title;
 | 
				
			||||||
    const leachingTotal = this.citations
 | 
					    const leachingTotal = this.citations
 | 
				
			||||||
| 
						 | 
					@ -25,6 +35,8 @@ class Post extends Actor {
 | 
				
			||||||
    const donationTotal = this.citations
 | 
					    const donationTotal = this.citations
 | 
				
			||||||
      .filter(({ weight }) => weight > 0)
 | 
					      .filter(({ weight }) => weight > 0)
 | 
				
			||||||
      .reduce((total, { weight }) => total += weight, 0);
 | 
					      .reduce((total, { weight }) => total += weight, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // TODO: Move evaluation of these parameters to Validation Pool
 | 
				
			||||||
    if (leachingTotal > params.revaluationLimit) {
 | 
					    if (leachingTotal > params.revaluationLimit) {
 | 
				
			||||||
      throw new Error('Post leaching total exceeds revaluation limit '
 | 
					      throw new Error('Post leaching total exceeds revaluation limit '
 | 
				
			||||||
        + `(${leachingTotal} > ${params.revaluationLimit})`);
 | 
					        + `(${leachingTotal} > ${params.revaluationLimit})`);
 | 
				
			||||||
| 
						 | 
					@ -49,6 +61,12 @@ class Post extends Actor {
 | 
				
			||||||
    </tr></table>`
 | 
					    </tr></table>`
 | 
				
			||||||
      .replaceAll(/\n\s*/g, '');
 | 
					      .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);
 | 
					    super(name, scene);
 | 
				
			||||||
    this.dao = dao;
 | 
					    this.dao = dao;
 | 
				
			||||||
    this.id = this.reputationPublicKey;
 | 
					    this.id = this.reputationPublicKey;
 | 
				
			||||||
    this.posts = new WDAG(scene);
 | 
					    this.graph = new WDAG(scene);
 | 
				
			||||||
    this.actions = {
 | 
					    this.actions = {
 | 
				
			||||||
      propagate: new Action('propagate', scene),
 | 
					      propagate: new Action('propagate', scene),
 | 
				
			||||||
      confirm: new Action('confirm', scene),
 | 
					      confirm: new Action('confirm', scene),
 | 
				
			||||||
| 
						 | 
					@ -69,32 +87,26 @@ export class Forum extends ReputationHolder {
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async addPost(authorId, postContent) {
 | 
					  async addPost(senderId, postContent) {
 | 
				
			||||||
    console.log('addPost', { authorId, postContent });
 | 
					    console.log('addPost', { senderId, postContent });
 | 
				
			||||||
    const post = new Post(this, authorId, postContent);
 | 
					    const post = new Post(this, senderId, postContent);
 | 
				
			||||||
    this.posts.addVertex(post.id, post, post.getLabel());
 | 
					    this.graph.addVertex(VertexTypes.POST, post.id, post, post.getLabel());
 | 
				
			||||||
    for (const { postId: citedPostId, weight } of post.citations) {
 | 
					    for (const { postId: citedPostId, weight } of post.citations) {
 | 
				
			||||||
      // Special case: Incinerator
 | 
					      // Special case: Incinerator
 | 
				
			||||||
      if (citedPostId === INCINERATOR_ADDRESS && !this.posts.getVertex(INCINERATOR_ADDRESS)) {
 | 
					      if (citedPostId === INCINERATOR_ADDRESS && !this.graph.getVertex(INCINERATOR_ADDRESS)) {
 | 
				
			||||||
        this.posts.addVertex(INCINERATOR_ADDRESS, { name: 'Incinerator' }, 'Incinerator');
 | 
					        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;
 | 
					    return post;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getPost(postId) {
 | 
					  getPost(postId) {
 | 
				
			||||||
    return this.posts.getVertexData(postId);
 | 
					    return this.graph.getVertexData(postId);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getPosts() {
 | 
					  getPosts() {
 | 
				
			||||||
    return this.posts.getVerticesData();
 | 
					    return this.graph.getVerticesData();
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  async setPostValue(post, value) {
 | 
					 | 
				
			||||||
    post.value = value;
 | 
					 | 
				
			||||||
    await post.setValue('value', value);
 | 
					 | 
				
			||||||
    this.posts.setVertexLabel(post.id, post.getLabel());
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getTotalValue() {
 | 
					  getTotalValue() {
 | 
				
			||||||
| 
						 | 
					@ -108,38 +120,74 @@ export class Forum extends ReputationHolder {
 | 
				
			||||||
  async onValidate({
 | 
					  async onValidate({
 | 
				
			||||||
    pool, postId, tokenId,
 | 
					    pool, postId, tokenId,
 | 
				
			||||||
  }) {
 | 
					  }) {
 | 
				
			||||||
 | 
					    console.log('onValidate', { pool, postId, tokenId });
 | 
				
			||||||
    const initialValue = this.dao.reputation.valueOf(tokenId);
 | 
					    const initialValue = this.dao.reputation.valueOf(tokenId);
 | 
				
			||||||
    const postVertex = this.posts.getVertex(postId);
 | 
					    const postVertex = this.graph.getVertex(postId);
 | 
				
			||||||
    const post = postVertex.data;
 | 
					    const post = postVertex.data;
 | 
				
			||||||
    post.setStatus('Validated');
 | 
					    post.setStatus('Validated');
 | 
				
			||||||
    post.initialValue = initialValue;
 | 
					    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,
 | 
					    const addAuthorToGraph = (publicKey, weight, authorTokenId) => {
 | 
				
			||||||
    // so that its value can be updated by future validated posts.
 | 
					      const authorVertex = this.graph.getVertex(publicKey)
 | 
				
			||||||
    post.tokenId = tokenId;
 | 
					        ?? 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();
 | 
					    const rewardsAccumulator = new Map();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Compute rewards
 | 
					    // Compute reputation rewards
 | 
				
			||||||
    await this.propagateValue(
 | 
					    await this.propagateValue(
 | 
				
			||||||
      { to: postVertex, from: { data: pool } },
 | 
					      { to: postVertex, from: { data: pool } },
 | 
				
			||||||
      { rewardsAccumulator, increment: initialValue },
 | 
					      { rewardsAccumulator, increment: initialValue },
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Apply computed rewards to update values of tokens
 | 
					    // Apply computed rewards to update values of tokens
 | 
				
			||||||
    for (const [id, value] of rewardsAccumulator) {
 | 
					    for (const [authorTokenId, amount] of rewardsAccumulator) {
 | 
				
			||||||
      if (value < 0) {
 | 
					      console.log('reward', { authorTokenId, amount });
 | 
				
			||||||
        this.dao.reputation.transferValueFrom(id, post.tokenId, -value);
 | 
					      // 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 {
 | 
					        } else {
 | 
				
			||||||
        this.dao.reputation.transferValueFrom(post.tokenId, id, value);
 | 
					          this.dao.reputation.transferValueFrom(tokenId, authorTokenId, amount);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Transfer ownership of the minted/staked token, from the posts to the post author
 | 
					    // Transfer ownership of the minted tokens to the authors
 | 
				
			||||||
    this.dao.reputation.transfer(this.id, post.authorPublicKey, post.tokenId);
 | 
					    for (const authorEdge of postVertex.getEdges(EdgeTypes.AUTHORED_BY, true)) {
 | 
				
			||||||
    // const toActor = this.scene?.findActor((actor) => actor.reputationPublicKey === post.authorPublicKey);
 | 
					      const authorVertex = authorEdge.to;
 | 
				
			||||||
    // const value = this.dao.reputation.valueOf(post.tokenId);
 | 
					      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,
 | 
					    rewardsAccumulator, increment, depth = 0, initialNegative = false,
 | 
				
			||||||
  }) {
 | 
					  }) {
 | 
				
			||||||
    const postVertex = edge.to;
 | 
					    const postVertex = edge.to;
 | 
				
			||||||
    const post = postVertex?.data;
 | 
					    const post = postVertex.data;
 | 
				
			||||||
    this.actions.propagate.log(edge.from.data, post, `(${increment})`);
 | 
					    this.actions.propagate.log(edge.from.data, post, `(${increment})`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!!params.referenceChainLimit && depth > params.referenceChainLimit) {
 | 
					    if (!!params.referenceChainLimit && depth > params.referenceChainLimit) {
 | 
				
			||||||
| 
						 | 
					@ -175,13 +223,14 @@ export class Forum extends ReputationHolder {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const propagate = async (positive) => {
 | 
					    const propagate = async (positive) => {
 | 
				
			||||||
      let totalOutboundAmount = 0;
 | 
					      let totalOutboundAmount = 0;
 | 
				
			||||||
      const citationEdges = postVertex.getEdges(CITATION, true)
 | 
					      const citationEdges = postVertex.getEdges(EdgeTypes.CITATION, true)
 | 
				
			||||||
        .filter(({ weight }) => (positive ? weight > 0 : weight < 0));
 | 
					        .filter(({ weight }) => (positive ? weight > 0 : weight < 0));
 | 
				
			||||||
      for (const citationEdge of citationEdges) {
 | 
					      for (const citationEdge of citationEdges) {
 | 
				
			||||||
        const { weight } = citationEdge;
 | 
					        const { weight } = citationEdge;
 | 
				
			||||||
        let outboundAmount = weight * increment;
 | 
					        let outboundAmount = weight * increment;
 | 
				
			||||||
        if (Math.abs(outboundAmount) > EPSILON) {
 | 
					        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;
 | 
					          let refundFromOutbound = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          // Special case: Incineration.
 | 
					          // 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,
 | 
					          // 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.
 | 
					          // 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;
 | 
					          totalOutboundAmount += outboundAmount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          this.actions.confirm.log(
 | 
					          this.actions.confirm.log(
 | 
				
			||||||
| 
						 | 
					@ -263,11 +317,15 @@ export class Forum extends ReputationHolder {
 | 
				
			||||||
      refundToInbound,
 | 
					      refundToInbound,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Award reputation to post author
 | 
					    // Apply reputation effects to post authors, not to the post directly
 | 
				
			||||||
    rewardsAccumulator.set(post.tokenId, appliedIncrement);
 | 
					    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
 | 
					    // Increment the value of the post
 | 
				
			||||||
    await this.setPostValue(post, newValue);
 | 
					    await post.setValue(newValue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return refundToInbound;
 | 
					    return refundToInbound;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -180,7 +180,7 @@ export class ValidationPool extends ReputationHolder {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Update computed display values
 | 
					      // Update computed display values
 | 
				
			||||||
      const actor = this.scene?.findActor((a) => a.reputationPublicKey === voter.reputationPublicKey);
 | 
					      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) {
 | 
					      if (!actor) {
 | 
				
			||||||
        throw new Error('Actor not found!');
 | 
					        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`);
 | 
					    this.scene?.stateToTable(`validation pool ${this.name} complete`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -58,12 +58,12 @@ export class Actor {
 | 
				
			||||||
    this.values.set(label, this.scene?.addDisplayValue(`${this.name} ${label}`));
 | 
					    this.values.set(label, this.scene?.addDisplayValue(`${this.name} ${label}`));
 | 
				
			||||||
    if (fn) {
 | 
					    if (fn) {
 | 
				
			||||||
      this.valueFunctions.set(label, fn);
 | 
					      this.valueFunctions.set(label, fn);
 | 
				
			||||||
      await this.computeValues();
 | 
					      await this.computeDisplayValues();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return this;
 | 
					    return this;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async setValue(label, value) {
 | 
					  async setDisplayValue(label, value) {
 | 
				
			||||||
    if (typeof value === 'function') {
 | 
					    if (typeof value === 'function') {
 | 
				
			||||||
      return this.addComputedValue(label, value);
 | 
					      return this.addComputedValue(label, value);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -76,10 +76,10 @@ export class Actor {
 | 
				
			||||||
    return this;
 | 
					    return this;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async computeValues() {
 | 
					  async computeDisplayValues() {
 | 
				
			||||||
    for (const [label, fn] of this.valueFunctions.entries()) {
 | 
					    for (const [label, fn] of this.valueFunctions.entries()) {
 | 
				
			||||||
      const value = fn();
 | 
					      const value = fn();
 | 
				
			||||||
      await this.setValue(label, value);
 | 
					      await this.setDisplayValue(label, value);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,14 +2,12 @@ import { Action } from '../display/action.js';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  Message, PostMessage, PeerMessage, messageFromJSON,
 | 
					  Message, PostMessage, PeerMessage, messageFromJSON,
 | 
				
			||||||
} from './message.js';
 | 
					} from './message.js';
 | 
				
			||||||
import { ForumView } from './forum-view.js';
 | 
					 | 
				
			||||||
import { NetworkNode } from './network-node.js';
 | 
					import { NetworkNode } from './network-node.js';
 | 
				
			||||||
import { randomID } from '../util/util.js';
 | 
					import { randomID } from '../../util.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class ForumNode extends NetworkNode {
 | 
					export class ForumNode extends NetworkNode {
 | 
				
			||||||
  constructor(name, scene) {
 | 
					  constructor(name, scene) {
 | 
				
			||||||
    super(name, scene);
 | 
					    super(name, scene);
 | 
				
			||||||
    this.forumView = new ForumView();
 | 
					 | 
				
			||||||
    this.actions = {
 | 
					    this.actions = {
 | 
				
			||||||
      ...this.actions,
 | 
					      ...this.actions,
 | 
				
			||||||
      storePost: new Action('store post', scene),
 | 
					      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();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -38,7 +38,8 @@ export class NetworkNode extends Actor {
 | 
				
			||||||
  // Enqueue it for further processing.
 | 
					  // Enqueue it for further processing.
 | 
				
			||||||
  async receiveMessage(messageStr) {
 | 
					  async receiveMessage(messageStr) {
 | 
				
			||||||
    const messageJson = JSON.parse(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);
 | 
					    this.queue.add(messageJson, senderReputation);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,6 +36,9 @@ export class ReputationTokenContract extends ERC721 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  incrementValue(tokenId, increment, context) {
 | 
					  incrementValue(tokenId, increment, context) {
 | 
				
			||||||
    const value = this.values.get(tokenId);
 | 
					    const value = this.values.get(tokenId);
 | 
				
			||||||
 | 
					    if (value === undefined) {
 | 
				
			||||||
 | 
					      throw new Error(`Token not found: ${tokenId}`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    const newValue = value + increment;
 | 
					    const newValue = value + increment;
 | 
				
			||||||
    const history = this.histories.get(tokenId) || [];
 | 
					    const history = this.histories.get(tokenId) || [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,6 +31,7 @@ export class ERC721 {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mint(to, tokenId) {
 | 
					  mint(to, tokenId) {
 | 
				
			||||||
 | 
					    console.log('ERC721.mint', { to, tokenId });
 | 
				
			||||||
    if (this.owners.get(tokenId)) {
 | 
					    if (this.owners.get(tokenId)) {
 | 
				
			||||||
      throw new Error('ERC721: token already minted');
 | 
					      throw new Error('ERC721: token already minted');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -60,9 +61,10 @@ export class ERC721 {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  transfer(from, to, tokenId) {
 | 
					  transfer(from, to, tokenId) {
 | 
				
			||||||
 | 
					    console.log('ERC721.transfer', { from, to, tokenId });
 | 
				
			||||||
    const owner = this.owners.get(tokenId);
 | 
					    const owner = this.owners.get(tokenId);
 | 
				
			||||||
    if (owner !== from) {
 | 
					    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(from, -1);
 | 
				
			||||||
    this.incrementBalance(to, 1);
 | 
					    this.incrementBalance(to, 1);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,7 @@
 | 
				
			||||||
export class Vertex {
 | 
					export class Vertex {
 | 
				
			||||||
  constructor(id, data) {
 | 
					  constructor(graph, type, id, data) {
 | 
				
			||||||
 | 
					    this.graph = graph;
 | 
				
			||||||
 | 
					    this.type = type;
 | 
				
			||||||
    this.id = id;
 | 
					    this.id = id;
 | 
				
			||||||
    this.data = data;
 | 
					    this.data = data;
 | 
				
			||||||
    this.edges = {
 | 
					    this.edges = {
 | 
				
			||||||
| 
						 | 
					@ -8,19 +10,20 @@ export class Vertex {
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getEdges(label, away) {
 | 
					  getEdges(type, away) {
 | 
				
			||||||
    return this.edges[away ? 'from' : 'to'].filter(
 | 
					    return this.edges[away ? 'from' : 'to'].filter(
 | 
				
			||||||
      (edge) => edge.label === label,
 | 
					      (edge) => edge.type === type,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Edge {
 | 
					export class Edge {
 | 
				
			||||||
  constructor(label, from, to, weight) {
 | 
					  constructor(type, from, to, weight, data) {
 | 
				
			||||||
    this.from = from;
 | 
					    this.from = from;
 | 
				
			||||||
    this.to = to;
 | 
					    this.to = to;
 | 
				
			||||||
    this.label = label;
 | 
					    this.type = type;
 | 
				
			||||||
    this.weight = weight;
 | 
					    this.weight = weight;
 | 
				
			||||||
 | 
					    this.data = data;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,7 +31,7 @@ export class WDAG {
 | 
				
			||||||
  constructor(scene) {
 | 
					  constructor(scene) {
 | 
				
			||||||
    this.scene = scene;
 | 
					    this.scene = scene;
 | 
				
			||||||
    this.vertices = new Map();
 | 
					    this.vertices = new Map();
 | 
				
			||||||
    this.edgeLabels = new Map();
 | 
					    this.edgeTypes = new Map();
 | 
				
			||||||
    this.nextVertexId = 0;
 | 
					    this.nextVertexId = 0;
 | 
				
			||||||
    this.flowchart = scene?.flowchart;
 | 
					    this.flowchart = scene?.flowchart;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -39,7 +42,11 @@ export class WDAG {
 | 
				
			||||||
    return this;
 | 
					    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
 | 
					    // Support simple case of auto-incremented numeric ids
 | 
				
			||||||
    if (typeof id === 'object') {
 | 
					    if (typeof id === 'object') {
 | 
				
			||||||
      data = id;
 | 
					      data = id;
 | 
				
			||||||
| 
						 | 
					@ -48,14 +55,10 @@ export class WDAG {
 | 
				
			||||||
    if (this.vertices.has(id)) {
 | 
					    if (this.vertices.has(id)) {
 | 
				
			||||||
      throw new Error(`Vertex already exists with id: ${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.vertices.set(id, vertex);
 | 
				
			||||||
    this.flowchart?.log(`${id}[${label ?? id}]`);
 | 
					    this.setVertexDisplayLabel(id, label ?? id);
 | 
				
			||||||
    return this;
 | 
					    return vertex;
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  setVertexLabel(id, label) {
 | 
					 | 
				
			||||||
    this.flowchart?.log(`${id}[${label}]`);
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getVertex(id) {
 | 
					  getVertex(id) {
 | 
				
			||||||
| 
						 | 
					@ -74,25 +77,25 @@ export class WDAG {
 | 
				
			||||||
    return btoa([from.id, to.id]).replaceAll(/[^A-Za-z0-9]+/g, '');
 | 
					    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);
 | 
					    from = from instanceof Vertex ? from : this.getVertex(from);
 | 
				
			||||||
    to = to instanceof Vertex ? to : this.getVertex(to);
 | 
					    to = to instanceof Vertex ? to : this.getVertex(to);
 | 
				
			||||||
    if (!from || !to) {
 | 
					    if (!from || !to) {
 | 
				
			||||||
      return undefined;
 | 
					      return undefined;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const edges = this.edgeLabels.get(label);
 | 
					    const edges = this.edgeTypes.get(type);
 | 
				
			||||||
    const edgeKey = WDAG.getEdgeKey({ from, to });
 | 
					    const edgeKey = WDAG.getEdgeKey({ from, to });
 | 
				
			||||||
    return edges?.get(edgeKey);
 | 
					    return edges?.get(edgeKey);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getEdgeWeight(label, from, to) {
 | 
					  getEdgeWeight(type, from, to) {
 | 
				
			||||||
    return this.getEdge(label, from, to)?.weight;
 | 
					    return this.getEdge(type, from, to)?.weight;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getEdgeHtml({ from, to }) {
 | 
					  getEdgeHtml({ from, to }) {
 | 
				
			||||||
    let html = '<table>';
 | 
					    let html = '<table>';
 | 
				
			||||||
    for (const { label, weight } of this.getEdges(null, from, to)) {
 | 
					    for (const { type, weight } of this.getEdges(null, from, to)) {
 | 
				
			||||||
      html += `<tr><td>${label}</td><td>${weight}</td></tr>`;
 | 
					      html += `<tr><td>${type}</td><td>${weight}</td></tr>`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    html += '</table>';
 | 
					    html += '</table>';
 | 
				
			||||||
    return html;
 | 
					    return html;
 | 
				
			||||||
| 
						 | 
					@ -103,14 +106,14 @@ export class WDAG {
 | 
				
			||||||
    return `${edgeKey}(${this.getEdgeHtml(edge)})`;
 | 
					    return `${edgeKey}(${this.getEdgeHtml(edge)})`;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  setEdgeWeight(label, from, to, weight) {
 | 
					  setEdgeWeight(type, from, to, weight, data) {
 | 
				
			||||||
    from = from instanceof Vertex ? from : this.getVertex(from);
 | 
					    from = from instanceof Vertex ? from : this.getVertex(from);
 | 
				
			||||||
    to = to instanceof Vertex ? to : this.getVertex(to);
 | 
					    to = to instanceof Vertex ? to : this.getVertex(to);
 | 
				
			||||||
    const edge = new Edge(label, from, to, weight);
 | 
					    const edge = new Edge(type, from, to, weight, data);
 | 
				
			||||||
    let edges = this.edgeLabels.get(label);
 | 
					    let edges = this.edgeTypes.get(type);
 | 
				
			||||||
    if (!edges) {
 | 
					    if (!edges) {
 | 
				
			||||||
      edges = new Map();
 | 
					      edges = new Map();
 | 
				
			||||||
      this.edgeLabels.set(label, edges);
 | 
					      this.edgeTypes.set(type, edges);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const edgeKey = WDAG.getEdgeKey(edge);
 | 
					    const edgeKey = WDAG.getEdgeKey(edge);
 | 
				
			||||||
    edges.set(edgeKey, edge);
 | 
					    edges.set(edgeKey, edge);
 | 
				
			||||||
| 
						 | 
					@ -118,26 +121,26 @@ export class WDAG {
 | 
				
			||||||
    return edge;
 | 
					    return edge;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  addEdge(label, from, to, weight) {
 | 
					  addEdge(type, from, to, weight, data) {
 | 
				
			||||||
    from = from instanceof Vertex ? from : this.getVertex(from);
 | 
					    from = from instanceof Vertex ? from : this.getVertex(from);
 | 
				
			||||||
    to = to instanceof Vertex ? to : this.getVertex(to);
 | 
					    to = to instanceof Vertex ? to : this.getVertex(to);
 | 
				
			||||||
    if (this.getEdge(label, from, to)) {
 | 
					    if (this.getEdge(type, from, to)) {
 | 
				
			||||||
      throw new Error(`Edge ${label} from ${from.id} to ${to.id} already exists`);
 | 
					      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);
 | 
					    from.edges.from.push(edge);
 | 
				
			||||||
    to.edges.to.push(edge);
 | 
					    to.edges.to.push(edge);
 | 
				
			||||||
    this.flowchart?.log(`${from.id} --- ${this.getEdgeFlowchartNode(edge)} --> ${to.id}`);
 | 
					    this.flowchart?.log(`${from.id} --- ${this.getEdgeFlowchartNode(edge)} --> ${to.id}`);
 | 
				
			||||||
    this.flowchart?.log(`class ${WDAG.getEdgeKey(edge)} edge`);
 | 
					    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);
 | 
					    from = from instanceof Vertex ? from : this.getVertex(from);
 | 
				
			||||||
    to = to instanceof Vertex ? to : this.getVertex(to);
 | 
					    to = to instanceof Vertex ? to : this.getVertex(to);
 | 
				
			||||||
    const edgeLabels = label ? [label] : Array.from(this.edgeLabels.keys());
 | 
					    const edgeTypes = type ? [type] : Array.from(this.edgeTypes.keys());
 | 
				
			||||||
    return edgeLabels.flatMap((edgeLabel) => {
 | 
					    return edgeTypes.flatMap((edgeType) => {
 | 
				
			||||||
      const edges = this.edgeLabels.get(edgeLabel);
 | 
					      const edges = this.edgeTypes.get(edgeType);
 | 
				
			||||||
      return Array.from(edges?.values() || []).filter((edge) => {
 | 
					      return Array.from(edges?.values() || []).filter((edge) => {
 | 
				
			||||||
        const matchFrom = from === null || from === undefined || from === edge.from;
 | 
					        const matchFrom = from === null || from === undefined || from === edge.from;
 | 
				
			||||||
        const matchTo = to === null || to === undefined || to === edge.to;
 | 
					        const matchTo = to === null || to === undefined || to === edge.to;
 | 
				
			||||||
| 
						 | 
					@ -146,7 +149,10 @@ export class WDAG {
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  countVertices() {
 | 
					  countVertices(type) {
 | 
				
			||||||
 | 
					    if (!type) {
 | 
				
			||||||
      return this.vertices.size;
 | 
					      return this.vertices.size;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    return Array.from(this.vertices.values()).filter((vertex) => vertex.type === type).length;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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) {
 | 
					  constructor(postId, weight) {
 | 
				
			||||||
    this.postId = postId;
 | 
					    this.postId = postId;
 | 
				
			||||||
    this.weight = weight;
 | 
					    this.weight = weight;
 | 
				
			||||||
| 
						 | 
					@ -17,11 +35,18 @@ export class Citation {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class PostContent {
 | 
					export class PostContent {
 | 
				
			||||||
  constructor(content) {
 | 
					  constructor(content = {}) {
 | 
				
			||||||
    this.content = content;
 | 
					    this.content = content;
 | 
				
			||||||
 | 
					    this.authors = [];
 | 
				
			||||||
    this.citations = [];
 | 
					    this.citations = [];
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  addAuthor(authorPublicKey, weight) {
 | 
				
			||||||
 | 
					    const author = new Author(authorPublicKey, weight);
 | 
				
			||||||
 | 
					    this.authors.push(author);
 | 
				
			||||||
 | 
					    return this;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  addCitation(postId, weight) {
 | 
					  addCitation(postId, weight) {
 | 
				
			||||||
    const citation = new Citation(postId, weight);
 | 
					    const citation = new Citation(postId, weight);
 | 
				
			||||||
    this.citations.push(citation);
 | 
					    this.citations.push(citation);
 | 
				
			||||||
| 
						 | 
					@ -36,6 +61,7 @@ export class PostContent {
 | 
				
			||||||
  toJSON() {
 | 
					  toJSON() {
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
      content: this.content,
 | 
					      content: this.content,
 | 
				
			||||||
 | 
					      authors: this.authors.map((author) => author.toJSON()),
 | 
				
			||||||
      citations: this.citations.map((citation) => citation.toJSON()),
 | 
					      citations: this.citations.map((citation) => citation.toJSON()),
 | 
				
			||||||
      ...(this.id ? { id: this.id } : {}),
 | 
					      ...(this.id ? { id: this.id } : {}),
 | 
				
			||||||
      title: this.title,
 | 
					      title: this.title,
 | 
				
			||||||
| 
						 | 
					@ -43,9 +69,10 @@ export class PostContent {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static fromJSON({
 | 
					  static fromJSON({
 | 
				
			||||||
    id, content, citations, title,
 | 
					    id, content, authors, citations, title,
 | 
				
			||||||
  }) {
 | 
					  }) {
 | 
				
			||||||
    const post = new PostContent(content);
 | 
					    const post = new PostContent(content);
 | 
				
			||||||
 | 
					    post.authors = authors.map((author) => Author.fromJSON(author));
 | 
				
			||||||
    post.citations = citations.map((citation) => Citation.fromJSON(citation));
 | 
					    post.citations = citations.map((citation) => Citation.fromJSON(citation));
 | 
				
			||||||
    post.id = id;
 | 
					    post.id = id;
 | 
				
			||||||
    post.title = title;
 | 
					    post.title = title;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,7 +28,7 @@
 | 
				
			||||||
  const actor2 = new Actor('B', scene);
 | 
					  const actor2 = new Actor('B', scene);
 | 
				
			||||||
  const action1 = new Action('Action 1', scene);
 | 
					  const action1 = new Action('Action 1', scene);
 | 
				
			||||||
  await action1.log(actor1, actor2);
 | 
					  await action1.log(actor1, actor2);
 | 
				
			||||||
  await actor1.setValue('value', 1);
 | 
					  await actor1.setDisplayValue('value', 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  await scene.withFlowchart();
 | 
					  await scene.withFlowchart();
 | 
				
			||||||
  await scene.flowchart.log('A --> B');
 | 
					  await scene.flowchart.log('A --> B');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,7 +18,7 @@ const newExpert = async () => {
 | 
				
			||||||
  const index = experts.length;
 | 
					  const index = experts.length;
 | 
				
			||||||
  const name = `Expert${index + 1}`;
 | 
					  const name = `Expert${index + 1}`;
 | 
				
			||||||
  const expert = await new Expert(dao, name, scene).initialize();
 | 
					  const expert = await new Expert(dao, name, scene).initialize();
 | 
				
			||||||
  expert.setValue(
 | 
					  expert.setDisplayValue(
 | 
				
			||||||
    'rep',
 | 
					    'rep',
 | 
				
			||||||
    () => dao.reputation.valueOwnedBy(expert.reputationPublicKey),
 | 
					    () => dao.reputation.valueOwnedBy(expert.reputationPublicKey),
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
| 
						 | 
					@ -36,7 +36,7 @@ const setup = async () => {
 | 
				
			||||||
  scene.withTable();
 | 
					  scene.withTable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  dao = new DAO('DGF', scene);
 | 
					  dao = new DAO('DGF', scene);
 | 
				
			||||||
  await dao.setValue('total rep', () => dao.reputation.getTotal());
 | 
					  await dao.setDisplayValue('total rep', () => dao.reputation.getTotal());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  experts = [];
 | 
					  experts = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,7 +24,10 @@ export class ForumTest {
 | 
				
			||||||
    const title = `posts[${postIndex}]`;
 | 
					    const title = `posts[${postIndex}]`;
 | 
				
			||||||
    await this.scene.sequence.startSection();
 | 
					    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) {
 | 
					    for (const { postId, weight } of citations) {
 | 
				
			||||||
      postContent.addCitation(postId, weight);
 | 
					      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 value', () => this.dao.reputation.getTotal());
 | 
				
			||||||
    // await this.dao.addComputedValue('total reputation', () => this.dao.forum.getTotalValue());
 | 
					    // await this.dao.addComputedValue('total reputation', () => this.dao.forum.getTotalValue());
 | 
				
			||||||
    this.dao.computeValues();
 | 
					    this.dao.computeDisplayValues();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,11 +15,11 @@ describe('Query the graph', function tests() {
 | 
				
			||||||
  before(() => {
 | 
					  before(() => {
 | 
				
			||||||
    graph = (window.graph = new WDAG()).withFlowchart();
 | 
					    graph = (window.graph = new WDAG()).withFlowchart();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    graph.addVertex({});
 | 
					    graph.addVertex('v1', {});
 | 
				
			||||||
    graph.addVertex({});
 | 
					    graph.addVertex('v1', {});
 | 
				
			||||||
    graph.addVertex({});
 | 
					    graph.addVertex('v1', {});
 | 
				
			||||||
    graph.addVertex({});
 | 
					    graph.addVertex('v1', {});
 | 
				
			||||||
    graph.addVertex({});
 | 
					    graph.addVertex('v1', {});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    graph.addEdge('e1', 0, 1, 1);
 | 
					    graph.addEdge('e1', 0, 1, 1);
 | 
				
			||||||
    graph.addEdge('e1', 2, 1, 0.5);
 | 
					    graph.addEdge('e1', 2, 1, 0.5);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue