Added business and availability SC

This commit is contained in:
Ladd Hoffman 2023-01-01 21:09:02 -06:00
parent 59c10f1ac2
commit 6ad293b5b8
16 changed files with 239 additions and 56 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

View File

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

View File

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

View File

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

View File

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

View File

@ -7,3 +7,4 @@
<body>
<div id="validation-pool"></div>
</body>
<scr