diff --git a/forum-network/public/availability-test.html b/forum-network/public/availability-test.html
new file mode 100644
index 0000000..459d14c
--- /dev/null
+++ b/forum-network/public/availability-test.html
@@ -0,0 +1,10 @@
+
+
+ Forum
+
+
+
+
+
+
+ {
+ member1.setValue('rep', bench.reputations.getTokens(member1.reputationPublicKey));
+ member2.setValue('rep', bench.reputations.getTokens(member2.reputationPublicKey));
+ bench.setValue('total rep', bench.getTotalReputation());
+ await scene.renderSequenceDiagram();
+};
+
+updateDisplayValues();
+
+// const post1 = window.post1 = new PostContent({ message: 'hi' });
+// const post2 = window.post2 = new PostContent({ message: 'hello' }).addCitation(window.post1.id, 1.0);
+
+// Populate availability pool
+availability.register(member1.reputationPublicKey, 1);
+availability.register(member2.reputationPublicKey, 1);
+
+await delay(500);
+
+// Submit work request
+const requestId = await business.submitRequest(100, { please: 'do some work' });
+await scene.renderSequenceDiagram();
+await delay(500);
+
+// Submit work evidence
+const pool = await business.submitWork(member1.reputationPublicKey, requestId, {
+ here: 'is some evidence of work product',
+}, {
+ tokenLossRatio: 1,
+ duration: 1000,
+});
+
+await scene.renderSequenceDiagram();
+await delay(500);
+
+// Vote on work evidence
+await member2.castVote(pool, { position: true, stake: 1 });
+await scene.renderSequenceDiagram();
+await delay(500);
+
+await member2.revealIdentity(pool);
+await scene.renderSequenceDiagram();
+await delay(500);
+
+await member1.revealIdentity(pool, member1.reputationPublicKey);
+await scene.renderSequenceDiagram();
+
+// Distribute reputation awards
+// Distribute fees
diff --git a/forum-network/public/classes/availability.js b/forum-network/public/classes/availability.js
index 554e20e..b4cced6 100644
--- a/forum-network/public/classes/availability.js
+++ b/forum-network/public/classes/availability.js
@@ -1,6 +1,49 @@
import { Actor } from './actor.js';
+class Worker {
+ stake = 0;
+
+ available = true;
+
+ assignedRequestId = null;
+
+ constructor(reputationPublicKey) {
+ this.reputationPublicKey = reputationPublicKey;
+ }
+}
+
/**
* Purpose: Enable staking reputation to enter the pool of workers
*/
-export class Availability extends Actor {}
+export class Availability extends Actor {
+ workers = new Map();
+
+ constructor(bench, name, scene) {
+ super(name, scene);
+ this.bench = bench;
+ }
+
+ register(reputationPublicKey, stake) {
+ // ? Is a particular stake amount required?
+ const worker = this.workers.get(reputationPublicKey) ?? new Worker(reputationPublicKey);
+ if (!worker.available) {
+ throw new Error('Worker is already registered and busy. Cannot increase stake.');
+ }
+ worker.stake += stake;
+ // ? Interact with Bench contract to encumber reputation?
+ this.workers.set(reputationPublicKey, worker);
+ }
+
+ get availableWorkers() {
+ return Array.from(this.workers.values()).filter(({ available }) => !!available);
+ }
+
+ async assignWork(requestId) {
+ // Get random worker
+ const index = Math.floor(Math.random() * this.availableWorkers.length);
+ const worker = this.availableWorkers[index];
+ worker.available = false;
+ worker.assignedRequestId = requestId;
+ // TOOD: Notify assignee
+ }
+}
diff --git a/forum-network/public/classes/bench.js b/forum-network/public/classes/bench.js
index 5595c02..5c849c2 100644
--- a/forum-network/public/classes/bench.js
+++ b/forum-network/public/classes/bench.js
@@ -64,6 +64,7 @@ export class Bench extends Actor {
tokenLossRatio,
contentiousDebate,
signingPublicKey,
+ authorStake,
},
) {
const validationPoolNumber = this.validationPools.size + 1;
@@ -77,6 +78,7 @@ export class Bench extends Actor {
tokenLossRatio,
contentiousDebate,
signingPublicKey,
+ authorStake,
},
`pool${validationPoolNumber}`,
this.scene,
diff --git a/forum-network/public/classes/business.js b/forum-network/public/classes/business.js
index 17c5236..019009e 100644
--- a/forum-network/public/classes/business.js
+++ b/forum-network/public/classes/business.js
@@ -1,10 +1,65 @@
import { Actor } from './actor.js';
+import { CryptoUtil } from './crypto.js';
+import { PostContent } from './post.js';
+
+class Request {
+ constructor(fee, content) {
+ this.id = CryptoUtil.randomUUID();
+ this.fee = fee;
+ this.content = content;
+ }
+}
/**
* Purpose: Enable fee-driven work requests, to be completed by workers from the availability pool
*/
export class Business extends Actor {
- // constructor(name, scene) { super(name, scene); }
+ requests = new Map();
- // submitRequest(fee) {}
+ constructor(bench, forum, availability, name, scene) {
+ super(name, scene);
+ this.bench = bench;
+ this.forum = forum;
+ this.availability = availability;
+ }
+
+ /**
+ * Fee should be held in escrow.
+ * That means there should be specific conditions under which the fee will be refunded.
+ * That means the submission should include some time value to indicate when it expires.
+ * There could be separate thresholds to indicate the earliest that the job may be cancelled,
+ * and the time at which the job will be automatically cancelled.
+ */
+ async submitRequest(fee, content) {
+ const request = new Request(fee, content);
+ this.requests.set(request.id, request);
+ await this.availability.assignWork(request.id);
+ return request.id;
+ }
+
+ async submitWork(reputationPublicKey, requestId, workEvidence, { tokenLossRatio, duration }) {
+ const { fee } = this.requests.get(requestId);
+
+ // Create a post representing this submission.
+ const post = new PostContent({
+ requestId,
+ workEvidence,
+ });
+ await this.forum.addPost(reputationPublicKey, post);
+
+ // Initiate a validation pool for this work evidence.
+ // Validation pool supports secret ballots but we aren't using that here, since we want
+ // the post to be attributable to the reputation holder.
+ const pool = await this.bench.initiateValidationPool(reputationPublicKey, {
+ postId: post.id,
+ fee,
+ duration,
+ tokenLossRatio,
+ signingPublicKey: reputationPublicKey,
+ });
+
+ // When the validation pool concludes,
+ // reputation should be awarded and fees should be distributed.
+ return pool;
+ }
}
diff --git a/forum-network/public/classes/forum-view.js b/forum-network/public/classes/forum-view.js
index ff4256b..1176d03 100644
--- a/forum-network/public/classes/forum-view.js
+++ b/forum-network/public/classes/forum-view.js
@@ -7,8 +7,7 @@ class Author {
}
}
-// TODO: Consider merging with "client-side" Post class in `./post.js`
-class Post {
+class PostVertex {
constructor(id, author, stake, content, citations) {
this.id = id;
this.author = author;
@@ -48,14 +47,15 @@ export class ForumView {
return author;
}
- addPost(authorId, postId, { citations = [], content }, stake) {
+ addPost(authorId, postId, postContent, stake) {
+ const { citations = [], content } = postContent;
const author = this.getOrInitializeAuthor(authorId);
- const post = new Post(postId, author, stake, content, citations);
- this.posts.addVertex(postId, post);
+ const postVertex = new PostVertex(postId, author, stake, content, citations);
+ this.posts.addVertex(postId, postVertex);
for (const citation of citations) {
this.posts.addEdge('citation', postId, citation.postId, citation);
}
- this.applyNonbindingReputationEffects(post);
+ this.applyNonbindingReputationEffects(postVertex);
}
getPost(postId) {
diff --git a/forum-network/public/classes/forum.js b/forum-network/public/classes/forum.js
index 955e038..adb0dc5 100644
--- a/forum-network/public/classes/forum.js
+++ b/forum-network/public/classes/forum.js
@@ -2,13 +2,13 @@ import { Actor } from './actor.js';
import { Graph, Vertex } from './graph.js';
import params from './params.js';
-class Post extends Vertex {
- constructor(forum, id, authorId, citations) {
+class PostVertex extends Vertex {
+ constructor(forum, authorId, postContent) {
super();
this.forum = forum;
- this.id = id;
+ this.id = postContent.id;
this.authorId = authorId;
- this.citations = citations;
+ this.citations = postContent.citations;
this.value = 0;
}
@@ -28,21 +28,20 @@ export class Forum extends Actor {
this.posts = new Graph();
}
- async addPost({
- authorId, postId, citations = [], poolParams,
- }) {
- const post = new Post(this, postId, authorId);
- this.posts.addVertex(postId, post);
- for (const { postId: citedPostId, weight } of citations) {
- this.posts.addEdge('citation', postId, citedPostId, { weight });
+ async addPost(authorId, postContent) {
+ const post = new PostVertex(this, authorId, postContent);
+ this.posts.addVertex(post.id, post);
+ for (const { postId: citedPostId, weight } of postContent.citations) {
+ this.posts.addEdge('citation', post.id, citedPostId, { weight });
}
// this.applyReputationEffects(post);
// initiateValidationPool(authorId, {postId, fee, duration, tokenLossRatio, contentiousDebate, signingPublicKey}) {
- const pool = await this.bench.initiateValidationPool(authorId, {
- ...poolParams,
- postId,
- });
- return pool;
+
+ // const pool = await this.bench.initiateValidationPool(authorId, {
+ // ...poolParams,
+ // postId,
+ // });
+ // return pool;
}
getPost(postId) {
diff --git a/forum-network/public/classes/member.js b/forum-network/public/classes/member.js
index 32d2da7..1374896 100644
--- a/forum-network/public/classes/member.js
+++ b/forum-network/public/classes/member.js
@@ -65,10 +65,16 @@ export class Member extends Actor {
validationPool.castVote(signingPublicKey, position, stake, lockingTime);
}
- async revealIdentity(validationPool) {
- const { signingPublicKey } = this.validationPools.get(validationPool.id);
+ async revealIdentity(validationPool, signingPublicKey) {
+ if (!signingPublicKey) {
+ signingPublicKey = this.validationPools.get(validationPool.id).signingPublicKey;
+ }
// TODO: sign message
this.actions.revealIdentity.log(this, validationPool);
validationPool.revealIdentity(signingPublicKey, this.reputationPublicKey);
}
+
+ async submitWork(business, requestId, evidence, { tokenLossRatio }) {
+ await business.submitWork(this.reputationPublicKey, requestId, evidence, { tokenLossRatio });
+ }
}
diff --git a/forum-network/public/classes/message.js b/forum-network/public/classes/message.js
index 7e4c033..e0ed6c8 100644
--- a/forum-network/public/classes/message.js
+++ b/forum-network/public/classes/message.js
@@ -1,5 +1,5 @@
import { CryptoUtil } from './crypto.js';
-import { Post } from './post.js';
+import { PostContent } from './post.js';
export class Message {
constructor(content) {
@@ -9,7 +9,7 @@ export class Message {
async sign({ publicKey, privateKey }) {
this.publicKey = await CryptoUtil.exportKey(publicKey);
// Call toJSON before signing, to match what we'll later send
- this.signature = await CryptoUtil.sign(this.contentToJSON(this.content), privateKey);
+ this.signature = await CryptoUtil.sign(this.contentToJSON(), privateKey);
return this;
}
@@ -17,18 +17,14 @@ export class Message {
return CryptoUtil.verify(content, publicKey, signature);
}
- static contentFromJSON(data) {
- return data;
- }
-
- static contentToJSON(content) {
- return content;
+ contentToJSON() {
+ return this.content;
}
toJSON() {
return {
type: this.type,
- content: this.contentToJSON(this.content),
+ content: this.contentToJSON(),
publicKey: this.publicKey,
signature: this.signature,
};
@@ -38,17 +34,17 @@ export class Message {
export class PostMessage extends Message {
type = 'post';
- static contentFromJSON({ post, stake }) {
- return {
- post: Post.fromJSON(post),
+ constructor({ post, stake }) {
+ super({
+ post: PostContent.fromJSON(post),
stake,
- };
+ });
}
- static contentToJSON({ post, stake }) {
+ contentToJSON() {
return {
- post: post.toJSON(),
- stake,
+ post: this.content.post.toJSON(),
+ stake: this.content.stake,
};
}
}
@@ -64,6 +60,6 @@ const messageTypes = new Map([
export const messageFromJSON = ({ type, content }) => {
const MessageType = messageTypes.get(type) || Message;
- const messageContent = MessageType.contentFromJSON(content);
- return new MessageType(messageContent);
+ // const messageContent = MessageType.contentFromJSON(content);
+ return new MessageType(content);
};
diff --git a/forum-network/public/classes/post.js b/forum-network/public/classes/post.js
index 515a691..e3c49a9 100644
--- a/forum-network/public/classes/post.js
+++ b/forum-network/public/classes/post.js
@@ -18,7 +18,7 @@ export class Citation {
}
}
-export class Post {
+export class PostContent {
constructor(content) {
this.id = CryptoUtil.randomUUID();
this.content = content;
@@ -40,7 +40,7 @@ export class Post {
}
static fromJSON({ id, content, citations }) {
- const post = new Post(content);
+ const post = new PostContent(content);
post.id = id;
post.citations = citations.map((citation) => Citation.fromJSON(citation));
return post;
diff --git a/forum-network/public/classes/public.js b/forum-network/public/classes/public.js
new file mode 100644
index 0000000..e69de29
diff --git a/forum-network/public/classes/validation-pool.js b/forum-network/public/classes/validation-pool.js
index 96b44d8..3e86788 100644
--- a/forum-network/public/classes/validation-pool.js
+++ b/forum-network/public/classes/validation-pool.js
@@ -23,6 +23,7 @@ export class ValidationPool extends Actor {
duration,
tokenLossRatio,
contentiousDebate = false,
+ authorStake = 0,
},
name,
scene,
@@ -66,10 +67,11 @@ export class ValidationPool extends Actor {
this.tokens = {
for: fee * params.mintingRatio * params.stakeForWin,
against: fee * params.mintingRatio * (1 - params.stakeForWin),
- // author: fee * params.mintingRatio * params.stakeForAuthor,
};
- // TODO: Consider availability stakes
- this.castVote(signingPublicKey, true, this.tokens.for, 0);
+ // tokens minted "for" the post go toward stake of author voting for their own post
+ // also, author can provide additional stakes, e.g. availability stakes for work evidence post
+ console.log('initiateValidationPool casting vote', { signingPublicKey });
+ this.castVote(signingPublicKey, true, this.tokens.for + authorStake, 0);
}
castVote(signingPublicKey, position, stake, lockingTime) {
diff --git a/forum-network/public/forum-test.html b/forum-network/public/forum-network-test.html
similarity index 70%
rename from forum-network/public/forum-test.html
rename to forum-network/public/forum-network-test.html
index ea3145e..edabe94 100644
--- a/forum-network/public/forum-test.html
+++ b/forum-network/public/forum-network-test.html
@@ -1,7 +1,7 @@
Forum
-
+
diff --git a/forum-network/public/forum-test.js b/forum-network/public/forum-network-test.js
similarity index 83%
rename from forum-network/public/forum-test.js
rename to forum-network/public/forum-network-test.js
index 8d44023..c74e547 100644
--- a/forum-network/public/forum-test.js
+++ b/forum-network/public/forum-network-test.js
@@ -1,6 +1,6 @@
import { Box } from './classes/box.js';
import { Scene } from './classes/scene.js';
-import { Post } from './classes/post.js';
+import { PostContent } from './classes/post.js';
import { Member } from './classes/member.js';
import { ForumNode } from './classes/forum-node.js';
import { ForumNetwork } from './classes/forum-network.js';
@@ -9,7 +9,7 @@ import { delay } from './util.js';
const rootElement = document.getElementById('forum-network');
const rootBox = new Box('rootBox', rootElement).flex();
-window.scene = new Scene('Forum test', rootBox).log('sequenceDiagram');
+window.scene = new Scene('Forum Network test', rootBox).log('sequenceDiagram');
window.author1 = await new Member('author1', window.scene).initialize();
window.author2 = await new Member('author2', window.scene).initialize();
@@ -30,8 +30,8 @@ const processInterval = setInterval(async () => {
// const blockchain = new Blockchain();
-window.post1 = new Post({ message: 'hi' });
-window.post2 = new Post({ message: 'hello' }).addCitation(window.post1.id, 1.0);
+window.post1 = new PostContent({ message: 'hi' });
+window.post2 = new PostContent({ message: 'hello' }).addCitation(window.post1.id, 1.0);
await delay(1000);
await window.author1.submitPost(window.forumNode1, window.post1, 50);
diff --git a/forum-network/public/index.html b/forum-network/public/index.html
index 0c52a8e..b8a4ba0 100644
--- a/forum-network/public/index.html
+++ b/forum-network/public/index.html
@@ -6,10 +6,11 @@
diff --git a/forum-network/public/validation-pool-test.html b/forum-network/public/validation-pool-test.html
index 8e13539..dc02c2b 100644
--- a/forum-network/public/validation-pool-test.html
+++ b/forum-network/public/validation-pool-test.html
@@ -7,3 +7,4 @@
+