Update business and availability contracts to use rep tokens
This commit is contained in:
parent
8982ac610f
commit
9deaf4db07
|
@ -0,0 +1,30 @@
|
||||||
|
We've considered implementing this validation pool + forum structure as smart contracts.
|
||||||
|
However, we expect that such contracts would be expensive to run, because the recursive algorithm for distributing reputation via the forum will incur a lot of computation, consuming a lot of gas.
|
||||||
|
|
||||||
|
Can we bake this reputation algorithm into the core protocol of our blockchain?
|
||||||
|
|
||||||
|
The structure seems to be similar to proof-of-stake. A big difference is that what is staked and awarded is reputation rather than currency.
|
||||||
|
The idea with reputation is that it entitles you to a proportional share of revenue earned by the network.
|
||||||
|
So what does that look like in this context?
|
||||||
|
|
||||||
|
Let's say we are extending Ethereum. ETH would continue to be the currency that users must spend in order to execute transactions.
|
||||||
|
So when a user wants to execute a transaction, they must pay a fee.
|
||||||
|
A portion of this fee could then be distributed to reputation holders.
|
||||||
|
|
||||||
|
- https://ethereum.org/en/developers/docs/consensus-mechanisms/pos/
|
||||||
|
- https://ethereum.org/en/developers/docs/nodes-and-clients/
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
execution client
|
||||||
|
execution gossip network
|
||||||
|
|
||||||
|
consensus client
|
||||||
|
consensus gossip network
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
cardano -- "dynamic availability"?
|
||||||
|
staking pools -- does it make sense with reputation?
|
||||||
|
what about for governance voting --
|
||||||
|
do we want a representative republic or a democracy?
|
|
@ -1,10 +1,13 @@
|
||||||
import { Action } from './action.js';
|
import { Action } from './action.js';
|
||||||
import { Actor } from './actor.js';
|
import { Actor } from './actor.js';
|
||||||
|
import { CryptoUtil } from './crypto.js';
|
||||||
|
|
||||||
class Worker {
|
class Worker {
|
||||||
constructor(tokenId) {
|
constructor(reputationPublicKey, tokenId, stakeAmount, duration) {
|
||||||
|
this.reputationPublicKey = reputationPublicKey;
|
||||||
this.tokenId = tokenId;
|
this.tokenId = tokenId;
|
||||||
this.stakeAmount = 0;
|
this.stakeAmount = stakeAmount;
|
||||||
|
this.duration = duration;
|
||||||
this.available = true;
|
this.available = true;
|
||||||
this.assignedRequestId = null;
|
this.assignedRequestId = null;
|
||||||
}
|
}
|
||||||
|
@ -14,8 +17,6 @@ class Worker {
|
||||||
* Purpose: Enable staking reputation to enter the pool of workers
|
* 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) {
|
constructor(bench, name, scene) {
|
||||||
super(name, scene);
|
super(name, scene);
|
||||||
this.bench = bench;
|
this.bench = bench;
|
||||||
|
@ -23,39 +24,48 @@ export class Availability extends Actor {
|
||||||
this.actions = {
|
this.actions = {
|
||||||
assignWork: new Action('assign work', scene),
|
assignWork: new Action('assign work', scene),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.workers = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
register({ stakeAmount, tokenId }) {
|
register(reputationPublicKey, { stakeAmount, tokenId, duration }) {
|
||||||
// TODO: expire after duration
|
// TODO: Should be signed by token owner
|
||||||
// ? Is a particular stake amount required?
|
this.bench.reputation.lock(tokenId, stakeAmount, duration);
|
||||||
const worker = this.workers.get(tokenId) ?? new Worker(tokenId);
|
const workerId = CryptoUtil.randomUUID();
|
||||||
if (!worker.available) {
|
this.workers.set(workerId, new Worker(reputationPublicKey, tokenId, stakeAmount, duration));
|
||||||
throw new Error('Worker is already registered and busy. Can not increase stake.');
|
return workerId;
|
||||||
}
|
}
|
||||||
worker.stakeAmount += stakeAmount;
|
|
||||||
// TODO: Interact with Bench contract to encumber reputation?
|
|
||||||
this.workers.set(tokenId, worker);
|
|
||||||
}
|
|
||||||
|
|
||||||
// unregister() { }
|
|
||||||
|
|
||||||
get availableWorkers() {
|
get availableWorkers() {
|
||||||
return Array.from(this.workers.values()).filter(({ available }) => !!available);
|
return Array.from(this.workers.values()).filter(({ available }) => !!available);
|
||||||
}
|
}
|
||||||
|
|
||||||
async assignWork(requestId) {
|
async assignWork(requestId) {
|
||||||
// Get random worker
|
const totalAmountStaked = this.availableWorkers
|
||||||
// TODO: Probability proportional to stakes
|
.reduce((total, { stakeAmount }) => total += stakeAmount, 0);
|
||||||
const index = Math.floor(Math.random() * this.availableWorkers.length);
|
// Imagine all these amounts layed end-to-end along a number line.
|
||||||
|
// To weight choice by amount staked, pick a stake by choosing a number at random
|
||||||
|
// from within that line segment.
|
||||||
|
const randomChoice = Math.random() * totalAmountStaked;
|
||||||
|
let index = 0;
|
||||||
|
let acc = 0;
|
||||||
|
for (const { stakeAmount } of this.workers.values()) {
|
||||||
|
acc += stakeAmount;
|
||||||
|
if (acc >= randomChoice) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
const worker = this.availableWorkers[index];
|
const worker = this.availableWorkers[index];
|
||||||
worker.available = false;
|
worker.available = false;
|
||||||
worker.assignedRequestId = requestId;
|
worker.assignedRequestId = requestId;
|
||||||
|
|
||||||
// TODO: Notify assignee
|
// TODO: Notify assignee
|
||||||
return worker;
|
return worker;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAssignedWork(reputationPublicKey) {
|
async getAssignedWork(workerId) {
|
||||||
const worker = this.workers.get(reputationPublicKey);
|
const worker = this.workers.get(workerId);
|
||||||
return worker.assignedRequestId;
|
return worker.assignedRequestId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,13 +45,23 @@ export class Bench extends Actor {
|
||||||
.reduce((acc, cur) => (acc += cur), 0);
|
.reduce((acc, cur) => (acc += cur), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
async initiateValidationPool(poolOptions) {
|
async initiateValidationPool(poolOptions, stakeOptions) {
|
||||||
const validationPoolNumber = this.validationPools.size + 1;
|
const validationPoolNumber = this.validationPools.size + 1;
|
||||||
const name = `Pool${validationPoolNumber}`;
|
const name = `Pool${validationPoolNumber}`;
|
||||||
const validationPool = new ValidationPool(this, this.forum, poolOptions, name, this.scene);
|
const pool = new ValidationPool(this, this.forum, poolOptions, name, this.scene);
|
||||||
this.validationPools.set(validationPool.id, validationPool);
|
this.validationPools.set(pool.id, pool);
|
||||||
await this.actions.createValidationPool.log(this, validationPool);
|
await this.actions.createValidationPool.log(this, pool);
|
||||||
validationPool.activate();
|
pool.activate();
|
||||||
return validationPool;
|
|
||||||
|
if (stakeOptions) {
|
||||||
|
const { reputationPublicKey, tokenId, authorStakeAmount } = stakeOptions;
|
||||||
|
await pool.stake(reputationPublicKey, {
|
||||||
|
tokenId,
|
||||||
|
position: true,
|
||||||
|
amount: authorStakeAmount,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return pool;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ class Request {
|
||||||
this.id = CryptoUtil.randomUUID();
|
this.id = CryptoUtil.randomUUID();
|
||||||
this.fee = fee;
|
this.fee = fee;
|
||||||
this.content = content;
|
this.content = content;
|
||||||
|
this.worker = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,8 +16,6 @@ class Request {
|
||||||
* Purpose: Enable fee-driven work requests, to be completed by workers from the availability pool
|
* Purpose: Enable fee-driven work requests, to be completed by workers from the availability pool
|
||||||
*/
|
*/
|
||||||
export class Business extends Actor {
|
export class Business extends Actor {
|
||||||
requests = new Map();
|
|
||||||
|
|
||||||
constructor(bench, forum, availability, name, scene) {
|
constructor(bench, forum, availability, name, scene) {
|
||||||
super(name, scene);
|
super(name, scene);
|
||||||
this.bench = bench;
|
this.bench = bench;
|
||||||
|
@ -28,13 +27,16 @@ export class Business extends Actor {
|
||||||
submitPost: new Action('submit post', scene),
|
submitPost: new Action('submit post', scene),
|
||||||
initiateValidationPool: new Action('initiate validation pool', scene),
|
initiateValidationPool: new Action('initiate validation pool', scene),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.requests = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
async submitRequest(fee, content) {
|
async submitRequest(fee, content) {
|
||||||
const request = new Request(fee, content);
|
const request = new Request(fee, content);
|
||||||
this.requests.set(request.id, request);
|
this.requests.set(request.id, request);
|
||||||
await this.actions.assignWork.log(this, this.availability);
|
await this.actions.assignWork.log(this, this.availability);
|
||||||
this.worker = await this.availability.assignWork(request.id);
|
const worker = await this.availability.assignWork(request.id);
|
||||||
|
request.worker = worker;
|
||||||
return request.id;
|
return request.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,26 +51,33 @@ export class Business extends Actor {
|
||||||
throw new Error(`Request not found! id: ${requestId}`);
|
throw new Error(`Request not found! id: ${requestId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (reputationPublicKey !== request.worker.reputationPublicKey) {
|
||||||
|
throw new Error('Work evidence must be submitted by the assigned worker!');
|
||||||
|
}
|
||||||
|
|
||||||
// Create a post representing this submission.
|
// Create a post representing this submission.
|
||||||
const post = new PostContent({
|
const post = new PostContent({
|
||||||
requestId,
|
requestId,
|
||||||
workEvidence,
|
workEvidence,
|
||||||
});
|
});
|
||||||
|
const requestIndex = Array.from(this.requests.values())
|
||||||
|
.findIndex(({ id }) => id === request.id);
|
||||||
|
post.setTitle(`Work Evidence ${requestIndex + 1}`);
|
||||||
|
|
||||||
await this.actions.submitPost.log(this, this.forum);
|
await this.actions.submitPost.log(this, this.forum);
|
||||||
const postId = await this.forum.addPost(reputationPublicKey, post);
|
const postId = await this.forum.addPost(reputationPublicKey, post);
|
||||||
|
|
||||||
// Initiate a validation pool for this work evidence.
|
// 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.
|
|
||||||
await this.actions.initiateValidationPool.log(this, this.bench);
|
await this.actions.initiateValidationPool.log(this, this.bench);
|
||||||
const pool = await this.bench.initiateValidationPool({
|
const pool = await this.bench.initiateValidationPool({
|
||||||
postId,
|
postId,
|
||||||
fee: request.fee,
|
fee: request.fee,
|
||||||
duration,
|
duration,
|
||||||
tokenLossRatio,
|
tokenLossRatio,
|
||||||
|
}, {
|
||||||
reputationPublicKey,
|
reputationPublicKey,
|
||||||
authorStakeAmount: this.worker.stakeAmount,
|
authorStakeAmount: request.worker.stakeAmount,
|
||||||
tokenId: this.worker.tokenId,
|
tokenId: request.worker.tokenId,
|
||||||
});
|
});
|
||||||
|
|
||||||
// When the validation pool concludes,
|
// When the validation pool concludes,
|
||||||
|
|
|
@ -54,7 +54,7 @@ export class ERC721 /* is ERC165 */ {
|
||||||
ownerOf(tokenId) {
|
ownerOf(tokenId) {
|
||||||
const owner = this.owners.get(tokenId);
|
const owner = this.owners.get(tokenId);
|
||||||
if (!owner) {
|
if (!owner) {
|
||||||
throw new Error('ERC721: invalid token ID');
|
throw new Error(`ERC721: invalid token ID: ${tokenId}`);
|
||||||
}
|
}
|
||||||
return owner;
|
return owner;
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ export class Expert extends ReputationHolder {
|
||||||
async submitPostWithFee(bench, forum, postContent, poolOptions) {
|
async submitPostWithFee(bench, forum, postContent, poolOptions) {
|
||||||
await this.actions.submitPost.log(this, forum);
|
await this.actions.submitPost.log(this, forum);
|
||||||
const postId = await forum.addPost(this.reputationPublicKey, postContent);
|
const postId = await forum.addPost(this.reputationPublicKey, postContent);
|
||||||
const pool = await this.initiateValidationPool(bench, { ...poolOptions, postId, anonymous: false });
|
const pool = await this.initiateValidationPool(bench, { ...poolOptions, postId });
|
||||||
this.tokens.push(pool.tokenId);
|
this.tokens.push(pool.tokenId);
|
||||||
return { postId, pool };
|
return { postId, pool };
|
||||||
}
|
}
|
||||||
|
@ -74,18 +74,22 @@ export class Expert extends ReputationHolder {
|
||||||
validationPool,
|
validationPool,
|
||||||
`(${position ? 'for' : 'against'}, stake: ${amount})`,
|
`(${position ? 'for' : 'against'}, stake: ${amount})`,
|
||||||
);
|
);
|
||||||
return validationPool.stake(this, {
|
return validationPool.stake(this.reputationPublicKey, {
|
||||||
position, amount, lockingTime, tokenId: this.tokens[0],
|
position, amount, lockingTime, tokenId: this.tokens[0],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async registerAvailability(availability, stakeAmount) {
|
async registerAvailability(availability, stakeAmount, duration) {
|
||||||
await this.actions.registerAvailability.log(this, availability, `(stake: ${stakeAmount})`);
|
await this.actions.registerAvailability.log(this, availability, `(stake: ${stakeAmount}, duration: ${duration})`);
|
||||||
await availability.register({ stakeAmount, tokenId: this.tokens[0].id });
|
this.workerId = await availability.register(this.reputationPublicKey, {
|
||||||
|
stakeAmount,
|
||||||
|
tokenId: this.tokens[0],
|
||||||
|
duration,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAssignedWork(availability, business) {
|
async getAssignedWork(availability, business) {
|
||||||
const requestId = await availability.getAssignedWork(this.reputationPublicKey);
|
const requestId = await availability.getAssignedWork(this.workerId);
|
||||||
const request = await business.getRequest(requestId);
|
const request = await business.getRequest(requestId);
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ export class Forum extends ReputationHolder {
|
||||||
this.actions = {
|
this.actions = {
|
||||||
addPost: new Action('add post', scene),
|
addPost: new Action('add post', scene),
|
||||||
propagateValue: new Action('propagate value', this.scene),
|
propagateValue: new Action('propagate value', this.scene),
|
||||||
|
transfer: new Action('transfer', this.scene),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,6 +80,7 @@ export class Forum extends ReputationHolder {
|
||||||
async onValidate({
|
async onValidate({
|
||||||
bench, pool, postId, tokenId,
|
bench, pool, postId, tokenId,
|
||||||
}) {
|
}) {
|
||||||
|
this.activate();
|
||||||
const initialValue = bench.reputation.valueOf(tokenId);
|
const initialValue = bench.reputation.valueOf(tokenId);
|
||||||
|
|
||||||
if (this.scene.flowchart) {
|
if (this.scene.flowchart) {
|
||||||
|
@ -103,6 +105,10 @@ export class Forum extends ReputationHolder {
|
||||||
|
|
||||||
// Transfer ownership of the minted/staked token, from the forum to the post author
|
// Transfer ownership of the minted/staked token, from the forum to the post author
|
||||||
bench.reputation.transferFrom(this.id, post.authorPublicKey, post.tokenId);
|
bench.reputation.transferFrom(this.id, post.authorPublicKey, post.tokenId);
|
||||||
|
const toActor = this.scene.findActor((actor) => actor.reputationPublicKey === post.authorPublicKey);
|
||||||
|
const value = bench.reputation.valueOf(post.tokenId);
|
||||||
|
this.actions.transfer.log(this, toActor, `(value: ${value})`);
|
||||||
|
this.deactivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
async propagateValue(rewardsAccumulator, fromActor, post, increment, depth = 0) {
|
async propagateValue(rewardsAccumulator, fromActor, post, increment, depth = 0) {
|
||||||
|
|
|
@ -42,6 +42,9 @@ export class ReputationTokenContract extends ERC721 {
|
||||||
}
|
}
|
||||||
|
|
||||||
transferValueFrom(fromTokenId, toTokenId, amount) {
|
transferValueFrom(fromTokenId, toTokenId, amount) {
|
||||||
|
if (amount === undefined) {
|
||||||
|
throw new Error('Transfer value: amount is undefined!');
|
||||||
|
}
|
||||||
const sourceAvailable = this.availableValueOf(fromTokenId);
|
const sourceAvailable = this.availableValueOf(fromTokenId);
|
||||||
const targetAvailable = this.availableValueOf(toTokenId);
|
const targetAvailable = this.availableValueOf(toTokenId);
|
||||||
if (sourceAvailable < amount - EPSILON) {
|
if (sourceAvailable < amount - EPSILON) {
|
||||||
|
|
|
@ -98,6 +98,10 @@ export class Scene {
|
||||||
this.actors.add(actor);
|
this.actors.add(actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findActor(fn) {
|
||||||
|
return Array.from(this.actors.values()).find(fn);
|
||||||
|
}
|
||||||
|
|
||||||
addAction(name) {
|
addAction(name) {
|
||||||
const action = new Action(name, this);
|
const action = new Action(name, this);
|
||||||
return action;
|
return action;
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { ReputationHolder } from './reputation-holder.js';
|
||||||
import { Stake } from './stake.js';
|
import { Stake } from './stake.js';
|
||||||
import { Voter } from './voter.js';
|
import { Voter } from './voter.js';
|
||||||
import params from '../params.js';
|
import params from '../params.js';
|
||||||
|
import { Action } from './action.js';
|
||||||
|
|
||||||
const ValidationPoolStates = Object.freeze({
|
const ValidationPoolStates = Object.freeze({
|
||||||
OPEN: 'OPEN',
|
OPEN: 'OPEN',
|
||||||
|
@ -24,13 +25,18 @@ export class ValidationPool extends ReputationHolder {
|
||||||
duration,
|
duration,
|
||||||
tokenLossRatio,
|
tokenLossRatio,
|
||||||
contentiousDebate = false,
|
contentiousDebate = false,
|
||||||
authorStakeAmount = 0,
|
|
||||||
},
|
},
|
||||||
name,
|
name,
|
||||||
scene,
|
scene,
|
||||||
) {
|
) {
|
||||||
super(`pool_${CryptoUtil.randomUUID()}`, name, scene);
|
super(`pool_${CryptoUtil.randomUUID()}`, name, scene);
|
||||||
this.id = this.reputationPublicKey;
|
this.id = this.reputationPublicKey;
|
||||||
|
|
||||||
|
this.actions = {
|
||||||
|
reward: new Action('reward', scene),
|
||||||
|
transfer: new Action('transfer', scene),
|
||||||
|
};
|
||||||
|
|
||||||
// If contentiousDebate = true, we will follow the progression defined by getTokenLossRatio()
|
// If contentiousDebate = true, we will follow the progression defined by getTokenLossRatio()
|
||||||
if (
|
if (
|
||||||
!contentiousDebate
|
!contentiousDebate
|
||||||
|
@ -69,12 +75,12 @@ export class ValidationPool extends ReputationHolder {
|
||||||
this.tokenId = this.bench.reputation.mint(this.id, this.mintedValue);
|
this.tokenId = this.bench.reputation.mint(this.id, this.mintedValue);
|
||||||
// Tokens minted "for" the post go toward stake of author voting for their own post.
|
// 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.
|
// Also, author can provide additional stakes, e.g. availability stakes for work evidence post.
|
||||||
this.stake(this, {
|
this.stake(this.id, {
|
||||||
position: true,
|
position: true,
|
||||||
amount: this.mintedValue * params.stakeForAuthor + authorStakeAmount,
|
amount: this.mintedValue * params.stakeForAuthor,
|
||||||
tokenId: this.tokenId,
|
tokenId: this.tokenId,
|
||||||
});
|
});
|
||||||
this.stake(this, {
|
this.stake(this.id, {
|
||||||
position: false,
|
position: false,
|
||||||
amount: this.mintedValue * (1 - params.stakeForAuthor),
|
amount: this.mintedValue * (1 - params.stakeForAuthor),
|
||||||
tokenId: this.tokenId,
|
tokenId: this.tokenId,
|
||||||
|
@ -134,7 +140,7 @@ export class ValidationPool extends ReputationHolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This can be handled as a hook on receipt of reputation token transfer
|
// TODO: This can be handled as a hook on receipt of reputation token transfer
|
||||||
async stake(reputationHolder, {
|
async stake(reputationPublicKey, {
|
||||||
tokenId, position, amount, lockingTime = 0,
|
tokenId, position, amount, lockingTime = 0,
|
||||||
}) {
|
}) {
|
||||||
if (this.state === ValidationPoolStates.CLOSED) {
|
if (this.state === ValidationPoolStates.CLOSED) {
|
||||||
|
@ -147,7 +153,6 @@ export class ValidationPool extends ReputationHolder {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { reputationPublicKey } = reputationHolder;
|
|
||||||
if (reputationPublicKey !== this.bench.reputation.ownerOf(tokenId)) {
|
if (reputationPublicKey !== this.bench.reputation.ownerOf(tokenId)) {
|
||||||
throw new Error('Reputation may only be staked by its owner!');
|
throw new Error('Reputation may only be staked by its owner!');
|
||||||
}
|
}
|
||||||
|
@ -237,6 +242,8 @@ export class ValidationPool extends ReputationHolder {
|
||||||
const reputationPublicKey = this.bench.reputation.ownerOf(tokenId);
|
const reputationPublicKey = this.bench.reputation.ownerOf(tokenId);
|
||||||
console.log(`reward for winning stake by ${reputationPublicKey}: ${reward}`);
|
console.log(`reward for winning stake by ${reputationPublicKey}: ${reward}`);
|
||||||
this.bench.reputation.transferValueFrom(this.tokenId, tokenId, reward + amount);
|
this.bench.reputation.transferValueFrom(this.tokenId, tokenId, reward + amount);
|
||||||
|
const toActor = this.scene.findActor((actor) => actor.reputationPublicKey === reputationPublicKey);
|
||||||
|
this.actions.reward.log(this, toActor, `(${reward})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (votePasses && !!this.forum) {
|
if (votePasses && !!this.forum) {
|
||||||
|
@ -246,6 +253,8 @@ export class ValidationPool extends ReputationHolder {
|
||||||
|
|
||||||
// Transfer ownership of the minted token, from the pool to the forum
|
// Transfer ownership of the minted token, from the pool to the forum
|
||||||
this.bench.reputation.transferFrom(this.id, this.forum.id, this.tokenId);
|
this.bench.reputation.transferFrom(this.id, this.forum.id, this.tokenId);
|
||||||
|
const value = this.bench.reputation.valueOf(this.tokenId);
|
||||||
|
this.actions.transfer.log(this, this.forum, `(value: ${value})`);
|
||||||
|
|
||||||
// Recurse through forum to determine reputation effects
|
// Recurse through forum to determine reputation effects
|
||||||
await this.forum.onValidate({
|
await this.forum.onValidate({
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
import { delay } from '../util.js';
|
import { delay } from '../util.js';
|
||||||
import { Forum } from '../classes/forum.js';
|
import { Forum } from '../classes/forum.js';
|
||||||
import { Public } from '../classes/public.js';
|
import { Public } from '../classes/public.js';
|
||||||
|
import { PostContent } from '../classes/post-content.js';
|
||||||
|
|
||||||
const DELAY_INTERVAL = 500;
|
const DELAY_INTERVAL = 500;
|
||||||
|
|
||||||
|
@ -24,6 +25,7 @@
|
||||||
|
|
||||||
const scene = (window.scene = new Scene('Availability test', rootBox));
|
const scene = (window.scene = new Scene('Availability test', rootBox));
|
||||||
scene.withSequenceDiagram();
|
scene.withSequenceDiagram();
|
||||||
|
scene.withFlowchart();
|
||||||
|
|
||||||
const experts = (window.experts = []);
|
const experts = (window.experts = []);
|
||||||
const newExpert = async () => {
|
const newExpert = async () => {
|
||||||
|
@ -36,10 +38,9 @@
|
||||||
|
|
||||||
const expert1 = await newExpert();
|
const expert1 = await newExpert();
|
||||||
const expert2 = await newExpert();
|
const expert2 = await newExpert();
|
||||||
await newExpert();
|
|
||||||
const forum = (window.forum = new Forum('Forum', scene));
|
const forum = (window.forum = new Forum('Forum', scene));
|
||||||
const bench = (window.bench = new Bench(forum, 'Bench', scene));
|
const bench = (window.bench = new Bench(forum, 'Bench', scene));
|
||||||
const availability = (window.bench = new Availability(
|
const availability = (window.availability = new Availability(
|
||||||
bench,
|
bench,
|
||||||
'Availability',
|
'Availability',
|
||||||
scene,
|
scene,
|
||||||
|
@ -64,9 +65,9 @@
|
||||||
await scene.sequence.render();
|
await scene.sequence.render();
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateDisplayValuesAndDelay = async () => {
|
const updateDisplayValuesAndDelay = async (delayMs = DELAY_INTERVAL) => {
|
||||||
await updateDisplayValues();
|
await updateDisplayValues();
|
||||||
await delay(DELAY_INTERVAL);
|
await delay(delayMs);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getActiveWorker = async () => {
|
const getActiveWorker = async () => {
|
||||||
|
@ -90,7 +91,6 @@
|
||||||
await expert.stake(pool, {
|
await expert.stake(pool, {
|
||||||
position: true,
|
position: true,
|
||||||
amount: 1,
|
amount: 1,
|
||||||
anonymous: false,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,9 +98,42 @@
|
||||||
|
|
||||||
await updateDisplayValuesAndDelay();
|
await updateDisplayValuesAndDelay();
|
||||||
|
|
||||||
|
// Experts gain initial reputation by submitting a post with fee
|
||||||
|
const { postId: postId1, pool: pool1 } = await expert1.submitPostWithFee(
|
||||||
|
bench,
|
||||||
|
forum,
|
||||||
|
new PostContent({ hello: 'there' }).setTitle('Post 1'),
|
||||||
|
{
|
||||||
|
fee: 10,
|
||||||
|
duration: 1000,
|
||||||
|
tokenLossRatio: 1,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
await updateDisplayValuesAndDelay(1000);
|
||||||
|
|
||||||
|
await pool1.evaluateWinningConditions();
|
||||||
|
await updateDisplayValuesAndDelay();
|
||||||
|
|
||||||
|
const { pool: pool2 } = await expert2.submitPostWithFee(
|
||||||
|
bench,
|
||||||
|
forum,
|
||||||
|
new PostContent({ hello: 'to you as well' })
|
||||||
|
.setTitle('Post 2')
|
||||||
|
.addCitation(postId1, 0.5),
|
||||||
|
{
|
||||||
|
fee: 10,
|
||||||
|
duration: 1000,
|
||||||
|
tokenLossRatio: 1,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
await updateDisplayValuesAndDelay(1000);
|
||||||
|
|
||||||
|
await pool2.evaluateWinningConditions();
|
||||||
|
await updateDisplayValuesAndDelay();
|
||||||
|
|
||||||
// Populate availability pool
|
// Populate availability pool
|
||||||
await expert1.registerAvailability(availability, 1);
|
await expert1.registerAvailability(availability, 1, 10000);
|
||||||
await expert2.registerAvailability(availability, 1);
|
await expert2.registerAvailability(availability, 1, 10000);
|
||||||
await updateDisplayValuesAndDelay();
|
await updateDisplayValuesAndDelay();
|
||||||
|
|
||||||
// Submit work request
|
// Submit work request
|
||||||
|
|
|
@ -81,9 +81,6 @@
|
||||||
);
|
);
|
||||||
await updateDisplayValuesAndDelay(1000);
|
await updateDisplayValuesAndDelay(1000);
|
||||||
|
|
||||||
// await expert2.stake(pool1, { position: true, amount 1, anonymous: false });
|
|
||||||
// await updateDisplayValuesAndDelay();
|
|
||||||
|
|
||||||
await pool1.evaluateWinningConditions();
|
await pool1.evaluateWinningConditions();
|
||||||
await updateDisplayValuesAndDelay();
|
await updateDisplayValuesAndDelay();
|
||||||
|
|
||||||
|
@ -104,7 +101,7 @@
|
||||||
);
|
);
|
||||||
await updateDisplayValuesAndDelay(1000);
|
await updateDisplayValuesAndDelay(1000);
|
||||||
|
|
||||||
// await expert1.stake(pool2, { position: true, amount 1, anonymous: false });
|
// await expert1.stake(pool2, { position: true, amount 1});
|
||||||
// await updateDisplayValuesAndDelay();
|
// await updateDisplayValuesAndDelay();
|
||||||
|
|
||||||
await pool2.evaluateWinningConditions();
|
await pool2.evaluateWinningConditions();
|
||||||
|
@ -127,7 +124,7 @@
|
||||||
);
|
);
|
||||||
await updateDisplayValuesAndDelay(1000);
|
await updateDisplayValuesAndDelay(1000);
|
||||||
|
|
||||||
// await expert1.stake(pool3, { position: true, amount 1, anonymous: false });
|
// await expert1.stake(pool3, { position: true, amount 1});
|
||||||
// await updateDisplayValuesAndDelay();
|
// await updateDisplayValuesAndDelay();
|
||||||
|
|
||||||
await pool3.evaluateWinningConditions();
|
await pool3.evaluateWinningConditions();
|
||||||
|
|
Loading…
Reference in New Issue