[Proofs] Remove double administration of request end

The timestamp when proofs no longer are required was
stored in both the Marketplace and Proofs. It is now
only stored in the Marketplace.
This commit is contained in:
Mark Spanbroek 2023-01-10 12:11:20 +01:00 committed by markspanbroek
parent 19f93500ea
commit f8e9f3c848
4 changed files with 34 additions and 99 deletions

View File

@ -71,7 +71,6 @@ contract Marketplace is Collateral, Proofs {
RequestContext storage context = _context(id); RequestContext storage context = _context(id);
// set contract end time to `duration` from now (time request was created) // set contract end time to `duration` from now (time request was created)
context.endsAt = block.timestamp + request.ask.duration; context.endsAt = block.timestamp + request.ask.duration;
_setProofEnd(id, context.endsAt);
requestsPerClient[request.client].add(RequestId.unwrap(id)); requestsPerClient[request.client].add(RequestId.unwrap(id));
@ -97,7 +96,7 @@ contract Marketplace is Collateral, Proofs {
require(balanceOf(msg.sender) >= collateral, "Insufficient collateral"); require(balanceOf(msg.sender) >= collateral, "Insufficient collateral");
_expectProofs(slotId, requestId, request.ask.proofProbability); _expectProofs(slotId, request.ask.proofProbability);
submitProof(slotId, proof); submitProof(slotId, proof);
slot.host = msg.sender; slot.host = msg.sender;
@ -179,7 +178,6 @@ contract Marketplace is Collateral, Proofs {
context.state == RequestState.Started context.state == RequestState.Started
) { ) {
context.state = RequestState.Failed; context.state = RequestState.Failed;
_setProofEnd(requestId, block.timestamp - 1);
context.endsAt = block.timestamp - 1; context.endsAt = block.timestamp - 1;
emit RequestFailed(requestId); emit RequestFailed(requestId);
@ -322,12 +320,12 @@ contract Marketplace is Collateral, Proofs {
return _timeout(); return _timeout();
} }
function proofEnd(SlotId slotId) public view returns (uint256) { function proofEnd(SlotId slotId) public view override returns (uint256) {
return requestEnd(_slot(slotId).requestId); return requestEnd(_slot(slotId).requestId);
} }
function requestEnd(RequestId requestId) public view returns (uint256) { function requestEnd(RequestId requestId) public view returns (uint256) {
uint256 end = _end(requestId); uint256 end = _context(requestId).endsAt;
if (_requestAcceptsProofs(requestId)) { if (_requestAcceptsProofs(requestId)) {
return end; return end;
} else { } else {

View File

@ -3,7 +3,7 @@ pragma solidity ^0.8.8;
import "./Requests.sol"; import "./Requests.sol";
contract Proofs { abstract contract Proofs {
uint256 private immutable period; uint256 private immutable period;
uint256 private immutable timeout; uint256 private immutable timeout;
uint8 private immutable downtime; uint8 private immutable downtime;
@ -17,8 +17,6 @@ contract Proofs {
mapping(SlotId => bool) private slotIds; mapping(SlotId => bool) private slotIds;
mapping(SlotId => uint256) private starts; mapping(SlotId => uint256) private starts;
mapping(RequestId => uint256) private ends;
mapping(SlotId => RequestId) private requestForSlot;
mapping(SlotId => uint256) private probabilities; mapping(SlotId => uint256) private probabilities;
mapping(SlotId => uint256) private markers; mapping(SlotId => uint256) private markers;
mapping(SlotId => uint256) private missed; mapping(SlotId => uint256) private missed;
@ -33,21 +31,9 @@ contract Proofs {
return timeout; return timeout;
} }
function _end(RequestId requestId) internal view returns (uint256) { // Override this to let the proving system know when proofs for a
uint256 end = ends[requestId]; // slot are no longer required.
require(end > 0, "Proof ending doesn't exist"); function proofEnd(SlotId id) public view virtual returns (uint256);
return end;
}
function _requestId(SlotId id) internal view returns (RequestId) {
RequestId requestId = requestForSlot[id];
require(RequestId.unwrap(requestId) > 0, "request for slot doesn't exist");
return requestId;
}
function _end(SlotId id) internal view returns (uint256) {
return _end(_requestId(id));
}
function missingProofs(SlotId slotId) public view returns (uint256) { function missingProofs(SlotId slotId) public view returns (uint256) {
return missed[slotId]; return missed[slotId];
@ -63,19 +49,13 @@ contract Proofs {
/// @notice Informs the contract that proofs should be expected for id /// @notice Informs the contract that proofs should be expected for id
/// @dev Requires that the id is not already in use /// @dev Requires that the id is not already in use
/// @param id identifies the proof expectation, typically a slot id
/// @param probability The probability that a proof should be expected /// @param probability The probability that a proof should be expected
function _expectProofs( function _expectProofs(SlotId id, uint256 probability) internal {
SlotId id, // typically slot id
RequestId request, // typically request id, used so that the ending is global for all slots
uint256 probability
) internal {
require(!slotIds[id], "Slot id already in use"); require(!slotIds[id], "Slot id already in use");
slotIds[id] = true; slotIds[id] = true;
starts[id] = block.timestamp; starts[id] = block.timestamp;
probabilities[id] = probability; probabilities[id] = probability;
markers[id] = uint256(blockhash(block.number - 1)) % period; markers[id] = uint256(blockhash(block.number - 1)) % period;
requestForSlot[id] = request;
} }
function _unexpectProofs(SlotId id) internal { function _unexpectProofs(SlotId id) internal {
@ -122,7 +102,7 @@ contract Proofs {
if (proofPeriod <= periodOf(starts[id])) { if (proofPeriod <= periodOf(starts[id])) {
return (false, 0); return (false, 0);
} }
uint256 end = _end(id); uint256 end = proofEnd(id);
if (proofPeriod >= periodOf(end)) { if (proofPeriod >= periodOf(end)) {
return (false, 0); return (false, 0);
} }
@ -171,16 +151,5 @@ contract Proofs {
missed[id] += 1; missed[id] += 1;
} }
/// @notice Sets the proof end time
/// @dev Can only be set once
/// @param ending the new end time (in seconds)
function _setProofEnd(RequestId request, uint256 ending) internal {
require(
ends[request] == 0 || ending < block.timestamp,
"End exists or must be past"
);
ends[request] = ending;
}
event ProofSubmitted(SlotId id, bytes proof); event ProofSubmitted(SlotId id, bytes proof);
} }

View File

@ -5,6 +5,8 @@ import "./Proofs.sol";
// exposes internal functions of Proofs for testing // exposes internal functions of Proofs for testing
contract TestProofs is Proofs { contract TestProofs is Proofs {
mapping(SlotId => uint256) private ends;
constructor( constructor(
uint256 __period, uint256 __period,
uint256 __timeout, uint256 __timeout,
@ -16,6 +18,10 @@ contract TestProofs is Proofs {
} }
function proofEnd(SlotId slotId) public view override returns (uint256) {
return ends[slotId];
}
function period() public view returns (uint256) { function period() public view returns (uint256) {
return _period(); return _period();
} }
@ -24,16 +30,8 @@ contract TestProofs is Proofs {
return _timeout(); return _timeout();
} }
function end(RequestId id) public view returns (uint256) { function expectProofs(SlotId slot, uint256 _probability) public {
return _end(id); _expectProofs(slot, _probability);
}
function expectProofs(
SlotId slot,
RequestId request,
uint256 _probability
) public {
_expectProofs(slot, request, _probability);
} }
function unexpectProofs(SlotId id) public { function unexpectProofs(SlotId id) public {
@ -60,7 +58,7 @@ contract TestProofs is Proofs {
_markProofAsMissing(id, _period); _markProofAsMissing(id, _period);
} }
function setProofEnd(RequestId id, uint256 ending) public { function setProofEnd(SlotId id, uint256 end) public {
_setProofEnd(id, ending); ends[id] = end;
} }
} }

View File

@ -37,18 +37,18 @@ describe("Proofs", function () {
describe("general", function () { describe("general", function () {
beforeEach(async function () { beforeEach(async function () {
await proofs.setProofEnd(requestId, (await currentTime()) + duration) await proofs.setProofEnd(slotId, (await currentTime()) + duration)
}) })
it("does not allow ids to be reused", async function () { it("does not allow ids to be reused", async function () {
await proofs.expectProofs(slotId, requestId, probability) await proofs.expectProofs(slotId, probability)
await expect( await expect(proofs.expectProofs(slotId, probability)).to.be.revertedWith(
proofs.expectProofs(slotId, requestId, probability) "Slot id already in use"
).to.be.revertedWith("Slot id already in use") )
}) })
it("requires proofs with an agreed upon probability", async function () { it("requires proofs with an agreed upon probability", async function () {
await proofs.expectProofs(slotId, requestId, probability) await proofs.expectProofs(slotId, probability)
let amount = 0 let amount = 0
for (let i = 0; i < 100; i++) { for (let i = 0; i < 100; i++) {
if (await proofs.isProofRequired(slotId)) { if (await proofs.isProofRequired(slotId)) {
@ -63,7 +63,7 @@ describe("Proofs", function () {
it("requires no proofs in the start period", async function () { it("requires no proofs in the start period", async function () {
const startPeriod = Math.floor((await currentTime()) / period) const startPeriod = Math.floor((await currentTime()) / period)
const probability = 1 const probability = 1
await proofs.expectProofs(slotId, requestId, probability) await proofs.expectProofs(slotId, probability)
while (Math.floor((await currentTime()) / period) == startPeriod) { while (Math.floor((await currentTime()) / period) == startPeriod) {
expect(await proofs.isProofRequired(slotId)).to.be.false expect(await proofs.isProofRequired(slotId)).to.be.false
await advanceTime(Math.floor(period / 10)) await advanceTime(Math.floor(period / 10))
@ -72,14 +72,14 @@ describe("Proofs", function () {
it("requires no proofs in the end period", async function () { it("requires no proofs in the end period", async function () {
const probability = 1 const probability = 1
await proofs.expectProofs(slotId, requestId, probability) await proofs.expectProofs(slotId, probability)
await advanceTime(duration) await advanceTime(duration)
expect(await proofs.isProofRequired(slotId)).to.be.false expect(await proofs.isProofRequired(slotId)).to.be.false
}) })
it("requires no proofs after the end time", async function () { it("requires no proofs after the end time", async function () {
const probability = 1 const probability = 1
await proofs.expectProofs(slotId, requestId, probability) await proofs.expectProofs(slotId, probability)
await advanceTime(duration + timeout) await advanceTime(duration + timeout)
expect(await proofs.isProofRequired(slotId)).to.be.false expect(await proofs.isProofRequired(slotId)).to.be.false
}) })
@ -89,7 +89,8 @@ describe("Proofs", function () {
let id2 = hexlify(randomBytes(32)) let id2 = hexlify(randomBytes(32))
let id3 = hexlify(randomBytes(32)) let id3 = hexlify(randomBytes(32))
for (let slotId of [id1, id2, id3]) { for (let slotId of [id1, id2, id3]) {
await proofs.expectProofs(slotId, requestId, probability) await proofs.setProofEnd(slotId, (await currentTime()) + duration)
await proofs.expectProofs(slotId, probability)
} }
let req1, req2, req3 let req1, req2, req3
while (req1 === req2 && req2 === req3) { while (req1 === req2 && req2 === req3) {
@ -119,8 +120,8 @@ describe("Proofs", function () {
} }
beforeEach(async function () { beforeEach(async function () {
await proofs.setProofEnd(requestId, (await currentTime()) + duration) await proofs.setProofEnd(slotId, (await currentTime()) + duration)
await proofs.expectProofs(slotId, requestId, probability) await proofs.expectProofs(slotId, probability)
await advanceTimeTo(periodEnd(periodOf(await currentTime()))) await advanceTimeTo(periodEnd(periodOf(await currentTime())))
await waitUntilProofWillBeRequired() await waitUntilProofWillBeRequired()
}) })
@ -153,8 +154,8 @@ describe("Proofs", function () {
const proof = hexlify(randomBytes(42)) const proof = hexlify(randomBytes(42))
beforeEach(async function () { beforeEach(async function () {
await proofs.setProofEnd(requestId, (await currentTime()) + duration) await proofs.setProofEnd(slotId, (await currentTime()) + duration)
await proofs.expectProofs(slotId, requestId, probability) await proofs.expectProofs(slotId, probability)
}) })
async function waitUntilProofIsRequired(slotId) { async function waitUntilProofIsRequired(slotId) {
@ -273,35 +274,4 @@ describe("Proofs", function () {
await expect(await proofs.isProofRequired(slotId)).to.be.false await expect(await proofs.isProofRequired(slotId)).to.be.false
}) })
}) })
describe("set proof end", function () {
const proof = hexlify(randomBytes(42))
it("fails to get proof end when proof ending doesn't exist", async function () {
await expect(proofs.end(requestId)).to.be.revertedWith(
"Proof ending doesn't exist"
)
})
it("sets proof end when proof ending doesn't already exist", async function () {
let ending = (await currentTime()) + duration
await expect(proofs.setProofEnd(requestId, ending)).not.to.be.reverted
await expect(await proofs.end(requestId)).to.equal(ending)
})
it("sets proof end when proof ending exists and ending set to past", async function () {
// let ending = (await currentTime()) + duration
// await proofs.setProofEnd(requestId, ending)
let past = (await currentTime()) - 1
await expect(proofs.setProofEnd(requestId, past)).not.to.be.reverted
})
it("fails when proof ending already exists and ending set to future", async function () {
let ending = (await currentTime()) + duration
await proofs.setProofEnd(requestId, ending)
await expect(proofs.setProofEnd(requestId, ending)).to.be.revertedWith(
"End exists or must be past"
)
})
})
}) })