Added business and availability SC
This commit is contained in:
parent
59c10f1ac2
commit
6ad293b5b8
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<head>
|
||||
<title>Forum</title>
|
||||
<script type="module" src="./availability-test.js" defer></script>
|
||||
<link type="text/css" rel="stylesheet" href="./index.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="availability-test"></div>
|
||||
</body>
|
||||
<scr
|
|
@ -0,0 +1,68 @@
|
|||
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';
|
||||
|
||||
const rootElement = document.getElementById('availability-test');
|
||||
const rootBox = new Box('rootBox', rootElement).flex();
|
||||
|
||||
const scene = window.scene = new Scene('Availability test', rootBox).log('sequenceDiagram');
|
||||
const member1 = window.member1 = await new Member('Member1', scene).initialize();
|
||||
const member2 = window.member2 = await new Member('Member2', scene).initialize();
|
||||
const bench = window.bench = new Bench('Bench', scene);
|
||||
const forum = window.forum = new Forum(bench, 'Forum', scene);
|
||||
const availability = window.bench = new Availability(bench, 'Availability', scene);
|
||||
const business = window.bench = new Business(bench, forum, availability, 'Business', scene);
|
||||
|
||||
const updateDisplayValues = async () => {
|
||||
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
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<head>
|
||||
<title>Forum</title>
|
||||
<script type="module" src="./forum-test.js" defer></script>
|
||||
<script type="module" src="./forum-network-test.js" defer></script>
|
||||
<link type="text/css" rel="stylesheet" href="./index.css" />
|
||||
</head>
|
||||
<body>
|
|
@ -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);
|
|
@ -6,10 +6,11 @@
|
|||
<body>
|
||||
<ul>
|
||||
<li><a href="./basic.html">Basic test</a></li>
|
||||
<li><a href="./forum-test.html">Forum test</a></li>
|
||||
<li><a href="./forum-network-test.html">Forum Network test</a></li>
|
||||
<li><a href="./graph-test.html">Graph test</a></li>
|
||||
<li><a href="./validation-pool-test.html">Validation Pool test</a></li>
|
||||
<li><a href="./mermaid-test.html">Mermaid test</a></li>
|
||||
<li><a href="./debounce-test.html">Debounce test</a></li>
|
||||
<li><a href="./availability-test.html">Availability test</a></li>
|
||||
</ul>
|
||||
</body>
|
||||
|
|
|
@ -7,3 +7,4 @@
|
|||
<body>
|
||||
<div id="validation-pool"></div>
|
||||
</body>
|
||||
<scr
|
||||
|
|
Loading…
Reference in New Issue