import { Actor } from './actor.js'; import { Action } from './action.js'; import { PostMessage } from './message.js'; import { CryptoUtil } from './crypto.js'; export class Expert extends Actor { constructor(name, scene) { super(name, scene); this.actions = { submitPostViaNetwork: new Action('submit post via network', scene), submitPost: new Action('submit post', scene), initiateValidationPool: new Action('initiate validation pool', scene), castVote: new Action('cast vote', scene), revealIdentity: new Action('reveal identity', scene), registerAvailability: new Action('register availability', scene), getAssignedWork: new Action('get assigned work', scene), submitWork: new Action('submit work evidence', scene), }; this.validationPools = new Map(); } async initialize() { this.reputationKey = await CryptoUtil.generateAsymmetricKey(); // this.reputationPublicKey = await CryptoUtil.exportKey(this.reputationKey.publicKey); this.reputationPublicKey = this.name; this.status.set('Initialized'); return this; } async submitPostViaNetwork(forumNode, post, stake) { // TODO: Include fee const postMessage = new PostMessage({ post, stake }); await postMessage.sign(this.reputationKey); await this.actions.submitPostViaNetwork.log(this, forumNode, null, { id: post.id }); // For now, directly call forumNode.receiveMessage(); await forumNode.receiveMessage(JSON.stringify(postMessage.toJSON())); } async submitPost(forum, postContent) { // TODO: Include fee await this.actions.submitPost.log(this, forum); return forum.addPost(this.reputationPublicKey, postContent); } async submitPostWithFee(bench, forum, postContent, poolOptions) { await this.actions.submitPost.log(this, forum); const postId = await forum.addPost(this.reputationPublicKey, postContent); const pool = await this.initiateValidationPool(bench, { ...poolOptions, postId, anonymous: false }); return { postId, pool }; } async initiateValidationPool(bench, poolOptions) { // For now, directly call bench.initiateValidationPool(); if (poolOptions.anonymous) { const signingKey = await CryptoUtil.generateAsymmetricKey(); poolOptions.signingPublicKey = await CryptoUtil.exportKey(signingKey.publicKey); } else { poolOptions.signingPublicKey = this.reputationPublicKey; } await this.actions.initiateValidationPool.log( this, bench, `(fee: ${poolOptions.fee}, stake: ${poolOptions.authorStake ?? 0})`, ); const pool = await bench.initiateValidationPool(poolOptions); this.validationPools.set(pool.id, poolOptions); return pool; } async castVote(validationPool, { position, stake, lockingTime, anonymous = true, }) { let signingPublicKey; if (anonymous) { const signingKey = await CryptoUtil.generateAsymmetricKey(); signingPublicKey = await CryptoUtil.exportKey(signingKey.publicKey); this.validationPools.set(validationPool.id, { signingPublicKey }); } else { signingPublicKey = this.reputationPublicKey; } // TODO: encrypt vote // TODO: sign message await this.actions.castVote.log( this, validationPool, `(${position ? 'for' : 'against'}, stake: ${stake}, anonymous: ${anonymous})`, ); return validationPool.castVote(signingPublicKey, { position, stake, lockingTime, anonymous, }); } async revealIdentity(validationPool) { const { signingPublicKey } = this.validationPools.get(validationPool.id); // TODO: sign message await this.actions.revealIdentity.log(this, validationPool); validationPool.revealIdentity(signingPublicKey, this.reputationPublicKey); } async registerAvailability(availability, stake) { await this.actions.registerAvailability.log(this, availability, `(stake: ${stake})`); await availability.register(this.reputationPublicKey, stake); } async getAssignedWork(availability, business) { const requestId = await availability.getAssignedWork(this.reputationPublicKey); const request = await business.getRequest(requestId); return request; } async submitWork(business, requestId, evidence, { tokenLossRatio, duration }) { await this.actions.submitWork.log(this, business); return business.submitWork(this.reputationPublicKey, requestId, evidence, { tokenLossRatio, duration }); } }