Cleanup related to forum network test

Also misc minor stub additions
This commit is contained in:
Ladd Hoffman 2023-01-30 20:30:05 -06:00
parent c24952497d
commit ab63ad1868
10 changed files with 119 additions and 141 deletions

View File

@ -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

View File

@ -0,0 +1,3 @@
export class BlockConsensus {
}

View File

@ -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()));
} }

View File

@ -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

View File

@ -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,
);
});
}
} }

View File

@ -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}`);
} }
} }

View File

View File

@ -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>

View File

@ -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();

View File

@ -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);