From 036a2144278b7537e685cb4d8378a44b3f474496 Mon Sep 17 00:00:00 2001 From: Mark Spanbroek Date: Wed, 2 Mar 2022 15:44:58 +0100 Subject: [PATCH] Proof period and proof timeout are network constants now --- contracts/Marketplace.sol | 2 -- contracts/Proofs.sol | 52 ++++++++++++++------------------------- contracts/Storage.sol | 27 ++++++++++---------- contracts/TestProofs.sol | 24 +++++++++--------- deploy/storage.js | 11 ++++++++- test/Proofs.test.js | 33 ++++++++----------------- test/Storage.test.js | 3 ++- test/examples.js | 2 -- test/ids.js | 4 --- 9 files changed, 67 insertions(+), 91 deletions(-) diff --git a/contracts/Marketplace.sol b/contracts/Marketplace.sol index b477db9..79adb06 100644 --- a/contracts/Marketplace.sol +++ b/contracts/Marketplace.sol @@ -98,8 +98,6 @@ contract Marketplace is Collateral { uint256 duration; uint256 size; bytes32 contentHash; - uint256 proofPeriod; - uint256 proofTimeout; uint256 maxPrice; uint256 expiry; bytes32 nonce; diff --git a/contracts/Proofs.sol b/contracts/Proofs.sol index f6b911b..5c7ff95 100644 --- a/contracts/Proofs.sol +++ b/contracts/Proofs.sol @@ -2,9 +2,15 @@ pragma solidity ^0.8.0; contract Proofs { + uint256 private immutable period; + uint256 private immutable timeout; + + constructor(uint256 __period, uint256 __timeout) { + period = __period; + timeout = __timeout; + } + mapping(bytes32 => bool) private ids; - mapping(bytes32 => uint256) private periods; - mapping(bytes32 => uint256) private timeouts; mapping(bytes32 => uint256) private starts; mapping(bytes32 => uint256) private ends; mapping(bytes32 => uint256) private markers; @@ -12,12 +18,12 @@ contract Proofs { mapping(bytes32 => mapping(uint256 => bool)) private received; mapping(bytes32 => mapping(uint256 => bool)) private missing; - function _period(bytes32 id) internal view returns (uint256) { - return periods[id]; + function _period() internal view returns (uint256) { + return period; } - function _timeout(bytes32 id) internal view returns (uint256) { - return timeouts[id]; + function _timeout() internal view returns (uint256) { + return timeout; } function _end(bytes32 id) internal view returns (uint256) { @@ -28,24 +34,9 @@ contract Proofs { return missed[id]; } - // Checks that proof timeout is <= 128. Only the latest 256 blocks can be - // checked in a smart contract, so that leaves a period of at least 128 blocks - // after timeout for a validator to signal the absence of a proof. - function _checkTimeout(uint256 timeout) private pure { - require(timeout <= 128, "Invalid proof timeout > 128"); - } - - function _expectProofs( - bytes32 id, - uint256 period, - uint256 timeout, - uint256 duration - ) internal { + function _expectProofs(bytes32 id, uint256 duration) internal { require(!ids[id], "Proof id already in use"); - _checkTimeout(timeout); ids[id] = true; - periods[id] = period; - timeouts[id] = timeout; starts[id] = block.number; ends[id] = block.number + duration + 2 * timeout; markers[id] = uint256(blockhash(block.number - 1)) % period; @@ -64,15 +55,11 @@ contract Proofs { return false; } bytes32 hash = blockhash(blocknumber - 1); - return hash != 0 && uint256(hash) % periods[id] == markers[id]; + return hash != 0 && uint256(hash) % period == markers[id]; } - function _isProofTimedOut(bytes32 id, uint256 blocknumber) - internal - view - returns (bool) - { - return block.number >= blocknumber + timeouts[id]; + function _isProofTimedOut(uint256 blocknumber) internal view returns (bool) { + return block.number >= blocknumber + timeout; } function _submitProof( @@ -85,16 +72,13 @@ contract Proofs { _isProofRequired(id, blocknumber), "No proof required for this block" ); - require( - !_isProofTimedOut(id, blocknumber), - "Proof not allowed after timeout" - ); + require(!_isProofTimedOut(blocknumber), "Proof not allowed after timeout"); require(!received[id][blocknumber], "Proof already submitted"); received[id][blocknumber] = true; } function _markProofAsMissing(bytes32 id, uint256 blocknumber) internal { - require(_isProofTimedOut(id, blocknumber), "Proof has not timed out yet"); + require(_isProofTimedOut(blocknumber), "Proof has not timed out yet"); require(!received[id][blocknumber], "Proof was submitted, not missing"); require(_isProofRequired(id, blocknumber), "Proof was not required"); require(!missing[id][blocknumber], "Proof already marked as missing"); diff --git a/contracts/Storage.sol b/contracts/Storage.sol index e5b2ef7..3ac8ec1 100644 --- a/contracts/Storage.sol +++ b/contracts/Storage.sol @@ -14,10 +14,12 @@ contract Storage is Collateral, Marketplace, Proofs { constructor( IERC20 token, + uint256 _proofPeriod, + uint256 _proofTimeout, uint256 _collateralAmount, uint256 _slashMisses, uint256 _slashPercentage - ) Marketplace(token, _collateralAmount) { + ) Marketplace(token, _collateralAmount) Proofs(_proofPeriod, _proofTimeout) { collateralAmount = _collateralAmount; slashMisses = _slashMisses; slashPercentage = _slashPercentage; @@ -27,12 +29,7 @@ contract Storage is Collateral, Marketplace, Proofs { Offer storage offer = _offer(id); require(msg.sender == offer.host, "Only host can call this function"); Request storage request = _request(offer.requestId); - _expectProofs( - id, - request.proofPeriod, - request.proofTimeout, - request.duration - ); + _expectProofs(id, request.duration); } function finishContract(bytes32 id) public { @@ -43,6 +40,14 @@ contract Storage is Collateral, Marketplace, Proofs { require(token.transfer(offer.host, offer.price), "Payment failed"); } + function proofPeriod() public view returns (uint256) { + return _period(); + } + + function proofTimeout() public view returns (uint256) { + return _timeout(); + } + function proofEnd(bytes32 contractId) public view returns (uint256) { return _end(contractId); } @@ -59,12 +64,8 @@ contract Storage is Collateral, Marketplace, Proofs { return _isProofRequired(contractId, blocknumber); } - function isProofTimedOut(bytes32 contractId, uint256 blocknumber) - public - view - returns (bool) - { - return _isProofTimedOut(contractId, blocknumber); + function isProofTimedOut(uint256 blocknumber) public view returns (bool) { + return _isProofTimedOut(blocknumber); } function submitProof( diff --git a/contracts/TestProofs.sol b/contracts/TestProofs.sol index e477e20..2641131 100644 --- a/contracts/TestProofs.sol +++ b/contracts/TestProofs.sol @@ -5,12 +5,19 @@ import "./Proofs.sol"; // exposes internal functions of Proofs for testing contract TestProofs is Proofs { - function period(bytes32 id) public view returns (uint256) { - return _period(id); + constructor(uint256 __period, uint256 __timeout) + Proofs(__period, __timeout) + // solhint-disable-next-line no-empty-blocks + { + } - function timeout(bytes32 id) public view returns (uint256) { - return _timeout(id); + function period() public view returns (uint256) { + return _period(); + } + + function timeout() public view returns (uint256) { + return _timeout(); } function end(bytes32 id) public view returns (uint256) { @@ -21,13 +28,8 @@ contract TestProofs is Proofs { return _missed(id); } - function expectProofs( - bytes32 id, - uint256 _period, - uint256 _timeout, - uint256 _duration - ) public { - _expectProofs(id, _period, _timeout, _duration); + function expectProofs(bytes32 id, uint256 _duration) public { + _expectProofs(id, _duration); } function isProofRequired(bytes32 id, uint256 blocknumber) diff --git a/deploy/storage.js b/deploy/storage.js index d333b4c..9633724 100644 --- a/deploy/storage.js +++ b/deploy/storage.js @@ -1,9 +1,18 @@ module.exports = async ({ deployments, getNamedAccounts }) => { const token = await deployments.get("TestToken") + const proofPeriod = 10 + const proofTimeout = 5 const collateralAmount = 100 const slashMisses = 3 const slashPercentage = 10 - const args = [token.address, collateralAmount, slashMisses, slashPercentage] + const args = [ + token.address, + proofPeriod, + proofTimeout, + collateralAmount, + slashMisses, + slashPercentage, + ] const { deployer } = await getNamedAccounts() await deployments.deploy("Storage", { args, from: deployer }) } diff --git a/test/Proofs.test.js b/test/Proofs.test.js index a4e0951..0b4b53e 100644 --- a/test/Proofs.test.js +++ b/test/Proofs.test.js @@ -12,40 +12,27 @@ describe("Proofs", function () { beforeEach(async function () { const Proofs = await ethers.getContractFactory("TestProofs") - proofs = await Proofs.deploy() + proofs = await Proofs.deploy(period, timeout) }) - it("indicates that proofs are required", async function () { - await proofs.expectProofs(id, period, timeout, duration) - expect(await proofs.period(id)).to.equal(period) - expect(await proofs.timeout(id)).to.equal(timeout) - }) - - it("calculates an endtime based on duration and timeout", async function () { - await proofs.expectProofs(id, period, timeout, duration) + it("calculates an end time based on duration and timeout", async function () { + await proofs.expectProofs(id, duration) let start = await minedBlockNumber() let end = start + duration + 2 * timeout expect(await proofs.end(id)).to.equal(end) }) it("does not allow ids to be reused", async function () { - await proofs.expectProofs(id, period, timeout, duration) - await expect( - proofs.expectProofs(id, period, timeout, duration) - ).to.be.revertedWith("Proof id already in use") - }) - - it("does not allow a proof timeout that is too large", async function () { - let invalidTimeout = 129 // max proof timeout is 128 blocks - await expect( - proofs.expectProofs(id, period, invalidTimeout, duration) - ).to.be.revertedWith("Invalid proof timeout") + await proofs.expectProofs(id, duration) + await expect(proofs.expectProofs(id, duration)).to.be.revertedWith( + "Proof id already in use" + ) }) it("requires on average a proof every period", async function () { let blocks = 600 let amount = 0 - await proofs.expectProofs(id, period, timeout, blocks) + await proofs.expectProofs(id, blocks) for (let i = 0; i < blocks; i++) { await mineBlock() if (await proofs.isProofRequired(id, await minedBlockNumber())) { @@ -60,7 +47,7 @@ describe("Proofs", function () { for (let i = 0; i < 4 * period; i++) { mineBlock() } - await proofs.expectProofs(id, period, timeout, duration) + await proofs.expectProofs(id, duration) let start = await minedBlockNumber() for (let i = 1; i < 4 * period; i++) { expect(await proofs.isProofRequired(id, start - i)).to.be.false @@ -69,7 +56,7 @@ describe("Proofs", function () { describe("when proofs are required", async function () { beforeEach(async function () { - await proofs.expectProofs(id, period, timeout, duration) + await proofs.expectProofs(id, duration) }) async function mineUntilProofIsRequired(id) { diff --git a/test/Storage.test.js b/test/Storage.test.js index cdb91c7..5fefe23 100644 --- a/test/Storage.test.js +++ b/test/Storage.test.js @@ -123,7 +123,8 @@ describe("Storage", function () { mineBlock() } const blocknumber = await minedBlockNumber() - for (let i = 0; i < request.proofTimeout; i++) { + const timeout = await storage.proofTimeout() + for (let i = 0; i < timeout; i++) { mineBlock() } await storage.markProofAsMissing(id, blocknumber) diff --git a/test/examples.js b/test/examples.js index 67bc81b..e1ed9c9 100644 --- a/test/examples.js +++ b/test/examples.js @@ -7,8 +7,6 @@ const exampleRequest = () => ({ duration: 150, // 150 blocks ≈ half an hour size: 1 * 1024 * 1024 * 1024, // 1 Gigabyte contentHash: sha256("0xdeadbeef"), - proofPeriod: 8, // 8 blocks ≈ 2 minutes - proofTimeout: 4, // 4 blocks ≈ 1 minute maxPrice: 84, expiry: now() + hours(1), nonce: hexlify(randomBytes(32)), diff --git a/test/ids.js b/test/ids.js index e7be9c8..0f518d3 100644 --- a/test/ids.js +++ b/test/ids.js @@ -11,8 +11,6 @@ function requestId(request) { "bytes32", "uint256", "uint256", - "uint256", - "uint256", "bytes32", ], requestToArray(request) @@ -35,8 +33,6 @@ function requestToArray(request) { request.duration, request.size, request.contentHash, - request.proofPeriod, - request.proofTimeout, request.maxPrice, request.expiry, request.nonce,