2021-11-01 11:30:35 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
2022-09-29 10:18:02 +00:00
|
|
|
pragma solidity ^0.8.8;
|
2021-11-01 11:30:35 +00:00
|
|
|
|
2023-01-09 13:20:59 +00:00
|
|
|
import "./Requests.sol";
|
2022-09-29 10:18:02 +00:00
|
|
|
|
2023-01-10 11:11:20 +00:00
|
|
|
abstract contract Proofs {
|
2022-03-02 14:44:58 +00:00
|
|
|
uint256 private immutable period;
|
|
|
|
uint256 private immutable timeout;
|
2022-03-10 09:19:21 +00:00
|
|
|
uint8 private immutable downtime;
|
2022-03-02 14:44:58 +00:00
|
|
|
|
2023-01-10 10:04:04 +00:00
|
|
|
constructor(uint256 __period, uint256 __timeout, uint8 __downtime) {
|
2022-03-09 10:21:19 +00:00
|
|
|
require(block.number > 256, "Insufficient block height");
|
2022-03-02 14:44:58 +00:00
|
|
|
period = __period;
|
|
|
|
timeout = __timeout;
|
2022-03-10 09:19:21 +00:00
|
|
|
downtime = __downtime;
|
2022-03-02 14:44:58 +00:00
|
|
|
}
|
|
|
|
|
2023-01-10 10:14:07 +00:00
|
|
|
mapping(SlotId => bool) private slotIds;
|
2023-01-09 13:20:59 +00:00
|
|
|
mapping(SlotId => uint256) private probabilities;
|
|
|
|
mapping(SlotId => uint256) private missed;
|
|
|
|
mapping(SlotId => mapping(uint256 => bool)) private received;
|
|
|
|
mapping(SlotId => mapping(uint256 => bool)) private missing;
|
2021-11-01 11:30:35 +00:00
|
|
|
|
2022-03-02 14:44:58 +00:00
|
|
|
function _period() internal view returns (uint256) {
|
|
|
|
return period;
|
2021-11-01 11:30:35 +00:00
|
|
|
}
|
|
|
|
|
2022-03-02 14:44:58 +00:00
|
|
|
function _timeout() internal view returns (uint256) {
|
|
|
|
return timeout;
|
2021-11-01 11:30:35 +00:00
|
|
|
}
|
|
|
|
|
2023-01-10 11:34:18 +00:00
|
|
|
// Override this to let the proving system know when proofs for a
|
|
|
|
// slot are required.
|
|
|
|
function proofStart(SlotId id) public view virtual returns (uint256);
|
|
|
|
|
2023-01-10 11:11:20 +00:00
|
|
|
// Override this to let the proving system know when proofs for a
|
|
|
|
// slot are no longer required.
|
|
|
|
function proofEnd(SlotId id) public view virtual returns (uint256);
|
2021-11-03 12:24:50 +00:00
|
|
|
|
2023-01-09 13:41:28 +00:00
|
|
|
function missingProofs(SlotId slotId) public view returns (uint256) {
|
|
|
|
return missed[slotId];
|
2021-11-01 11:30:35 +00:00
|
|
|
}
|
|
|
|
|
2022-03-08 14:58:08 +00:00
|
|
|
function periodOf(uint256 timestamp) private view returns (uint256) {
|
|
|
|
return timestamp / period;
|
|
|
|
}
|
|
|
|
|
|
|
|
function currentPeriod() private view returns (uint256) {
|
|
|
|
return periodOf(block.timestamp);
|
|
|
|
}
|
|
|
|
|
2022-09-21 09:57:26 +00:00
|
|
|
/// @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
|
2023-01-10 11:51:26 +00:00
|
|
|
function _startRequiringProofs(SlotId id, uint256 probability) internal {
|
|
|
|
require(!slotIds[id], "Proofs already required for slot");
|
2023-01-10 10:14:07 +00:00
|
|
|
slotIds[id] = true;
|
2022-03-08 14:58:08 +00:00
|
|
|
probabilities[id] = probability;
|
2021-11-01 11:30:35 +00:00
|
|
|
}
|
|
|
|
|
2023-01-10 11:51:26 +00:00
|
|
|
function _stopRequiringProofs(SlotId id) internal {
|
|
|
|
require(slotIds[id], "Proofs not required for slot");
|
2023-01-10 10:14:07 +00:00
|
|
|
slotIds[id] = false;
|
2022-09-13 07:14:57 +00:00
|
|
|
}
|
|
|
|
|
2023-01-10 10:04:04 +00:00
|
|
|
function _getPointer(
|
|
|
|
SlotId id,
|
|
|
|
uint256 proofPeriod
|
|
|
|
) internal view returns (uint8) {
|
2022-03-10 09:12:03 +00:00
|
|
|
uint256 blockNumber = block.number % 256;
|
|
|
|
uint256 periodNumber = proofPeriod % 256;
|
2023-01-09 13:20:59 +00:00
|
|
|
uint256 idOffset = uint256(SlotId.unwrap(id)) % 256;
|
2022-03-10 09:12:03 +00:00
|
|
|
uint256 pointer = (blockNumber + periodNumber + idOffset) % 256;
|
|
|
|
return uint8(pointer);
|
|
|
|
}
|
2022-03-08 14:58:08 +00:00
|
|
|
|
2023-01-09 13:20:59 +00:00
|
|
|
function _getPointer(SlotId id) internal view returns (uint8) {
|
2022-03-10 12:35:41 +00:00
|
|
|
return _getPointer(id, currentPeriod());
|
|
|
|
}
|
|
|
|
|
2022-03-10 09:12:03 +00:00
|
|
|
function _getChallenge(uint8 pointer) internal view returns (bytes32) {
|
|
|
|
bytes32 hash = blockhash(block.number - 1 - pointer);
|
|
|
|
assert(uint256(hash) != 0);
|
|
|
|
return keccak256(abi.encode(hash));
|
|
|
|
}
|
2022-03-08 14:58:08 +00:00
|
|
|
|
2023-01-10 10:04:04 +00:00
|
|
|
function _getChallenge(
|
|
|
|
SlotId id,
|
|
|
|
uint256 proofPeriod
|
|
|
|
) internal view returns (bytes32) {
|
2022-03-10 09:12:03 +00:00
|
|
|
return _getChallenge(_getPointer(id, proofPeriod));
|
2021-11-01 11:30:35 +00:00
|
|
|
}
|
|
|
|
|
2023-01-09 13:20:59 +00:00
|
|
|
function _getChallenge(SlotId id) internal view returns (bytes32) {
|
2022-03-10 12:04:46 +00:00
|
|
|
return _getChallenge(id, currentPeriod());
|
|
|
|
}
|
|
|
|
|
2023-01-10 10:04:04 +00:00
|
|
|
function _getProofRequirement(
|
|
|
|
SlotId id,
|
|
|
|
uint256 proofPeriod
|
|
|
|
) internal view returns (bool isRequired, uint8 pointer) {
|
2023-01-10 11:34:18 +00:00
|
|
|
if (proofPeriod <= periodOf(proofStart(id))) {
|
2022-04-05 09:27:02 +00:00
|
|
|
return (false, 0);
|
2022-03-10 09:12:03 +00:00
|
|
|
}
|
2023-01-10 11:11:20 +00:00
|
|
|
uint256 end = proofEnd(id);
|
2022-09-21 09:57:26 +00:00
|
|
|
if (proofPeriod >= periodOf(end)) {
|
2022-04-05 09:27:02 +00:00
|
|
|
return (false, 0);
|
2022-03-10 09:12:03 +00:00
|
|
|
}
|
2022-04-05 09:27:02 +00:00
|
|
|
pointer = _getPointer(id, proofPeriod);
|
2022-03-10 09:12:03 +00:00
|
|
|
bytes32 challenge = _getChallenge(pointer);
|
2022-03-10 09:19:21 +00:00
|
|
|
uint256 probability = (probabilities[id] * (256 - downtime)) / 256;
|
2023-01-10 10:14:07 +00:00
|
|
|
isRequired = slotIds[id] && uint256(challenge) % probability == 0;
|
2022-04-05 09:27:02 +00:00
|
|
|
}
|
|
|
|
|
2023-01-10 10:04:04 +00:00
|
|
|
function _isProofRequired(
|
|
|
|
SlotId id,
|
|
|
|
uint256 proofPeriod
|
|
|
|
) internal view returns (bool) {
|
2022-04-05 09:27:02 +00:00
|
|
|
bool isRequired;
|
|
|
|
uint8 pointer;
|
|
|
|
(isRequired, pointer) = _getProofRequirement(id, proofPeriod);
|
|
|
|
return isRequired && pointer >= downtime;
|
2021-11-01 11:30:35 +00:00
|
|
|
}
|
|
|
|
|
2023-01-09 13:20:59 +00:00
|
|
|
function _isProofRequired(SlotId id) internal view returns (bool) {
|
2022-03-08 14:58:08 +00:00
|
|
|
return _isProofRequired(id, currentPeriod());
|
|
|
|
}
|
|
|
|
|
2023-01-09 13:20:59 +00:00
|
|
|
function _willProofBeRequired(SlotId id) internal view returns (bool) {
|
2022-04-05 09:27:02 +00:00
|
|
|
bool isRequired;
|
|
|
|
uint8 pointer;
|
|
|
|
(isRequired, pointer) = _getProofRequirement(id, currentPeriod());
|
|
|
|
return isRequired && pointer < downtime;
|
|
|
|
}
|
|
|
|
|
2023-01-09 14:19:00 +00:00
|
|
|
function submitProof(SlotId id, bytes calldata proof) public {
|
2022-04-12 06:43:47 +00:00
|
|
|
require(proof.length > 0, "Invalid proof"); // TODO: replace by actual check
|
2022-03-08 14:58:08 +00:00
|
|
|
require(!received[id][currentPeriod()], "Proof already submitted");
|
|
|
|
received[id][currentPeriod()] = true;
|
2022-04-12 06:43:47 +00:00
|
|
|
emit ProofSubmitted(id, proof);
|
2021-11-01 11:30:35 +00:00
|
|
|
}
|
|
|
|
|
2023-01-09 13:20:59 +00:00
|
|
|
function _markProofAsMissing(SlotId id, uint256 missedPeriod) internal {
|
2022-03-08 14:58:08 +00:00
|
|
|
uint256 periodEnd = (missedPeriod + 1) * period;
|
|
|
|
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");
|
|
|
|
require(_isProofRequired(id, missedPeriod), "Proof was not required");
|
|
|
|
require(!missing[id][missedPeriod], "Proof already marked as missing");
|
|
|
|
missing[id][missedPeriod] = true;
|
2021-11-01 11:30:35 +00:00
|
|
|
missed[id] += 1;
|
|
|
|
}
|
2022-04-12 06:43:47 +00:00
|
|
|
|
2023-01-09 13:20:59 +00:00
|
|
|
event ProofSubmitted(SlotId id, bytes proof);
|
2021-11-01 11:30:35 +00:00
|
|
|
}
|