Prepare forum implementation for further development
Includes minor refactoring of existing code / tests
This commit is contained in:
parent
03af7d4b10
commit
4c53d2a0f7
|
@ -0,0 +1,10 @@
|
|||
export class Token {
|
||||
constructor(ownerPublicKey) {
|
||||
this.ownerPublicKey = ownerPublicKey;
|
||||
}
|
||||
|
||||
transfer(newOwnerPublicKey) {
|
||||
// TODO: Current owner must sign this request
|
||||
this.ownerPublicKey = newOwnerPublicKey;
|
||||
}
|
||||
}
|
|
@ -84,6 +84,9 @@ export class ForumNode extends Actor {
|
|||
|
||||
// Process an incoming post, received by whatever means
|
||||
processPost(authorId, post, stake) {
|
||||
if (!post.id) {
|
||||
post.id = CryptoUtil.randomUUID();
|
||||
}
|
||||
this.actions.storePost.log(this, this, null, { authorId, post, stake });
|
||||
this.forumView.addPost(authorId, post.id, post, stake);
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ export class ForumView {
|
|||
const { citations = [], content } = postContent;
|
||||
const author = this.getOrInitializeAuthor(authorId);
|
||||
const postVertex = new PostVertex(postId, author, stake, content, citations);
|
||||
console.log('addPost', { id: postId, postContent });
|
||||
this.posts.addVertex(postId, postVertex);
|
||||
for (const citation of citations) {
|
||||
this.posts.addEdge('citation', postId, citation.postId, citation);
|
||||
|
|
|
@ -1,21 +1,18 @@
|
|||
import { Actor } from './actor.js';
|
||||
import { Graph, Vertex } from './graph.js';
|
||||
import { Graph } from './graph.js';
|
||||
import { CryptoUtil } from './crypto.js';
|
||||
import params from './params.js';
|
||||
import { Action } from './action.js';
|
||||
|
||||
class PostVertex extends Vertex {
|
||||
class Post extends Actor {
|
||||
constructor(forum, authorId, postContent) {
|
||||
super();
|
||||
this.forum = forum;
|
||||
this.id = postContent.id;
|
||||
const index = forum.posts.countVertices();
|
||||
const name = `Post${index + 1}`;
|
||||
super(name, forum.scene);
|
||||
this.id = postContent.id ?? CryptoUtil.randomUUID();
|
||||
this.authorId = authorId;
|
||||
this.citations = postContent.citations;
|
||||
this.value = 0;
|
||||
}
|
||||
|
||||
onValidate({ tokensMinted }) {
|
||||
this.value = params.initialPostValueFunction({ tokensMinted });
|
||||
this.forum.distributeReputation(this, this.value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,14 +23,19 @@ export class Forum extends Actor {
|
|||
super(name, scene);
|
||||
this.bench = bench;
|
||||
this.posts = new Graph();
|
||||
this.actions = {
|
||||
addPost: new Action('add post', scene),
|
||||
};
|
||||
}
|
||||
|
||||
async addPost(authorId, postContent) {
|
||||
const post = new PostVertex(this, authorId, postContent);
|
||||
const post = new Post(this, authorId, postContent);
|
||||
this.actions.addPost.log(this, post);
|
||||
this.posts.addVertex(post.id, post);
|
||||
for (const { postId: citedPostId, weight } of postContent.citations) {
|
||||
this.posts.addEdge('citation', post.id, citedPostId, { weight });
|
||||
}
|
||||
return post.id;
|
||||
}
|
||||
|
||||
getPost(postId) {
|
||||
|
@ -44,6 +46,11 @@ export class Forum extends Actor {
|
|||
return this.posts.getVertices();
|
||||
}
|
||||
|
||||
onValidate({ tokensMinted }) {
|
||||
const initialValue = params.initialPostValueFunction({ tokensMinted });
|
||||
this.distributeReputation(this, initialValue);
|
||||
}
|
||||
|
||||
distributeReputation(post, amount, depth = 0) {
|
||||
console.log('distributeReputation', { post, amount, depth });
|
||||
// Add the given value to the current post
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import mermaid from 'https://unpkg.com/mermaid@9.2.2/dist/mermaid.esm.min.mjs';
|
||||
import { debounce } from '../util.js';
|
||||
|
||||
export class Vertex {
|
||||
constructor(data) {
|
||||
this.data = data;
|
||||
|
@ -38,7 +41,11 @@ export class Graph {
|
|||
data = id;
|
||||
id = this.nextVertexId++;
|
||||
}
|
||||
if (this.vertices.has(id)) {
|
||||
throw new Error(`Vertex already exists with id: ${id}`);
|
||||
}
|
||||
const vertex = new Vertex(data);
|
||||
console.log('addVertex', vertex);
|
||||
this.vertices.set(id, vertex);
|
||||
return this;
|
||||
}
|
||||
|
@ -70,6 +77,7 @@ export class Graph {
|
|||
}
|
||||
|
||||
addEdge(label, from, to, data) {
|
||||
console.log('addEdge', { from, to });
|
||||
if (this.getEdge(label, from, to)) {
|
||||
throw new Error(`Edge ${label} from ${from} to ${to} already exists`);
|
||||
}
|
||||
|
@ -91,4 +99,24 @@ export class Graph {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
countVertices() {
|
||||
return this.vertices.size;
|
||||
}
|
||||
|
||||
async renderGraph() {
|
||||
const render = async () => {
|
||||
const dateStart = new Date();
|
||||
const graph = await mermaid.mermaidAPI.render(
|
||||
this.seqDiagramElement.getId(),
|
||||
this.logBox.getInnerText(),
|
||||
);
|
||||
this.seqDiagramBox.setInnerHTML(graph);
|
||||
if (!this.dateLastRender) {
|
||||
this.dateLastRender = new Date();
|
||||
}
|
||||
this.dateLastRender = dateStart;
|
||||
};
|
||||
debounce(render, 100);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ export class Member extends Actor {
|
|||
constructor(name, scene) {
|
||||
super(name, scene);
|
||||
this.actions = {
|
||||
submitPostViaNetwork: new Action('submit post via network', scene),
|
||||
submitPost: new Action('submit post', scene),
|
||||
initiateValidationPool: new Action('initiate validation pool', scene),
|
||||
castVote: new Action('cast vote', scene),
|
||||
|
@ -28,15 +29,22 @@ export class Member extends Actor {
|
|||
return this;
|
||||
}
|
||||
|
||||
async submitPost(forumNode, post, stake) {
|
||||
async submitPostViaNetwork(forumNode, post, stake) {
|
||||
// TODO: Include fee
|
||||
const postMessage = new PostMessage({ post, stake });
|
||||
console.log('submitPostViaNetwork', postMessage);
|
||||
await postMessage.sign(this.reputationKey);
|
||||
this.actions.submitPost.log(this, forumNode, null, { id: post.id });
|
||||
this.actions.submitPostViaNetwork.log(this, forumNode, null, { id: post.id });
|
||||
// For now, directly call forumNode.receiveMessage();
|
||||
await forumNode.receiveMessage(JSON.stringify(postMessage.toJSON()));
|
||||
}
|
||||
|
||||
async submitPost(forum, postContent) {
|
||||
// TODO: Include fee
|
||||
this.actions.submitPost.log(this, forum);
|
||||
return forum.addPost(this.reputationPublicKey, postContent);
|
||||
}
|
||||
|
||||
async initiateValidationPool(bench, options) {
|
||||
// For now, directly call bench.initiateValidationPool();
|
||||
const signingKey = await CryptoUtil.generateAsymmetricKey();
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { CryptoUtil } from './crypto.js';
|
||||
|
||||
export class Citation {
|
||||
constructor(postId, weight) {
|
||||
this.postId = postId;
|
||||
|
@ -20,7 +18,6 @@ export class Citation {
|
|||
|
||||
export class PostContent {
|
||||
constructor(content) {
|
||||
this.id = CryptoUtil.randomUUID();
|
||||
this.content = content;
|
||||
this.citations = [];
|
||||
}
|
||||
|
@ -33,16 +30,16 @@ export class PostContent {
|
|||
|
||||
toJSON() {
|
||||
return {
|
||||
id: this.id,
|
||||
content: this.content,
|
||||
citations: this.citations.map((citation) => citation.toJSON()),
|
||||
...(this.id ? { id: this.id } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
static fromJSON({ id, content, citations }) {
|
||||
const post = new PostContent(content);
|
||||
post.id = id;
|
||||
post.citations = citations.map((citation) => Citation.fromJSON(citation));
|
||||
post.id = id;
|
||||
return post;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,17 +69,8 @@ export class Scene {
|
|||
if (!this.dateLastRender) {
|
||||
this.dateLastRender = new Date();
|
||||
}
|
||||
console.log(
|
||||
`renderSequenceDiagram time: ${
|
||||
new Date() - dateStart
|
||||
} ms, time since last render: ${dateStart - this.dateLastRender}`,
|
||||
);
|
||||
this.dateLastRender = dateStart;
|
||||
};
|
||||
debounce(render, 100);
|
||||
}
|
||||
|
||||
insertSvg(svgCode) {
|
||||
this.seqDiagramElement.setInnerHTML(svgCode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,5 +13,6 @@
|
|||
<li><a href="/tests/mermaid.html">Mermaid</a></li>
|
||||
<li><a href="/tests/debounce.html">Debounce</a></li>
|
||||
<li><a href="/tests/availability.html">Availability</a></li>
|
||||
<li><a href="/tests/forum.html">Forum</a></li>
|
||||
</ul>
|
||||
</body>
|
||||
|
|
|
@ -55,14 +55,12 @@
|
|||
const requestor = new Public("Public", scene);
|
||||
|
||||
const updateDisplayValues = async () => {
|
||||
member1.setValue(
|
||||
for (const member of members) {
|
||||
member.setValue(
|
||||
"rep",
|
||||
bench.reputations.getTokens(member1.reputationPublicKey)
|
||||
);
|
||||
member2.setValue(
|
||||
"rep",
|
||||
bench.reputations.getTokens(member2.reputationPublicKey)
|
||||
bench.reputations.getTokens(member.reputationPublicKey)
|
||||
);
|
||||
}
|
||||
bench.setValue("total rep", bench.getTotalReputation());
|
||||
await scene.renderSequenceDiagram();
|
||||
};
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
import { Member } from "/classes/member.js";
|
||||
import { ForumNode } from "/classes/forum-node.js";
|
||||
import { ForumNetwork } from "/classes/forum-network.js";
|
||||
import { CryptoUtil } from "/classes/crypto.js";
|
||||
import { delay } from "/util.js";
|
||||
|
||||
const rootElement = document.getElementById("forum-network");
|
||||
|
@ -48,15 +49,24 @@
|
|||
// const blockchain = new Blockchain();
|
||||
|
||||
window.post1 = new PostContent({ message: "hi" });
|
||||
window.post1.id = CryptoUtil.randomUUID();
|
||||
window.post2 = new PostContent({ message: "hello" }).addCitation(
|
||||
window.post1.id,
|
||||
1.0
|
||||
);
|
||||
|
||||
await delay(1000);
|
||||
await window.author1.submitPost(window.forumNode1, window.post1, 50);
|
||||
await window.author1.submitPostViaNetwork(
|
||||
window.forumNode1,
|
||||
window.post1,
|
||||
50
|
||||
);
|
||||
await delay(1000);
|
||||
await window.author2.submitPost(window.forumNode2, window.post2, 100);
|
||||
await window.author2.submitPostViaNetwork(
|
||||
window.forumNode2,
|
||||
window.post2,
|
||||
100
|
||||
);
|
||||
|
||||
await delay(1000);
|
||||
clearInterval(processInterval);
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
<!DOCTYPE html>
|
||||
<head>
|
||||
<title>Forum test</title>
|
||||
<link type="text/css" rel="stylesheet" href="/index.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="forum-test"></div>
|
||||
</body>
|
||||
<script type="module">
|
||||
import { Box } from "/classes/box.js";
|
||||
import { Scene } from "/classes/scene.js";
|
||||
import { Member } from "/classes/member.js";
|
||||
import { Bench } from "/classes/bench.js";
|
||||
import { Business } from "/classes/business.js";
|
||||
import { Availability } from "/classes/availability.js";
|
||||
import { delay } from "/util.js";
|
||||
import { Forum } from "/classes/forum.js";
|
||||
import { Public } from "/classes/public.js";
|
||||
import { PostContent } from "/classes/post.js";
|
||||
|
||||
const DELAY_INTERVAL = 500;
|
||||
|
||||
const rootElement = document.getElementById("forum-test");
|
||||
const rootBox = new Box("rootBox", rootElement).flex();
|
||||
|
||||
const scene = (window.scene = new Scene("Forum test", rootBox).log(
|
||||
"sequenceDiagram"
|
||||
));
|
||||
|
||||
const members = (window.members = []);
|
||||
const newMember = async () => {
|
||||
const index = members.length;
|
||||
const name = `Member${index + 1}`;
|
||||
const member = await new Member(name, scene).initialize();
|
||||
members.push(member);
|
||||
return member;
|
||||
};
|
||||
|
||||
const posts = (window.posts = []);
|
||||
const newPost = async (author, content, citations) => {
|
||||
const postContent = new PostContent({ hello: "there" });
|
||||
const postId = await member1.submitPost(forum, postContent1);
|
||||
return postId;
|
||||
};
|
||||
|
||||
const member1 = await newMember();
|
||||
const member2 = await newMember();
|
||||
await newMember();
|
||||
const bench = (window.bench = new Bench("Bench", scene));
|
||||
const forum = (window.forum = new Forum(bench, "Forum", scene));
|
||||
|
||||
const updateDisplayValues = async () => {
|
||||
for (const member of members) {
|
||||
member.setValue(
|
||||
"rep",
|
||||
bench.reputations.getTokens(member.reputationPublicKey)
|
||||
);
|
||||
}
|
||||
bench.setValue("total rep", bench.getTotalReputation());
|
||||
await scene.renderSequenceDiagram();
|
||||
};
|
||||
|
||||
const updateDisplayValuesAndDelay = async () => {
|
||||
await updateDisplayValues();
|
||||
await delay(DELAY_INTERVAL);
|
||||
};
|
||||
|
||||
await updateDisplayValuesAndDelay();
|
||||
|
||||
const postId1 = await member1.submitPost(
|
||||
forum,
|
||||
new PostContent({ hello: "there" })
|
||||
);
|
||||
await updateDisplayValuesAndDelay();
|
||||
|
||||
const postId2 = await member1.submitPost(
|
||||
forum,
|
||||
new PostContent({ hello: "to you as well" }).addCitation(postId1, 0.5)
|
||||
);
|
||||
await updateDisplayValuesAndDelay();
|
||||
</script>
|
Loading…
Reference in New Issue