import { Action } from './action.js'; import { Actor } from './actor.js'; import { CryptoUtil } from './crypto.js'; class Worker { constructor(reputationPublicKey, tokenId, stakeAmount, duration) { this.reputationPublicKey = reputationPublicKey; this.tokenId = tokenId; this.stakeAmount = stakeAmount; this.duration = duration; this.available = true; this.assignedRequestId = null; } } /** * Purpose: Enable staking reputation to enter the pool of workers */ export class Availability extends Actor { constructor(dao, name) { super(name); this.dao = dao; this.actions = { assignWork: new Action('assign work'), }; this.workers = new Map(); } register(reputationPublicKey, { stakeAmount, tokenId, duration }) { // TODO: Should be signed by token owner this.dao.reputation.lock(tokenId, stakeAmount, duration); const workerId = CryptoUtil.randomUUID(); this.workers.set(workerId, new Worker(reputationPublicKey, tokenId, stakeAmount, duration)); return workerId; } get availableWorkers() { return Array.from(this.workers.values()).filter(({ available }) => !!available); } async assignWork(requestId) { const totalAmountStaked = this.availableWorkers .reduce((total, { stakeAmount }) => total += stakeAmount, 0); // 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]; worker.available = false; worker.assignedRequestId = requestId; // TODO: Notify assignee return worker; } async getAssignedWork(workerId) { const worker = this.workers.get(workerId); return worker.assignedRequestId; } }