mirror of
https://github.com/codex-storage/codex-contracts-eth.git
synced 2025-02-13 11:56:46 +00:00
feat: partial payouts for cancelled requests (#69)
This commit is contained in:
parent
1854dfba99
commit
14e453ac31
@ -25,6 +25,10 @@ contract Marketplace is Proofs, StateRetrieval {
|
||||
struct RequestContext {
|
||||
RequestState state;
|
||||
uint256 slotsFilled;
|
||||
|
||||
/// @notice Tracks how much funds should be returned when Request expires to the Request creator
|
||||
/// @dev The sum is deducted every time a host fills a Slot by precalculated amount that he should receive if the Request expires
|
||||
uint256 expiryFundsWithdraw;
|
||||
uint256 startedAt;
|
||||
uint256 endsAt;
|
||||
}
|
||||
@ -32,7 +36,12 @@ contract Marketplace is Proofs, StateRetrieval {
|
||||
struct Slot {
|
||||
SlotState state;
|
||||
RequestId requestId;
|
||||
|
||||
/// @notice Timestamp that signals when slot was filled
|
||||
/// @dev Used for partial payouts when Requests expires and Hosts are paid out only the time they host the content.
|
||||
uint256 filledAt;
|
||||
uint256 slotIndex;
|
||||
|
||||
/// @notice Tracks the current amount of host's collateral that is to be payed out at the end of Slot's lifespan.
|
||||
/// @dev When Slot is filled, the collateral is collected in amount of request.ask.collateral
|
||||
/// @dev When Host is slashed for missing a proof the slashed amount is reflected in this variable
|
||||
@ -80,6 +89,7 @@ contract Marketplace is Proofs, StateRetrieval {
|
||||
_addToMyRequests(request.client, id);
|
||||
|
||||
uint256 amount = request.price();
|
||||
_requestContexts[id].expiryFundsWithdraw = amount;
|
||||
_marketplaceTotals.received += amount;
|
||||
_transferFrom(msg.sender, amount);
|
||||
|
||||
@ -106,8 +116,10 @@ contract Marketplace is Proofs, StateRetrieval {
|
||||
|
||||
slot.host = msg.sender;
|
||||
slot.state = SlotState.Filled;
|
||||
slot.filledAt = block.timestamp;
|
||||
RequestContext storage context = _requestContexts[requestId];
|
||||
context.slotsFilled += 1;
|
||||
context.expiryFundsWithdraw -= _expiryPayoutAmount(requestId, block.timestamp);
|
||||
|
||||
// Collect collateral
|
||||
uint256 collateralAmount = request.ask.collateral;
|
||||
@ -133,6 +145,8 @@ contract Marketplace is Proofs, StateRetrieval {
|
||||
|
||||
if (state == SlotState.Finished) {
|
||||
_payoutSlot(slot.requestId, slotId);
|
||||
} else if (state == SlotState.Cancelled) {
|
||||
_payoutCancelledSlot(slot.requestId, slotId);
|
||||
} else if (state == SlotState.Failed) {
|
||||
_removeFromMySlots(msg.sender, slotId);
|
||||
} else if (state == SlotState.Filled) {
|
||||
@ -207,6 +221,19 @@ contract Marketplace is Proofs, StateRetrieval {
|
||||
assert(token.transfer(slot.host, amount));
|
||||
}
|
||||
|
||||
function _payoutCancelledSlot(
|
||||
RequestId requestId,
|
||||
SlotId slotId
|
||||
) private requestIsKnown(requestId) {
|
||||
Slot storage slot = _slots[slotId];
|
||||
_removeFromMySlots(slot.host, slotId);
|
||||
|
||||
uint256 amount = _expiryPayoutAmount(requestId, slot.filledAt) + slot.currentCollateral;
|
||||
_marketplaceTotals.sent += amount;
|
||||
slot.state = SlotState.Paid;
|
||||
assert(token.transfer(slot.host, amount));
|
||||
}
|
||||
|
||||
/// @notice Withdraws storage request funds back to the client that deposited them.
|
||||
/// @dev Request must be expired, must be in RequestState.New, and the transaction must originate from the depositer address.
|
||||
/// @param requestId the id of the request
|
||||
@ -224,10 +251,7 @@ contract Marketplace is Proofs, StateRetrieval {
|
||||
|
||||
emit RequestCancelled(requestId);
|
||||
|
||||
// TODO: To be changed once we start paying out hosts for the time they
|
||||
// fill a slot. The amount that we paid to hosts will then have to be
|
||||
// deducted from the price.
|
||||
uint256 amount = request.price();
|
||||
uint256 amount = context.expiryFundsWithdraw;
|
||||
_marketplaceTotals.sent += amount;
|
||||
assert(token.transfer(msg.sender, amount));
|
||||
}
|
||||
@ -268,6 +292,14 @@ contract Marketplace is Proofs, StateRetrieval {
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice Calculates the amount that should be payed out to a host if a request expires based on when the host fills the slot
|
||||
function _expiryPayoutAmount(RequestId requestId, uint256 startingTimestamp) private view returns (uint256) {
|
||||
Request storage request = _requests[requestId];
|
||||
require(startingTimestamp < request.expiry, "Start not before expiry");
|
||||
|
||||
return (request.expiry - startingTimestamp) * request.ask.reward;
|
||||
}
|
||||
|
||||
function getHost(SlotId slotId) public view returns (address) {
|
||||
return _slots[slotId].host;
|
||||
}
|
||||
@ -300,7 +332,7 @@ contract Marketplace is Proofs, StateRetrieval {
|
||||
return SlotState.Paid;
|
||||
}
|
||||
if (reqState == RequestState.Cancelled) {
|
||||
return SlotState.Finished;
|
||||
return SlotState.Cancelled;
|
||||
}
|
||||
if (reqState == RequestState.Finished) {
|
||||
return SlotState.Finished;
|
||||
|
@ -51,7 +51,8 @@ enum SlotState {
|
||||
Filled, // host has filled slot
|
||||
Finished, // successfully completed
|
||||
Failed, // the request has failed
|
||||
Paid // host has been paid
|
||||
Paid, // host has been paid
|
||||
Cancelled // when request was cancelled then slot is cancelled as well
|
||||
}
|
||||
|
||||
library Requests {
|
||||
|
@ -24,11 +24,13 @@ const {
|
||||
revert,
|
||||
mine,
|
||||
ensureMinimumBlockHeight,
|
||||
advanceTime,
|
||||
advanceTimeTo,
|
||||
advanceTimeForNextBlock,
|
||||
advanceTimeToForNextBlock,
|
||||
currentTime,
|
||||
} = require("./evm")
|
||||
|
||||
const ACCOUNT_STARTING_BALANCE = 1_000_000_000
|
||||
|
||||
describe("Marketplace constructor", function () {
|
||||
let Marketplace, token, config
|
||||
|
||||
@ -90,8 +92,8 @@ describe("Marketplace", function () {
|
||||
|
||||
const TestToken = await ethers.getContractFactory("TestToken")
|
||||
token = await TestToken.deploy()
|
||||
for (account of [client, host1, host2, host3]) {
|
||||
await token.mint(account.address, 1_000_000_000)
|
||||
for (let account of [client, host1, host2, host3]) {
|
||||
await token.mint(account.address, ACCOUNT_STARTING_BALANCE)
|
||||
}
|
||||
|
||||
const Marketplace = await ethers.getContractFactory("TestMarketplace")
|
||||
@ -320,6 +322,7 @@ describe("Marketplace", function () {
|
||||
it("sets request end time to the past once cancelled", async function () {
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
await waitUntilCancelled(request)
|
||||
await mine()
|
||||
const now = await currentTime()
|
||||
await expect(await marketplace.requestEnd(requestId(request))).to.be.eq(
|
||||
now - 1
|
||||
@ -329,6 +332,7 @@ describe("Marketplace", function () {
|
||||
it("checks that request end time is in the past once finished", async function () {
|
||||
await waitUntilStarted(marketplace, request, proof, token)
|
||||
await waitUntilFinished(marketplace, requestId(request))
|
||||
await mine()
|
||||
const now = await currentTime()
|
||||
// in the process of calling currentTime and requestEnd,
|
||||
// block.timestamp has advanced by 1, so the expected proof end time will
|
||||
@ -403,12 +407,17 @@ describe("Marketplace", function () {
|
||||
})
|
||||
|
||||
it("pays the host when contract was cancelled", async function () {
|
||||
// Lets move the time into middle of the expiry window
|
||||
const fillTimestamp = await currentTime() + Math.floor((request.expiry - await currentTime()) / 2) - 1
|
||||
await advanceTimeToForNextBlock(fillTimestamp)
|
||||
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
await waitUntilCancelled(request)
|
||||
const startBalance = await token.balanceOf(host.address)
|
||||
await marketplace.freeSlot(slotId(slot))
|
||||
|
||||
const endBalance = await token.balanceOf(host.address)
|
||||
expect(endBalance).to.be.gt(startBalance)
|
||||
const expectedPartialPayout = (request.expiry - fillTimestamp) * request.ask.reward
|
||||
expect(endBalance - ACCOUNT_STARTING_BALANCE).to.be.equal(expectedPartialPayout)
|
||||
})
|
||||
|
||||
it("does not pay when the contract hasn't ended", async function () {
|
||||
@ -542,6 +551,19 @@ describe("Marketplace", function () {
|
||||
const endBalance = await token.balanceOf(client.address)
|
||||
expect(endBalance - startBalance).to.equal(price(request))
|
||||
})
|
||||
|
||||
it("withdraws to the client for cancelled requests lowered by hosts payout", async function () {
|
||||
const fillTimestamp = await currentTime() + Math.floor((request.expiry - await currentTime()) / 2)
|
||||
await advanceTimeToForNextBlock(fillTimestamp)
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
await waitUntilCancelled(request)
|
||||
const expectedPartialHostPayout = (request.expiry - fillTimestamp) * request.ask.reward
|
||||
|
||||
switchAccount(client)
|
||||
await marketplace.withdrawFunds(slot.request)
|
||||
const endBalance = await token.balanceOf(client.address)
|
||||
expect(ACCOUNT_STARTING_BALANCE - endBalance).to.equal(expectedPartialHostPayout)
|
||||
})
|
||||
})
|
||||
|
||||
describe("request state", function () {
|
||||
@ -561,6 +583,7 @@ describe("Marketplace", function () {
|
||||
|
||||
it("changes to 'Cancelled' once request is cancelled", async function () {
|
||||
await waitUntilCancelled(request)
|
||||
await mine()
|
||||
expect(await marketplace.requestState(slot.request)).to.equal(Cancelled)
|
||||
})
|
||||
|
||||
@ -579,6 +602,7 @@ describe("Marketplace", function () {
|
||||
it("changes to 'Failed' once too many slots are freed", async function () {
|
||||
await waitUntilStarted(marketplace, request, proof, token)
|
||||
await waitUntilFailed(marketplace, request)
|
||||
await mine()
|
||||
expect(await marketplace.requestState(slot.request)).to.equal(Failed)
|
||||
})
|
||||
|
||||
@ -601,6 +625,7 @@ describe("Marketplace", function () {
|
||||
it("changes to 'Finished' when the request ends", async function () {
|
||||
await waitUntilStarted(marketplace, request, proof, token)
|
||||
await waitUntilFinished(marketplace, requestId(request))
|
||||
await mine()
|
||||
expect(await marketplace.requestState(slot.request)).to.equal(Finished)
|
||||
})
|
||||
|
||||
@ -613,7 +638,7 @@ describe("Marketplace", function () {
|
||||
})
|
||||
|
||||
describe("slot state", function () {
|
||||
const { Free, Filled, Finished, Failed, Paid } = SlotState
|
||||
const { Free, Filled, Finished, Failed, Paid, Cancelled } = SlotState
|
||||
let period, periodEnd
|
||||
|
||||
beforeEach(async function () {
|
||||
@ -628,14 +653,16 @@ describe("Marketplace", function () {
|
||||
})
|
||||
|
||||
async function waitUntilProofIsRequired(id) {
|
||||
await advanceTimeTo(periodEnd(periodOf(await currentTime())))
|
||||
await advanceTimeToForNextBlock(periodEnd(periodOf(await currentTime())))
|
||||
await mine()
|
||||
while (
|
||||
!(
|
||||
(await marketplace.isProofRequired(id)) &&
|
||||
(await marketplace.getPointer(id)) < 250
|
||||
)
|
||||
) {
|
||||
await advanceTime(period)
|
||||
await advanceTimeForNextBlock(period)
|
||||
await mine()
|
||||
}
|
||||
}
|
||||
|
||||
@ -651,13 +678,15 @@ describe("Marketplace", function () {
|
||||
it("changes to 'Finished' when request finishes", async function () {
|
||||
await waitUntilStarted(marketplace, request, proof, token)
|
||||
await waitUntilFinished(marketplace, slot.request)
|
||||
await mine()
|
||||
expect(await marketplace.slotState(slotId(slot))).to.equal(Finished)
|
||||
})
|
||||
|
||||
it("changes to 'Finished' when request is cancelled", async function () {
|
||||
it("changes to 'Cancelled' when request is cancelled", async function () {
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
await waitUntilCancelled(request)
|
||||
expect(await marketplace.slotState(slotId(slot))).to.equal(Finished)
|
||||
await mine()
|
||||
expect(await marketplace.slotState(slotId(slot))).to.equal(Cancelled)
|
||||
})
|
||||
|
||||
it("changes to 'Free' when host frees the slot", async function () {
|
||||
@ -671,7 +700,8 @@ describe("Marketplace", function () {
|
||||
while ((await marketplace.slotState(slotId(slot))) === Filled) {
|
||||
await waitUntilProofIsRequired(slotId(slot))
|
||||
const missedPeriod = periodOf(await currentTime())
|
||||
await advanceTime(period)
|
||||
await advanceTimeForNextBlock(period)
|
||||
await mine()
|
||||
await marketplace.markProofAsMissing(slotId(slot), missedPeriod)
|
||||
}
|
||||
expect(await marketplace.slotState(slotId(slot))).to.equal(Free)
|
||||
@ -680,6 +710,7 @@ describe("Marketplace", function () {
|
||||
it("changes to 'Failed' when request fails", async function () {
|
||||
await waitUntilStarted(marketplace, request, proof, token)
|
||||
await waitUntilSlotFailed(marketplace, request, slot)
|
||||
await mine()
|
||||
expect(await marketplace.slotState(slotId(slot))).to.equal(Failed)
|
||||
})
|
||||
|
||||
@ -712,14 +743,15 @@ describe("Marketplace", function () {
|
||||
}
|
||||
|
||||
async function waitUntilProofIsRequired(id) {
|
||||
await advanceTimeTo(periodEnd(periodOf(await currentTime())))
|
||||
await advanceTimeToForNextBlock(periodEnd(periodOf(await currentTime())))
|
||||
while (
|
||||
!(
|
||||
(await marketplace.isProofRequired(id)) &&
|
||||
(await marketplace.getPointer(id)) < 250
|
||||
)
|
||||
) {
|
||||
await advanceTime(period)
|
||||
await advanceTimeForNextBlock(period)
|
||||
await mine()
|
||||
}
|
||||
}
|
||||
|
||||
@ -734,7 +766,8 @@ describe("Marketplace", function () {
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
await waitUntilProofWillBeRequired(id)
|
||||
await expect(await marketplace.willProofBeRequired(id)).to.be.true
|
||||
await advanceTimeTo(request.expiry + 1)
|
||||
await waitUntilCancelled(request)
|
||||
await mine()
|
||||
await expect(await marketplace.willProofBeRequired(id)).to.be.false
|
||||
})
|
||||
|
||||
@ -743,7 +776,8 @@ describe("Marketplace", function () {
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
await waitUntilProofIsRequired(id)
|
||||
await expect(await marketplace.isProofRequired(id)).to.be.true
|
||||
await advanceTimeTo(request.expiry + 1)
|
||||
await waitUntilCancelled(request)
|
||||
await mine()
|
||||
await expect(await marketplace.isProofRequired(id)).to.be.false
|
||||
})
|
||||
|
||||
@ -751,9 +785,11 @@ describe("Marketplace", function () {
|
||||
const id = slotId(slot)
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
await waitUntilProofIsRequired(id)
|
||||
await mine()
|
||||
const challenge1 = await marketplace.getChallenge(id)
|
||||
expect(BigNumber.from(challenge1).gt(0))
|
||||
await advanceTimeTo(request.expiry + 1)
|
||||
await waitUntilCancelled(request)
|
||||
await mine()
|
||||
const challenge2 = await marketplace.getChallenge(id)
|
||||
expect(BigNumber.from(challenge2).isZero())
|
||||
})
|
||||
@ -762,9 +798,11 @@ describe("Marketplace", function () {
|
||||
const id = slotId(slot)
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
await waitUntilProofIsRequired(id)
|
||||
await mine()
|
||||
const challenge1 = await marketplace.getChallenge(id)
|
||||
expect(BigNumber.from(challenge1).gt(0))
|
||||
await advanceTimeTo(request.expiry + 1)
|
||||
await waitUntilCancelled(request)
|
||||
await mine()
|
||||
const challenge2 = await marketplace.getChallenge(id)
|
||||
expect(BigNumber.from(challenge2).isZero())
|
||||
})
|
||||
@ -785,14 +823,16 @@ describe("Marketplace", function () {
|
||||
})
|
||||
|
||||
async function waitUntilProofIsRequired(id) {
|
||||
await advanceTimeTo(periodEnd(periodOf(await currentTime())))
|
||||
await advanceTimeToForNextBlock(periodEnd(periodOf(await currentTime())))
|
||||
await mine()
|
||||
while (
|
||||
!(
|
||||
(await marketplace.isProofRequired(id)) &&
|
||||
(await marketplace.getPointer(id)) < 250
|
||||
)
|
||||
) {
|
||||
await advanceTime(period)
|
||||
await advanceTimeForNextBlock(period)
|
||||
await mine()
|
||||
}
|
||||
}
|
||||
|
||||
@ -813,7 +853,7 @@ describe("Marketplace", function () {
|
||||
for (let i = 0; i < slashCriterion; i++) {
|
||||
await waitUntilProofIsRequired(id)
|
||||
let missedPeriod = periodOf(await currentTime())
|
||||
await advanceTime(period)
|
||||
await advanceTimeForNextBlock(period+1)
|
||||
await marketplace.markProofAsMissing(id, missedPeriod)
|
||||
}
|
||||
const expectedBalance =
|
||||
@ -841,7 +881,7 @@ describe("Marketplace", function () {
|
||||
)
|
||||
await waitUntilProofIsRequired(slotId(slot))
|
||||
const missedPeriod = periodOf(await currentTime())
|
||||
await advanceTime(period)
|
||||
await advanceTimeForNextBlock(period+1)
|
||||
await marketplace.markProofAsMissing(slotId(slot), missedPeriod)
|
||||
}
|
||||
expect(await marketplace.slotState(slotId(slot))).to.equal(SlotState.Free)
|
||||
@ -865,7 +905,7 @@ describe("Marketplace", function () {
|
||||
)
|
||||
await waitUntilProofIsRequired(slotId(slot))
|
||||
const missedPeriod = periodOf(await currentTime())
|
||||
await advanceTime(period)
|
||||
await advanceTimeForNextBlock(period+1)
|
||||
expect(await marketplace.missingProofs(slotId(slot))).to.equal(
|
||||
missedProofs
|
||||
)
|
||||
@ -896,6 +936,7 @@ describe("Marketplace", function () {
|
||||
it("keeps request in list when cancelled", async function () {
|
||||
await marketplace.requestStorage(request)
|
||||
await waitUntilCancelled(request)
|
||||
await mine()
|
||||
expect(await marketplace.myRequests()).to.deep.equal([requestId(request)])
|
||||
})
|
||||
|
||||
@ -911,6 +952,7 @@ describe("Marketplace", function () {
|
||||
switchAccount(host)
|
||||
await waitUntilStarted(marketplace, request, proof, token)
|
||||
await waitUntilFailed(marketplace, request)
|
||||
await mine()
|
||||
switchAccount(client)
|
||||
expect(await marketplace.myRequests()).to.deep.equal([requestId(request)])
|
||||
})
|
||||
@ -963,6 +1005,7 @@ describe("Marketplace", function () {
|
||||
await token.approve(marketplace.address, request.ask.collateral)
|
||||
await marketplace.fillSlot(slot.request, slot1.index, proof)
|
||||
await waitUntilCancelled(request)
|
||||
await mine()
|
||||
expect(await marketplace.mySlots()).to.have.members([
|
||||
slotId(slot),
|
||||
slotId(slot1),
|
||||
|
@ -7,8 +7,8 @@ const {
|
||||
mine,
|
||||
ensureMinimumBlockHeight,
|
||||
currentTime,
|
||||
advanceTime,
|
||||
advanceTimeTo,
|
||||
advanceTimeForNextBlock,
|
||||
advanceTimeToForNextBlock,
|
||||
} = require("./evm")
|
||||
const { periodic } = require("./time")
|
||||
const { SlotState } = require("./requests")
|
||||
@ -44,13 +44,15 @@ describe("Proofs", function () {
|
||||
const samples = 256 // 256 samples avoids bias due to pointer downtime
|
||||
|
||||
await proofs.startRequiringProofs(slotId, probability)
|
||||
await advanceTime(period)
|
||||
await advanceTimeForNextBlock(period)
|
||||
await mine()
|
||||
let amount = 0
|
||||
for (let i = 0; i < samples; i++) {
|
||||
if (await proofs.isProofRequired(slotId)) {
|
||||
amount += 1
|
||||
}
|
||||
await advanceTime(period)
|
||||
await advanceTimeForNextBlock(period)
|
||||
await mine()
|
||||
}
|
||||
|
||||
const p = 1 / probability // expected probability
|
||||
@ -62,7 +64,8 @@ describe("Proofs", function () {
|
||||
|
||||
it("supports probability 1 (proofs are always required)", async function () {
|
||||
await proofs.startRequiringProofs(slotId, 1)
|
||||
await advanceTime(period)
|
||||
await advanceTimeForNextBlock(period)
|
||||
await mine()
|
||||
while ((await proofs.getPointer(slotId)) < downtime) {
|
||||
await mine()
|
||||
}
|
||||
@ -75,7 +78,8 @@ describe("Proofs", function () {
|
||||
await proofs.startRequiringProofs(slotId, probability)
|
||||
while (Math.floor((await currentTime()) / period) == startPeriod) {
|
||||
expect(await proofs.isProofRequired(slotId)).to.be.false
|
||||
await advanceTime(Math.floor(period / 10))
|
||||
await advanceTimeForNextBlock(Math.floor(period / 10))
|
||||
await mine()
|
||||
}
|
||||
})
|
||||
|
||||
@ -92,12 +96,14 @@ describe("Proofs", function () {
|
||||
req1 = await proofs.isProofRequired(id1)
|
||||
req2 = await proofs.isProofRequired(id2)
|
||||
req3 = await proofs.isProofRequired(id3)
|
||||
await advanceTime(period)
|
||||
await advanceTimeForNextBlock(period)
|
||||
await mine()
|
||||
}
|
||||
})
|
||||
|
||||
it("moves pointer one block at a time", async function () {
|
||||
await advanceTimeTo(periodEnd(periodOf(await currentTime())))
|
||||
await advanceTimeToForNextBlock(periodEnd(periodOf(await currentTime())))
|
||||
await mine()
|
||||
for (let i = 0; i < 256; i++) {
|
||||
let previous = await proofs.getPointer(slotId)
|
||||
await mine()
|
||||
@ -117,7 +123,7 @@ describe("Proofs", function () {
|
||||
beforeEach(async function () {
|
||||
await proofs.setSlotState(slotId, SlotState.Filled)
|
||||
await proofs.startRequiringProofs(slotId, probability)
|
||||
await advanceTimeTo(periodEnd(periodOf(await currentTime())))
|
||||
await advanceTimeToForNextBlock(periodEnd(periodOf(await currentTime())))
|
||||
await waitUntilProofWillBeRequired()
|
||||
})
|
||||
|
||||
@ -154,14 +160,17 @@ describe("Proofs", function () {
|
||||
})
|
||||
|
||||
async function waitUntilProofIsRequired(slotId) {
|
||||
await advanceTimeTo(periodEnd(periodOf(await currentTime())))
|
||||
await advanceTimeToForNextBlock(periodEnd(periodOf(await currentTime())))
|
||||
await mine()
|
||||
|
||||
while (
|
||||
!(
|
||||
(await proofs.isProofRequired(slotId)) &&
|
||||
(await proofs.getPointer(slotId)) < 250
|
||||
)
|
||||
) {
|
||||
await advanceTime(period)
|
||||
await advanceTimeForNextBlock(period)
|
||||
await mine()
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,7 +208,7 @@ describe("Proofs", function () {
|
||||
})
|
||||
|
||||
it("fails proof submission when already submitted", async function () {
|
||||
await advanceTimeTo(periodEnd(periodOf(await currentTime())))
|
||||
await advanceTimeToForNextBlock(periodEnd(periodOf(await currentTime())))
|
||||
await proofs.submitProof(slotId, proof)
|
||||
await expect(proofs.submitProof(slotId, proof)).to.be.revertedWith(
|
||||
"Proof already submitted"
|
||||
@ -210,7 +219,8 @@ describe("Proofs", function () {
|
||||
expect(await proofs.missingProofs(slotId)).to.equal(0)
|
||||
await waitUntilProofIsRequired(slotId)
|
||||
let missedPeriod = periodOf(await currentTime())
|
||||
await advanceTimeTo(periodEnd(missedPeriod))
|
||||
await advanceTimeToForNextBlock(periodEnd(missedPeriod))
|
||||
await mine()
|
||||
await proofs.markProofAsMissing(slotId, missedPeriod)
|
||||
expect(await proofs.missingProofs(slotId)).to.equal(1)
|
||||
})
|
||||
@ -226,7 +236,7 @@ describe("Proofs", function () {
|
||||
it("does not mark a proof as missing after timeout", async function () {
|
||||
await waitUntilProofIsRequired(slotId)
|
||||
let currentPeriod = periodOf(await currentTime())
|
||||
await advanceTimeTo(periodEnd(currentPeriod) + timeout)
|
||||
await advanceTimeToForNextBlock(periodEnd(currentPeriod) + timeout)
|
||||
await expect(
|
||||
proofs.markProofAsMissing(slotId, currentPeriod)
|
||||
).to.be.revertedWith("Validation timed out")
|
||||
@ -236,7 +246,8 @@ describe("Proofs", function () {
|
||||
await waitUntilProofIsRequired(slotId)
|
||||
let submittedPeriod = periodOf(await currentTime())
|
||||
await proofs.submitProof(slotId, proof)
|
||||
await advanceTimeTo(periodEnd(submittedPeriod))
|
||||
await advanceTimeToForNextBlock(periodEnd(submittedPeriod))
|
||||
await mine()
|
||||
await expect(
|
||||
proofs.markProofAsMissing(slotId, submittedPeriod)
|
||||
).to.be.revertedWith("Proof was submitted, not missing")
|
||||
@ -244,10 +255,12 @@ describe("Proofs", function () {
|
||||
|
||||
it("does not mark proof as missing when not required", async function () {
|
||||
while (await proofs.isProofRequired(slotId)) {
|
||||
await advanceTime(period)
|
||||
await advanceTimeForNextBlock(period)
|
||||
await mine()
|
||||
}
|
||||
let currentPeriod = periodOf(await currentTime())
|
||||
await advanceTimeTo(periodEnd(currentPeriod))
|
||||
await advanceTimeToForNextBlock(periodEnd(currentPeriod))
|
||||
await mine()
|
||||
await expect(
|
||||
proofs.markProofAsMissing(slotId, currentPeriod)
|
||||
).to.be.revertedWith("Proof was not required")
|
||||
@ -256,7 +269,8 @@ describe("Proofs", function () {
|
||||
it("does not mark proof as missing twice", async function () {
|
||||
await waitUntilProofIsRequired(slotId)
|
||||
let missedPeriod = periodOf(await currentTime())
|
||||
await advanceTimeTo(periodEnd(missedPeriod))
|
||||
await advanceTimeToForNextBlock(periodEnd(missedPeriod))
|
||||
await mine()
|
||||
await proofs.markProofAsMissing(slotId, missedPeriod)
|
||||
await expect(
|
||||
proofs.markProofAsMissing(slotId, missedPeriod)
|
||||
|
39
test/evm.js
39
test/evm.js
@ -14,6 +14,13 @@ async function revert() {
|
||||
await ethers.provider.send("evm_setNextBlockTimestamp", [time + 1])
|
||||
}
|
||||
|
||||
/**
|
||||
* Mines new block.
|
||||
*
|
||||
* This call increases the block's timestamp by 1!
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function mine() {
|
||||
await ethers.provider.send("evm_mine")
|
||||
}
|
||||
@ -29,16 +36,30 @@ async function currentTime() {
|
||||
return block.timestamp
|
||||
}
|
||||
|
||||
async function advanceTime(seconds) {
|
||||
/**
|
||||
* Function that advances time by adding seconds to current timestamp for **next block**.
|
||||
*
|
||||
* If you need the timestamp to be already applied for current block then mine a new block with `mine()` after this call.
|
||||
* This is mainly needed when doing assertions on top of view calls that does not create transactions and mine new block.
|
||||
*
|
||||
* @param timestamp
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function advanceTimeForNextBlock(seconds) {
|
||||
await ethers.provider.send("evm_increaseTime", [seconds])
|
||||
await mine()
|
||||
}
|
||||
|
||||
async function advanceTimeTo(timestamp) {
|
||||
if ((await currentTime()) !== timestamp) {
|
||||
await ethers.provider.send("evm_setNextBlockTimestamp", [timestamp])
|
||||
await mine()
|
||||
}
|
||||
/**
|
||||
* Function that sets specific timestamp for **next block**.
|
||||
*
|
||||
* If you need the timestamp to be already applied for current block then mine a new block with `mine()` after this call.
|
||||
* This is mainly needed when doing assertions on top of view calls that does not create transactions and mine new block.
|
||||
*
|
||||
* @param timestamp
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function advanceTimeToForNextBlock(timestamp) {
|
||||
await ethers.provider.send("evm_setNextBlockTimestamp", [timestamp])
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
@ -47,6 +68,6 @@ module.exports = {
|
||||
mine,
|
||||
ensureMinimumBlockHeight,
|
||||
currentTime,
|
||||
advanceTime,
|
||||
advanceTimeTo,
|
||||
advanceTimeForNextBlock,
|
||||
advanceTimeToForNextBlock,
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
const { advanceTimeTo } = require("./evm")
|
||||
const { advanceTimeToForNextBlock, currentTime } = require("./evm")
|
||||
const { slotId, requestId } = require("./ids")
|
||||
const {price} = require("./price");
|
||||
|
||||
async function waitUntilCancelled(request) {
|
||||
await advanceTimeTo(request.expiry + 1)
|
||||
await advanceTimeToForNextBlock(request.expiry + 1)
|
||||
}
|
||||
|
||||
async function waitUntilStarted(contract, request, proof, token) {
|
||||
@ -16,7 +16,7 @@ async function waitUntilStarted(contract, request, proof, token) {
|
||||
|
||||
async function waitUntilFinished(contract, requestId) {
|
||||
const end = (await contract.requestEnd(requestId)).toNumber()
|
||||
await advanceTimeTo(end + 1)
|
||||
await advanceTimeToForNextBlock(end + 1)
|
||||
}
|
||||
|
||||
async function waitUntilFailed(contract, request) {
|
||||
|
@ -14,6 +14,7 @@ const SlotState = {
|
||||
Finished: 2,
|
||||
Failed: 3,
|
||||
Paid: 4,
|
||||
Cancelled: 5,
|
||||
}
|
||||
|
||||
const enableRequestAssertions = function () {
|
||||
|
Loading…
x
Reference in New Issue
Block a user