diff --git a/ethereum/contracts/Work1.sol b/ethereum/contracts/Work1.sol index db094d6..8c1fab8 100644 --- a/ethereum/contracts/Work1.sol +++ b/ethereum/contracts/Work1.sol @@ -70,7 +70,13 @@ contract Work1 is IAcceptAvailability { msg.sender == stake.worker, "Worker can only extend their own availability stake" ); - stake.endTime = block.timestamp + duration; + require(!stake.reclaimed, "Stake has already been reclaimed"); + require(!stake.assigned, "Stake has already been assigned work"); + if (block.timestamp > stake.endTime) { + stake.endTime = block.timestamp + duration; + } else { + stake.endTime = stake.endTime + duration; + } emit AvailabilityStaked(stakeIndex); } diff --git a/ethereum/test/Work1.js b/ethereum/test/Work1.js index ddfe4cb..8e25c3f 100644 --- a/ethereum/test/Work1.js +++ b/ethereum/test/Work1.js @@ -103,6 +103,28 @@ describe('Work1', () => { await time.increase(STAKE_DURATION * 2); await expect(work1.connect(account2).extendAvailability(0, STAKE_DURATION)).to.be.revertedWith('Worker can only extend their own availability stake'); }); + + it('should not be able to extend a stake that has been reclaimed', async () => { + await time.increase(STAKE_DURATION * 2); + await work1.reclaimAvailability(0); + await expect(work1.extendAvailability(0, STAKE_DURATION)).to.be.revertedWith('Stake has already been reclaimed'); + }); + + it('extending a stake before expiration should increase the end time by the given duration', async () => { + await time.increase(STAKE_DURATION / 2); + await work1.extendAvailability(0, STAKE_DURATION * 2); + const expectedEndTime = await time.latest() + 2.5 * STAKE_DURATION; + const stake = await work1.stakes(0); + expect(stake.endTime).to.be.within(expectedEndTime - 1, expectedEndTime); + }); + + it('extending a stake after expiration should restart the stake for the given duration', async () => { + await time.increase(STAKE_DURATION * 2); + await work1.extendAvailability(0, STAKE_DURATION * 2); + const expectedEndTime = await time.latest() + STAKE_DURATION * 2; + const stake = await work1.stakes(0); + expect(stake.endTime).to.be.within(expectedEndTime - 1, expectedEndTime); + }); }); describe('Request and assign work', () => { @@ -163,6 +185,16 @@ describe('Work1', () => { await expect(requestWork()).to.emit(work1, 'WorkAssigned').withArgs(account1, 0); await expect(requestWork()).to.be.revertedWith('No available worker stakes'); }); + + it('should not be able to extend a stake that has been assigned work', async () => { + const { + dao, work1, account2, + } = await loadFixture(deploy); + await dao.stakeAvailability(work1.target, 50, STAKE_DURATION); + await work1.connect(account2).requestWork({ value: WORK1_PRICE }); + await time.increase(STAKE_DURATION * 2); + await expect(work1.extendAvailability(0, STAKE_DURATION)).to.be.revertedWith('Stake has already been assigned work'); + }); }); describe('Work evidence and approval/disapproval', () => {