2021-11-01 11:30:35 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
pragma solidity ^0.8.0;
|
|
|
|
|
|
|
|
contract Proofs {
|
2022-03-02 14:44:58 +00:00
|
|
|
uint256 private immutable period;
|
|
|
|
uint256 private immutable timeout;
|
|
|
|
|
|
|
|
constructor(uint256 __period, uint256 __timeout) {
|
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-02-09 13:17:23 +00:00
|
|
|
mapping(bytes32 => bool) private ids;
|
|
|
|
mapping(bytes32 => uint256) private starts;
|
|
|
|
mapping(bytes32 => uint256) private ends;
|
2022-03-08 14:58:08 +00:00
|
|
|
mapping(bytes32 => uint256) private probabilities;
|
2022-02-09 13:17:23 +00:00
|
|
|
mapping(bytes32 => uint256) private markers;
|
|
|
|
mapping(bytes32 => uint256) private missed;
|
|
|
|
mapping(bytes32 => mapping(uint256 => bool)) private received;
|
|
|
|
mapping(bytes32 => 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
|
|
|
}
|
|
|
|
|
2022-02-09 13:17:23 +00:00
|
|
|
function _end(bytes32 id) internal view returns (uint256) {
|
2021-11-03 12:24:50 +00:00
|
|
|
return ends[id];
|
|
|
|
}
|
|
|
|
|
2022-02-09 13:17:23 +00:00
|
|
|
function _missed(bytes32 id) internal view returns (uint256) {
|
2021-11-01 11:30:35 +00:00
|
|
|
return missed[id];
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
function _expectProofs(
|
|
|
|
bytes32 id,
|
|
|
|
uint256 probability,
|
|
|
|
uint256 duration
|
|
|
|
) internal {
|
2021-11-01 11:30:35 +00:00
|
|
|
require(!ids[id], "Proof id already in use");
|
|
|
|
ids[id] = true;
|
2022-03-08 14:58:08 +00:00
|
|
|
starts[id] = block.timestamp;
|
|
|
|
ends[id] = block.timestamp + duration;
|
|
|
|
probabilities[id] = probability;
|
2022-02-09 13:17:23 +00:00
|
|
|
markers[id] = uint256(blockhash(block.number - 1)) % period;
|
2021-11-01 11:30:35 +00:00
|
|
|
}
|
|
|
|
|
2022-03-08 14:58:08 +00:00
|
|
|
function _getChallenges(bytes32 id, uint256 proofperiod)
|
2022-02-09 13:17:23 +00:00
|
|
|
internal
|
|
|
|
view
|
2022-03-08 14:58:08 +00:00
|
|
|
returns (Challenge memory challenge1, Challenge memory challenge2)
|
2021-11-01 11:30:35 +00:00
|
|
|
{
|
2022-03-08 14:58:08 +00:00
|
|
|
if (
|
|
|
|
proofperiod <= periodOf(starts[id]) || proofperiod >= periodOf(ends[id])
|
|
|
|
) {
|
|
|
|
bytes32 nullChallenge;
|
|
|
|
return (Challenge(false, nullChallenge), Challenge(false, nullChallenge));
|
2021-11-03 16:02:12 +00:00
|
|
|
}
|
2022-03-08 14:58:08 +00:00
|
|
|
|
|
|
|
uint256 blocknumber = block.number % 256;
|
|
|
|
uint256 periodnumber = proofperiod % 256;
|
|
|
|
uint256 idoffset = uint256(id) % 256;
|
|
|
|
|
|
|
|
uint256 pointer1 = (blocknumber + periodnumber + idoffset) % 256;
|
|
|
|
uint256 pointer2 = (blocknumber + periodnumber + idoffset + 128) % 256;
|
|
|
|
|
|
|
|
bytes32 blockhash1 = blockhash(block.number - 1 - pointer1);
|
|
|
|
bytes32 blockhash2 = blockhash(block.number - 1 - pointer2);
|
|
|
|
|
|
|
|
assert(uint256(blockhash1) != 0);
|
|
|
|
assert(uint256(blockhash2) != 0);
|
|
|
|
|
|
|
|
challenge1.challenge = keccak256(abi.encode(blockhash1));
|
|
|
|
challenge2.challenge = keccak256(abi.encode(blockhash2));
|
|
|
|
|
|
|
|
challenge1.isProofRequired =
|
|
|
|
uint256(challenge1.challenge) % probabilities[id] == 0;
|
|
|
|
challenge2.isProofRequired =
|
|
|
|
uint256(challenge2.challenge) % probabilities[id] == 0;
|
2021-11-01 11:30:35 +00:00
|
|
|
}
|
|
|
|
|
2022-03-08 14:58:08 +00:00
|
|
|
function _isProofRequired(bytes32 id, uint256 proofPeriod)
|
|
|
|
internal
|
|
|
|
view
|
|
|
|
returns (bool)
|
|
|
|
{
|
|
|
|
Challenge memory challenge1;
|
|
|
|
Challenge memory challenge2;
|
|
|
|
(challenge1, challenge2) = _getChallenges(id, proofPeriod);
|
|
|
|
return challenge1.isProofRequired && challenge2.isProofRequired;
|
2021-11-01 11:30:35 +00:00
|
|
|
}
|
|
|
|
|
2022-03-08 14:58:08 +00:00
|
|
|
function _isProofRequired(bytes32 id) internal view returns (bool) {
|
|
|
|
return _isProofRequired(id, currentPeriod());
|
|
|
|
}
|
|
|
|
|
|
|
|
function _submitProof(bytes32 id, bool proof) internal {
|
2021-11-01 11:30:35 +00:00
|
|
|
require(proof, "Invalid proof"); // TODO: replace bool by actual proof
|
2022-03-08 14:58:08 +00:00
|
|
|
require(!received[id][currentPeriod()], "Proof already submitted");
|
|
|
|
received[id][currentPeriod()] = true;
|
2021-11-01 11:30:35 +00:00
|
|
|
}
|
|
|
|
|
2022-03-08 14:58:08 +00:00
|
|
|
function _markProofAsMissing(bytes32 id, uint256 missedPeriod) internal {
|
|
|
|
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-03-08 14:58:08 +00:00
|
|
|
|
|
|
|
struct Challenge {
|
|
|
|
bool isProofRequired;
|
|
|
|
bytes32 challenge;
|
|
|
|
}
|
2021-11-01 11:30:35 +00:00
|
|
|
}
|