diff --git a/ethereum/contracts/Work1.sol b/ethereum/contracts/Work1.sol index 2a3d8f1..8bcd80a 100644 --- a/ethereum/contracts/Work1.sol +++ b/ethereum/contracts/Work1.sol @@ -40,6 +40,7 @@ contract Work1 is IAcceptAvailability { uint constant POOL_DURATION = 1 days; event WorkAssigned(address worker, uint requestIndex); + event WorkEvidenceSubmitted(uint requestIndex); constructor(DAO dao_, uint price_) { dao = dao_; @@ -90,6 +91,7 @@ contract Work1 is IAcceptAvailability { if (block.timestamp > stakes[i].endTime) continue; totalStakes += stakes[i].amount; } + require(totalStakes > 0, "No available worker stakes"); uint select = block.prevrandao % totalStakes; uint acc; for (uint i = 0; i < stakeCount; i++) { @@ -98,7 +100,7 @@ contract Work1 is IAcceptAvailability { acc += stakes[i].amount; if (acc > select) return i; } - revert("Failed to select worker"); + revert("Failed to assign worker"); // Should never get here } /// Assign a random available worker @@ -126,6 +128,7 @@ contract Work1 is IAcceptAvailability { AvailabilityStake storage stake = stakes[request.stakeIndex]; require(stake.worker == msg.sender); request.status = WorkStatus.EvidenceSubmitted; + emit WorkEvidenceSubmitted(requestIndex); } /// Accept work approval/disapproval from customer @@ -136,7 +139,7 @@ contract Work1 is IAcceptAvailability { request.status = WorkStatus.ApprovalSubmitted; request.approval = approval; // Initiate validation pool - request.poolIndex = dao.initiateValidationPool( + request.poolIndex = dao.initiateValidationPool{value: request.fee}( stake.worker, POOL_DURATION ); diff --git a/ethereum/test/Work1.js b/ethereum/test/Work1.js index dc57581..272d2b4 100644 --- a/ethereum/test/Work1.js +++ b/ethereum/test/Work1.js @@ -1,12 +1,15 @@ const { time, loadFixture, + // setPrevRandao, } = require('@nomicfoundation/hardhat-toolbox/network-helpers'); const { expect } = require('chai'); const { ethers } = require('hardhat'); describe('Work1', () => { const WORK1_PRICE = 100; + const STAKE_DURATION = 60; + const STAKE_AMOUNT = 50; async function deploy() { // Contracts are deployed using the first signer/account by default const [account1, account2] = await ethers.getSigners(); @@ -36,13 +39,71 @@ describe('Work1', () => { it('Should be able to receive availability stake', async () => { const { dao, work1, account1 } = await loadFixture(deploy); - await dao.stakeAvailability(work1.target, 50, 60); - expect(await dao.balanceOf(account1)).to.equal(50); - expect(await dao.balanceOf(work1.target)).to.equal(50); + await dao.stakeAvailability(work1.target, STAKE_AMOUNT, STAKE_DURATION); + expect(await dao.balanceOf(account1)).to.equal(STAKE_AMOUNT); + expect(await dao.balanceOf(work1.target)).to.equal(STAKE_AMOUNT); expect(await work1.stakeCount()).to.equal(1); const stake = await work1.stakes(0); expect(stake.worker).to.equal(account1); - expect(stake.amount).to.equal(50); - expect(stake.endTime).to.equal(await time.latest() + 60); + expect(stake.amount).to.equal(STAKE_AMOUNT); + expect(stake.endTime).to.equal(await time.latest() + STAKE_DURATION); + }); + + it('should be able to request work and assign to a worker', async () => { + const { + dao, work1, account1, account2, + } = await loadFixture(deploy); + await dao.stakeAvailability(work1.target, STAKE_AMOUNT, STAKE_DURATION, { from: account1 }); + const requestWork = () => work1.connect(account2).requestWork({ value: WORK1_PRICE }); + await expect(requestWork()).to.emit(work1, 'WorkAssigned').withArgs(account1, 0); + expect(await work1.requestCount()).to.equal(1); + const request = await work1.requests(0); + expect(request.customer).to.equal(account2); + }); + + it('should not be able to request work if there are no availability stakes', async () => { + const { + work1, account2, + } = await loadFixture(deploy); + const requestWork = () => work1.connect(account2).requestWork({ value: WORK1_PRICE }); + await expect(requestWork()).to.be.revertedWith('No available worker stakes'); + }); + + it('should not assign work to an expired availability stake', async () => { + const { + dao, work1, account1, account2, + } = await loadFixture(deploy); + await dao.stakeAvailability(work1.target, STAKE_AMOUNT, STAKE_DURATION, { from: account1 }); + const requestWork = () => work1.connect(account2).requestWork({ value: WORK1_PRICE }); + await time.increase(61); + await expect(requestWork()).to.be.revertedWith('No available worker stakes'); + }); + it('should not assign work to the same availability stake twice', async () => { + const { + dao, work1, account1, account2, + } = await loadFixture(deploy); + await dao.stakeAvailability(work1.target, STAKE_AMOUNT, STAKE_DURATION, { from: account1 }); + const requestWork = () => work1.connect(account2).requestWork({ value: WORK1_PRICE }); + await expect(requestWork()).to.emit(work1, 'WorkAssigned').withArgs(account1, 0); + await expect(requestWork()).to.be.revertedWith('No available worker stakes'); + }); + + it('should be able to submit work evidence', async () => { + const { + dao, work1, account1, account2, + } = await loadFixture(deploy); + await dao.stakeAvailability(work1.target, STAKE_AMOUNT, STAKE_DURATION, { from: account1 }); + await work1.connect(account2).requestWork({ value: WORK1_PRICE }); + await expect(work1.submitWorkEvidence(0)).to.emit(work1, 'WorkEvidenceSubmitted').withArgs(0); + }); + + it('should be able to submit work approval', async () => { + const { + dao, work1, account1, account2, + } = await loadFixture(deploy); + await dao.stakeAvailability(work1.target, STAKE_AMOUNT, STAKE_DURATION, { from: account1 }); + await work1.connect(account2).requestWork({ value: WORK1_PRICE }); + await work1.submitWorkEvidence(0); + await expect(work1.submitWorkApproval(0, true)).to.emit(dao, 'ValidationPoolInitiated').withArgs(1); }); });