From ad155be5a14e75583f0289b7c213622678954024 Mon Sep 17 00:00:00 2001 From: Mark Spanbroek Date: Tue, 10 Jan 2023 14:16:32 +0100 Subject: [PATCH] [Proofs] Extract period related types and function --- contracts/Marketplace.sol | 4 +-- contracts/Periods.sol | 40 +++++++++++++++++++++++++ contracts/Proofs.sol | 61 ++++++++++++++++----------------------- contracts/TestProofs.sol | 6 +--- 4 files changed, 68 insertions(+), 43 deletions(-) create mode 100644 contracts/Periods.sol diff --git a/contracts/Marketplace.sol b/contracts/Marketplace.sol index 94c2fd2..98fbdba 100644 --- a/contracts/Marketplace.sol +++ b/contracts/Marketplace.sol @@ -129,7 +129,7 @@ contract Marketplace is Collateral, Proofs { function markProofAsMissing( SlotId slotId, - uint256 period + Period period ) public slotMustAcceptProofs(slotId) { _markProofAsMissing(slotId, period); address host = getHost(slotId); @@ -313,7 +313,7 @@ contract Marketplace is Collateral, Proofs { } function proofPeriod() public view returns (uint256) { - return _period(); + return secondsPerPeriod; } function proofTimeout() public view returns (uint256) { diff --git a/contracts/Periods.sol b/contracts/Periods.sol new file mode 100644 index 0000000..144f9f8 --- /dev/null +++ b/contracts/Periods.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.8; + +contract Periods { + type Period is uint256; + + uint256 internal immutable secondsPerPeriod; + + constructor(uint256 _secondsPerPeriod) { + secondsPerPeriod = _secondsPerPeriod; + } + + function periodOf(uint256 timestamp) internal view returns (Period) { + return Period.wrap(timestamp / secondsPerPeriod); + } + + function blockPeriod() internal view returns (Period) { + return periodOf(block.timestamp); + } + + function nextPeriod(Period period) internal pure returns (Period) { + return Period.wrap(Period.unwrap(period) + 1); + } + + function periodStart(Period period) internal view returns (uint256) { + return Period.unwrap(period) * secondsPerPeriod; + } + + function periodEnd(Period period) internal view returns (uint256) { + return periodStart(nextPeriod(period)); + } + + function isBefore(Period a, Period b) internal pure returns (bool) { + return Period.unwrap(a) < Period.unwrap(b); + } + + function isAfter(Period a, Period b) internal pure returns (bool) { + return isBefore(b, a); + } +} diff --git a/contracts/Proofs.sol b/contracts/Proofs.sol index 9c6434b..97601f5 100644 --- a/contracts/Proofs.sol +++ b/contracts/Proofs.sol @@ -2,15 +2,18 @@ pragma solidity ^0.8.8; import "./Requests.sol"; +import "./Periods.sol"; -abstract contract Proofs { - uint256 private immutable period; +abstract contract Proofs is Periods { uint256 private immutable timeout; uint8 private immutable downtime; - constructor(uint256 __period, uint256 __timeout, uint8 __downtime) { + constructor( + uint256 __period, + uint256 __timeout, + uint8 __downtime + ) Periods(__period) { require(block.number > 256, "Insufficient block height"); - period = __period; timeout = __timeout; downtime = __downtime; } @@ -18,12 +21,8 @@ abstract contract Proofs { mapping(SlotId => bool) private slotIds; mapping(SlotId => uint256) private probabilities; mapping(SlotId => uint256) private missed; - mapping(SlotId => mapping(uint256 => bool)) private received; - mapping(SlotId => mapping(uint256 => bool)) private missing; - - function _period() internal view returns (uint256) { - return period; - } + mapping(SlotId => mapping(Period => bool)) private received; + mapping(SlotId => mapping(Period => bool)) private missing; function _timeout() internal view returns (uint256) { return timeout; @@ -41,14 +40,6 @@ abstract contract Proofs { return missed[slotId]; } - function periodOf(uint256 timestamp) private view returns (uint256) { - return timestamp / period; - } - - function currentPeriod() private view returns (uint256) { - return periodOf(block.timestamp); - } - /// @notice Informs the contract that proofs should be expected for id /// @dev Requires that the id is not already in use /// @param probability The probability that a proof should be expected @@ -65,17 +56,17 @@ abstract contract Proofs { function _getPointer( SlotId id, - uint256 proofPeriod + Period proofPeriod ) internal view returns (uint8) { uint256 blockNumber = block.number % 256; - uint256 periodNumber = proofPeriod % 256; + uint256 periodNumber = Period.unwrap(proofPeriod) % 256; uint256 idOffset = uint256(SlotId.unwrap(id)) % 256; uint256 pointer = (blockNumber + periodNumber + idOffset) % 256; return uint8(pointer); } function _getPointer(SlotId id) internal view returns (uint8) { - return _getPointer(id, currentPeriod()); + return _getPointer(id, blockPeriod()); } function _getChallenge(uint8 pointer) internal view returns (bytes32) { @@ -86,24 +77,22 @@ abstract contract Proofs { function _getChallenge( SlotId id, - uint256 proofPeriod + Period proofPeriod ) internal view returns (bytes32) { return _getChallenge(_getPointer(id, proofPeriod)); } function _getChallenge(SlotId id) internal view returns (bytes32) { - return _getChallenge(id, currentPeriod()); + return _getChallenge(id, blockPeriod()); } function _getProofRequirement( SlotId id, - uint256 proofPeriod + Period proofPeriod ) internal view returns (bool isRequired, uint8 pointer) { - if (proofPeriod <= periodOf(proofStart(id))) { - return (false, 0); - } - uint256 end = proofEnd(id); - if (proofPeriod >= periodOf(end)) { + Period start = periodOf(proofStart(id)); + Period end = periodOf(proofEnd(id)); + if (!isAfter(proofPeriod, start) || !isBefore(proofPeriod, end)) { return (false, 0); } pointer = _getPointer(id, proofPeriod); @@ -114,7 +103,7 @@ abstract contract Proofs { function _isProofRequired( SlotId id, - uint256 proofPeriod + Period proofPeriod ) internal view returns (bool) { bool isRequired; uint8 pointer; @@ -123,25 +112,25 @@ abstract contract Proofs { } function _isProofRequired(SlotId id) internal view returns (bool) { - return _isProofRequired(id, currentPeriod()); + return _isProofRequired(id, blockPeriod()); } function _willProofBeRequired(SlotId id) internal view returns (bool) { bool isRequired; uint8 pointer; - (isRequired, pointer) = _getProofRequirement(id, currentPeriod()); + (isRequired, pointer) = _getProofRequirement(id, blockPeriod()); return isRequired && pointer < downtime; } function submitProof(SlotId id, bytes calldata proof) public { require(proof.length > 0, "Invalid proof"); // TODO: replace by actual check - require(!received[id][currentPeriod()], "Proof already submitted"); - received[id][currentPeriod()] = true; + require(!received[id][blockPeriod()], "Proof already submitted"); + received[id][blockPeriod()] = true; emit ProofSubmitted(id, proof); } - function _markProofAsMissing(SlotId id, uint256 missedPeriod) internal { - uint256 periodEnd = (missedPeriod + 1) * period; + function _markProofAsMissing(SlotId id, Period missedPeriod) internal { + uint256 periodEnd = periodEnd(missedPeriod); require(periodEnd < block.timestamp, "Period has not ended yet"); require(block.timestamp < periodEnd + timeout, "Validation timed out"); require(!received[id][missedPeriod], "Proof was submitted, not missing"); diff --git a/contracts/TestProofs.sol b/contracts/TestProofs.sol index 25112a6..af5d9d7 100644 --- a/contracts/TestProofs.sol +++ b/contracts/TestProofs.sol @@ -27,10 +27,6 @@ contract TestProofs is Proofs { return ends[slotId]; } - function period() public view returns (uint256) { - return _period(); - } - function timeout() public view returns (uint256) { return _timeout(); } @@ -59,7 +55,7 @@ contract TestProofs is Proofs { return _getPointer(id); } - function markProofAsMissing(SlotId id, uint256 _period) public { + function markProofAsMissing(SlotId id, Period _period) public { _markProofAsMissing(id, _period); }