From 6ad293b5b818558c879dd39c54819361d6e4574e Mon Sep 17 00:00:00 2001 From: Ladd Hoffman Date: Sun, 1 Jan 2023 21:09:02 -0600 Subject: [PATCH] Added business and availability SC --- forum-network/public/availability-test.html | 10 +++ forum-network/public/availability-test.js | 68 +++++++++++++++++++ forum-network/public/classes/availability.js | 45 +++++++++++- forum-network/public/classes/bench.js | 2 + forum-network/public/classes/business.js | 59 +++++++++++++++- forum-network/public/classes/forum-view.js | 12 ++-- forum-network/public/classes/forum.js | 31 ++++----- forum-network/public/classes/member.js | 10 ++- forum-network/public/classes/message.js | 32 ++++----- forum-network/public/classes/post.js | 4 +- forum-network/public/classes/public.js | 0 .../public/classes/validation-pool.js | 8 ++- ...orum-test.html => forum-network-test.html} | 2 +- .../{forum-test.js => forum-network-test.js} | 8 +-- forum-network/public/index.html | 3 +- .../public/validation-pool-test.html | 1 + 16 files changed, 239 insertions(+), 56 deletions(-) create mode 100644 forum-network/public/availability-test.html create mode 100644 forum-network/public/availability-test.js create mode 100644 forum-network/public/classes/public.js rename forum-network/public/{forum-test.html => forum-network-test.html} (70%) rename forum-network/public/{forum-test.js => forum-network-test.js} (83%) 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 @@
+