Cleanup related to forum network test
Also misc minor stub additions
This commit is contained in:
parent
c24952497d
commit
ab63ad1868
|
@ -0,0 +1,43 @@
|
||||||
|
# Primary
|
||||||
|
|
||||||
|
## Forum
|
||||||
|
|
||||||
|
## ValidationPool
|
||||||
|
|
||||||
|
## ReputationToken
|
||||||
|
|
||||||
|
## WDAG
|
||||||
|
|
||||||
|
# Secondary
|
||||||
|
|
||||||
|
## Availability
|
||||||
|
|
||||||
|
## Business
|
||||||
|
|
||||||
|
## ERC721
|
||||||
|
|
||||||
|
## Expert
|
||||||
|
|
||||||
|
## Bench
|
||||||
|
|
||||||
|
# Tertiary
|
||||||
|
|
||||||
|
## Actor
|
||||||
|
|
||||||
|
## Action
|
||||||
|
|
||||||
|
## Scene
|
||||||
|
|
||||||
|
# To Explore
|
||||||
|
|
||||||
|
## Exchange
|
||||||
|
|
||||||
|
## Storage
|
||||||
|
|
||||||
|
## Network
|
||||||
|
|
||||||
|
## Wallet
|
||||||
|
|
||||||
|
## Agent/UI
|
||||||
|
|
||||||
|
## BlockConsensus
|
|
@ -0,0 +1,3 @@
|
||||||
|
export class BlockConsensus {
|
||||||
|
|
||||||
|
}
|
|
@ -31,7 +31,7 @@ export class Expert extends ReputationHolder {
|
||||||
// TODO: Include fee
|
// TODO: Include fee
|
||||||
const postMessage = new PostMessage({ post, stake });
|
const postMessage = new PostMessage({ post, stake });
|
||||||
await postMessage.sign(this.reputationKey);
|
await postMessage.sign(this.reputationKey);
|
||||||
await this.actions.submitPostViaNetwork.log(this, forumNode, null, { id: post.id });
|
await this.actions.submitPostViaNetwork.log(this, forumNode);
|
||||||
// For now, directly call forumNode.receiveMessage();
|
// For now, directly call forumNode.receiveMessage();
|
||||||
await forumNode.receiveMessage(JSON.stringify(postMessage.toJSON()));
|
await forumNode.receiveMessage(JSON.stringify(postMessage.toJSON()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,8 @@ export class ForumNode extends Actor {
|
||||||
.filter((forumNode) => forumNode.keyPair.publicKey !== this.keyPair.publicKey);
|
.filter((forumNode) => forumNode.keyPair.publicKey !== this.keyPair.publicKey);
|
||||||
for (const forumNode of otherForumNodes) {
|
for (const forumNode of otherForumNodes) {
|
||||||
// For now just call receiveMessage on the target node
|
// For now just call receiveMessage on the target node
|
||||||
await this.actions.peerMessage.log(this, forumNode, null, message.content);
|
// await this.actions.peerMessage.log(this, forumNode, null, message.content);
|
||||||
|
await this.actions.peerMessage.log(this, forumNode);
|
||||||
await forumNode.receiveMessage(JSON.stringify(message.toJSON()));
|
await forumNode.receiveMessage(JSON.stringify(message.toJSON()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,14 +62,13 @@ export class ForumNode extends Actor {
|
||||||
try {
|
try {
|
||||||
await Message.verify(messageJson);
|
await Message.verify(messageJson);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await this.actions.processMessage.log(this, this, 'invalid signature', messageJson, '-x');
|
await this.actions.processMessage.log(this, this, 'invalid signature', null, '-x');
|
||||||
console.log(`${this.name}: received message with invalid signature`);
|
console.log(`${this.name}: received message with invalid signature`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { publicKey } = messageJson;
|
const { publicKey } = messageJson;
|
||||||
const message = messageFromJSON(messageJson);
|
const message = messageFromJSON(messageJson);
|
||||||
console.log(`${this.name}: processMessage`, message);
|
|
||||||
|
|
||||||
if (message instanceof PostMessage) {
|
if (message instanceof PostMessage) {
|
||||||
await this.processPostMessage(publicKey, message.content);
|
await this.processPostMessage(publicKey, message.content);
|
||||||
|
@ -77,18 +77,16 @@ export class ForumNode extends Actor {
|
||||||
} else {
|
} else {
|
||||||
// Unknown message type
|
// Unknown message type
|
||||||
// Penalize sender for wasting our time
|
// Penalize sender for wasting our time
|
||||||
console.log(`${this.name}: penalizing sender for unknown message type ${message.type}`);
|
|
||||||
this.forumView.incrementReputation(message.publicKey, -1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process an incoming post, received by whatever means
|
// Process an incoming post, received by whatever means
|
||||||
async processPost(authorId, post, stake) {
|
async processPost(authorId, post) {
|
||||||
if (!post.id) {
|
if (!post.id) {
|
||||||
post.id = CryptoUtil.randomUUID();
|
post.id = CryptoUtil.randomUUID();
|
||||||
}
|
}
|
||||||
await this.actions.storePost.log(this, this, null, { authorId, post, stake });
|
await this.actions.storePost.log(this, this);
|
||||||
this.forumView.addPost(authorId, post.id, post, stake);
|
// this.forumView.addPost(authorId, post.id, post, stake);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process a post we received in a message
|
// Process a post we received in a message
|
||||||
|
|
|
@ -51,12 +51,10 @@ export class ForumView {
|
||||||
const { citations = [], content } = postContent;
|
const { citations = [], content } = postContent;
|
||||||
const author = this.getOrInitializeAuthor(authorId);
|
const author = this.getOrInitializeAuthor(authorId);
|
||||||
const postVertex = new PostVertex(postId, author, stake, content, citations);
|
const postVertex = new PostVertex(postId, author, stake, content, citations);
|
||||||
console.log('addPost', { id: postId, postContent });
|
|
||||||
this.posts.addVertex(postId, postVertex);
|
this.posts.addVertex(postId, postVertex);
|
||||||
for (const { postId: citedPostId, weight } of citations) {
|
for (const { postId: citedPostId, weight } of citations) {
|
||||||
this.posts.addEdge('citation', postId, citedPostId, weight);
|
this.posts.addEdge('citation', postId, citedPostId, weight);
|
||||||
}
|
}
|
||||||
this.applyNonbindingReputationEffects(postVertex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getPost(postId) {
|
getPost(postId) {
|
||||||
|
@ -66,65 +64,4 @@ export class ForumView {
|
||||||
getPosts() {
|
getPosts() {
|
||||||
return this.posts.getVertices();
|
return this.posts.getVertices();
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll start with naieve implementations of the computations we need.
|
|
||||||
|
|
||||||
// We want to derive a value -- maybe call it a reputation score -- for each post.
|
|
||||||
// This value is a recursive sum of contributions from citations.
|
|
||||||
|
|
||||||
// There should be a diminishment of effect upon each recursion,
|
|
||||||
// perhaps following a geometric progression.
|
|
||||||
|
|
||||||
// Each post gets some initial score due to the reputation that the author stakes.
|
|
||||||
|
|
||||||
// Citations are weighted, and can be positive or negative.
|
|
||||||
|
|
||||||
// So each post has a reputation score. Each author also has a reputation score.
|
|
||||||
// The value of the author's reputation score could be a factor in the magnitude of the effects of their citations.
|
|
||||||
// Post_rep = (Author_rep * stake);
|
|
||||||
//
|
|
||||||
|
|
||||||
// Options:
|
|
||||||
// - update a state model incrementally with each action in the history (/unfolding present) of the forum,
|
|
||||||
// in order to arrive at the current view.
|
|
||||||
|
|
||||||
// When an author stakes reputation on a post, if it's a non-binding stake, then it merely expresses opinion.
|
|
||||||
// If it's a binding stake, then they may lose the staked reputation as a result of other posts
|
|
||||||
// staking reputation against theirs.
|
|
||||||
|
|
||||||
citationFraction = 0.3;
|
|
||||||
|
|
||||||
applyNonbindingReputationEffects(newPost) {
|
|
||||||
this.distributeNonbindingReputation(newPost, newPost, newPost.stake);
|
|
||||||
}
|
|
||||||
|
|
||||||
distributeNonbindingReputation(newPost, post, amount, depth = 0) {
|
|
||||||
console.log('distributeNonbindingReputation', { post, amount, depth });
|
|
||||||
// Some of the incoming reputation goes to this post
|
|
||||||
post.reputation += amount * (1 - this.citationFraction);
|
|
||||||
this.setReputation(post.id, post.reputation);
|
|
||||||
// Some of the incoming reputation gets distributed among cited posts
|
|
||||||
const distributeAmongCitations = amount * this.citationFraction;
|
|
||||||
|
|
||||||
// citation weights can be interpreted as a ratio, or we can somehow constrain the input
|
|
||||||
// to add up to some specified total.
|
|
||||||
// It's easy enough to let them be on any arbitrary scale and just compute the ratios here.
|
|
||||||
const totalWeight = post.citations
|
|
||||||
?.map(({ weight }) => weight)
|
|
||||||
.reduce((acc, cur) => (acc += cur), 0);
|
|
||||||
|
|
||||||
post.citations?.forEach((citation) => {
|
|
||||||
const citedPost = this.getPost(citation.postId);
|
|
||||||
if (!citedPost) {
|
|
||||||
// TODO: Here is where we may want to engage our peer protocol to query for possible missing records
|
|
||||||
throw new Error(`Post ${post.postId} cites unknown post ${citation.postId}`);
|
|
||||||
}
|
|
||||||
this.distributeNonbindingReputation(
|
|
||||||
newPost,
|
|
||||||
citedPost,
|
|
||||||
(citation.weight / totalWeight) * distributeAmongCitations,
|
|
||||||
depth + 1,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,8 +34,8 @@ class Post extends Actor {
|
||||||
throw new Error('Post donation total exceeds revaluation limit '
|
throw new Error('Post donation total exceeds revaluation limit '
|
||||||
+ `(${donationTotal} > ${params.revaluationLimit})`);
|
+ `(${donationTotal} > ${params.revaluationLimit})`);
|
||||||
}
|
}
|
||||||
if (this.citations.some(({ weight }) => Math.abs(weight) > 1)) {
|
if (this.citations.some(({ weight }) => Math.abs(weight) > params.revaluationLimit)) {
|
||||||
throw new Error('Each citation weight must be in the range [-1, 1]');
|
throw new Error(`Each citation magnitude must not exceed revaluation limit ${params.revaluationLimit}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,68 +4,6 @@
|
||||||
<link type="text/css" rel="stylesheet" href="../index.css" />
|
<link type="text/css" rel="stylesheet" href="../index.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="forum-network"></div>
|
<div id="scene"></div>
|
||||||
</body>
|
</body>
|
||||||
<script type="module">
|
<script type="module" src="./scripts/forum-network.test.js"></script>
|
||||||
import { Box } from '../classes/box.js';
|
|
||||||
import { Scene } from '../classes/scene.js';
|
|
||||||
import { PostContent } from '../classes/post-content.js';
|
|
||||||
import { Expert } from '../classes/expert.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');
|
|
||||||
const rootBox = new Box('rootBox', rootElement).flex();
|
|
||||||
|
|
||||||
window.scene = new Scene('Forum Network test', rootBox).withSequenceDiagram();
|
|
||||||
|
|
||||||
window.author1 = await new Expert('author1', window.scene).initialize();
|
|
||||||
window.author2 = await new Expert('author2', window.scene).initialize();
|
|
||||||
|
|
||||||
window.forumNetwork = new ForumNetwork();
|
|
||||||
|
|
||||||
window.forumNode1 = await new ForumNode('node1', window.scene).initialize(
|
|
||||||
window.forumNetwork,
|
|
||||||
);
|
|
||||||
window.forumNode2 = await new ForumNode('node2', window.scene).initialize(
|
|
||||||
window.forumNetwork,
|
|
||||||
);
|
|
||||||
window.forumNode3 = await new ForumNode('node3', window.scene).initialize(
|
|
||||||
window.forumNetwork,
|
|
||||||
);
|
|
||||||
|
|
||||||
const processInterval = setInterval(async () => {
|
|
||||||
await window.forumNode1.processNextMessage();
|
|
||||||
await window.forumNode2.processNextMessage();
|
|
||||||
await window.forumNode3.processNextMessage();
|
|
||||||
|
|
||||||
await window.scene.sequence.render();
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
// 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.submitPostViaNetwork(
|
|
||||||
window.forumNode1,
|
|
||||||
window.post1,
|
|
||||||
50,
|
|
||||||
);
|
|
||||||
await delay(1000);
|
|
||||||
await window.author2.submitPostViaNetwork(
|
|
||||||
window.forumNode2,
|
|
||||||
window.post2,
|
|
||||||
100,
|
|
||||||
);
|
|
||||||
|
|
||||||
await delay(1000);
|
|
||||||
clearInterval(processInterval);
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<link type="text/css" rel="stylesheet" href="../index.css" />
|
<link type="text/css" rel="stylesheet" href="../index.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="test"></div>
|
<div id="scene"></div>
|
||||||
</body>
|
</body>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import { Box } from '../classes/box.js';
|
import { Box } from '../classes/box.js';
|
||||||
|
@ -16,10 +16,10 @@ import { delay } from '../util.js';
|
||||||
|
|
||||||
const DEFAULT_DELAY_INTERVAL = 500;
|
const DEFAULT_DELAY_INTERVAL = 500;
|
||||||
|
|
||||||
const rootElement = document.getElementById('forum-test');
|
const rootElement = document.getElementById('scene');
|
||||||
const rootBox = new Box('rootBox', rootElement).flex();
|
const rootBox = new Box('rootBox', rootElement).flex();
|
||||||
|
|
||||||
const scene = (window.scene = new Scene('Forum test', rootBox));
|
const scene = (window.scene = new Scene('Reputation test', rootBox));
|
||||||
scene.withSequenceDiagram();
|
scene.withSequenceDiagram();
|
||||||
scene.withFlowchart();
|
scene.withFlowchart();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
import { Box } from '../../classes/box.js';
|
||||||
|
import { Scene } from '../../classes/scene.js';
|
||||||
|
import { PostContent } from '../../classes/post-content.js';
|
||||||
|
import { Expert } from '../../classes/expert.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('scene');
|
||||||
|
const rootBox = new Box('rootBox', rootElement).flex();
|
||||||
|
|
||||||
|
window.scene = new Scene('Forum Network test', rootBox).withSequenceDiagram();
|
||||||
|
|
||||||
|
window.author1 = await new Expert('author1', window.scene).initialize();
|
||||||
|
window.author2 = await new Expert('author2', window.scene).initialize();
|
||||||
|
|
||||||
|
window.forumNetwork = new ForumNetwork();
|
||||||
|
|
||||||
|
window.forumNode1 = await new ForumNode('node1', window.scene).initialize(
|
||||||
|
window.forumNetwork,
|
||||||
|
);
|
||||||
|
window.forumNode2 = await new ForumNode('node2', window.scene).initialize(
|
||||||
|
window.forumNetwork,
|
||||||
|
);
|
||||||
|
window.forumNode3 = await new ForumNode('node3', window.scene).initialize(
|
||||||
|
window.forumNetwork,
|
||||||
|
);
|
||||||
|
|
||||||
|
const processInterval = setInterval(async () => {
|
||||||
|
await window.forumNode1.processNextMessage();
|
||||||
|
await window.forumNode2.processNextMessage();
|
||||||
|
await window.forumNode3.processNextMessage();
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
// 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.submitPostViaNetwork(
|
||||||
|
window.forumNode1,
|
||||||
|
window.post1,
|
||||||
|
50,
|
||||||
|
);
|
||||||
|
await delay(1000);
|
||||||
|
await window.author2.submitPostViaNetwork(
|
||||||
|
window.forumNode2,
|
||||||
|
window.post2,
|
||||||
|
100,
|
||||||
|
);
|
||||||
|
|
||||||
|
await delay(1000);
|
||||||
|
clearInterval(processInterval);
|
Loading…
Reference in New Issue