2021-11-01 11:30:35 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
pragma solidity ^0.8.0;
|
|
|
|
|
|
|
|
contract Proofs {
|
|
|
|
|
|
|
|
mapping(bytes32=>bool) private ids;
|
|
|
|
mapping(bytes32=>uint) private periods;
|
|
|
|
mapping(bytes32=>uint) private timeouts;
|
2021-11-03 12:24:50 +00:00
|
|
|
mapping(bytes32=>uint) private ends;
|
2021-11-01 11:30:35 +00:00
|
|
|
mapping(bytes32=>uint) private markers;
|
|
|
|
mapping(bytes32=>uint) private missed;
|
|
|
|
mapping(bytes32=>mapping(uint=>bool)) private received;
|
|
|
|
mapping(bytes32=>mapping(uint=>bool)) private missing;
|
|
|
|
|
|
|
|
function _period(bytes32 id) internal view returns (uint) {
|
|
|
|
return periods[id];
|
|
|
|
}
|
|
|
|
|
|
|
|
function _timeout(bytes32 id) internal view returns (uint) {
|
|
|
|
return timeouts[id];
|
|
|
|
}
|
|
|
|
|
2021-11-03 12:24:50 +00:00
|
|
|
function _end(bytes32 id) internal view returns (uint) {
|
|
|
|
return ends[id];
|
|
|
|
}
|
|
|
|
|
2021-11-01 11:30:35 +00:00
|
|
|
function _missed(bytes32 id) internal view returns (uint) {
|
|
|
|
return missed[id];
|
|
|
|
}
|
|
|
|
|
2021-11-01 14:28:22 +00:00
|
|
|
// 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(uint timeout) private pure {
|
|
|
|
require(timeout <= 128, "Invalid proof timeout, needs to be <= 128");
|
|
|
|
}
|
|
|
|
|
2021-11-03 12:24:50 +00:00
|
|
|
function _expectProofs(
|
|
|
|
bytes32 id,
|
|
|
|
uint period,
|
|
|
|
uint timeout,
|
|
|
|
uint duration
|
|
|
|
) internal {
|
2021-11-01 11:30:35 +00:00
|
|
|
require(!ids[id], "Proof id already in use");
|
2021-11-01 14:28:22 +00:00
|
|
|
_checkTimeout(timeout);
|
2021-11-01 11:30:35 +00:00
|
|
|
ids[id] = true;
|
|
|
|
periods[id] = period;
|
|
|
|
timeouts[id] = timeout;
|
2021-11-03 12:24:50 +00:00
|
|
|
ends[id] = block.number + duration + 2 * timeout;
|
2021-11-01 11:30:35 +00:00
|
|
|
markers[id] = uint(blockhash(block.number - 1)) % period;
|
|
|
|
}
|
|
|
|
|
2021-11-02 08:46:15 +00:00
|
|
|
// Check whether a proof is required at the time of the block with the
|
|
|
|
// specified block number. A proof has to be submitted within the proof
|
|
|
|
// timeout for it to be valid. Whether a proof is required is determined
|
|
|
|
// randomly, but on average it is once every proof period.
|
2021-11-01 11:30:35 +00:00
|
|
|
function _isProofRequired(
|
|
|
|
bytes32 id,
|
|
|
|
uint blocknumber
|
|
|
|
)
|
|
|
|
internal view
|
|
|
|
returns (bool)
|
|
|
|
{
|
2021-11-03 16:02:12 +00:00
|
|
|
if (block.number >= ends[id]) {
|
|
|
|
return false;
|
|
|
|
}
|
2021-11-03 12:18:08 +00:00
|
|
|
bytes32 hash = blockhash(blocknumber - 1);
|
2021-11-01 11:30:35 +00:00
|
|
|
return hash != 0 && uint(hash) % periods[id] == markers[id];
|
|
|
|
}
|
|
|
|
|
|
|
|
function _isProofTimedOut(
|
|
|
|
bytes32 id,
|
|
|
|
uint blocknumber
|
|
|
|
)
|
|
|
|
internal view
|
|
|
|
returns (bool)
|
|
|
|
{
|
|
|
|
return block.number >= blocknumber + timeouts[id];
|
|
|
|
}
|
|
|
|
|
|
|
|
function _submitProof(
|
|
|
|
bytes32 id,
|
|
|
|
uint blocknumber,
|
|
|
|
bool proof
|
|
|
|
)
|
|
|
|
internal
|
|
|
|
{
|
|
|
|
require(proof, "Invalid proof"); // TODO: replace bool by actual proof
|
|
|
|
require(
|
|
|
|
_isProofRequired(id, blocknumber),
|
|
|
|
"No proof required for this block"
|
|
|
|
);
|
|
|
|
require(
|
|
|
|
!_isProofTimedOut(id, blocknumber),
|
|
|
|
"Proof not allowed after timeout"
|
|
|
|
);
|
|
|
|
require(!received[id][blocknumber], "Proof already submitted");
|
|
|
|
received[id][blocknumber] = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
function _markProofAsMissing(bytes32 id, uint blocknumber) internal {
|
|
|
|
require(
|
|
|
|
_isProofTimedOut(id, 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");
|
|
|
|
missing[id][blocknumber] = true;
|
|
|
|
missed[id] += 1;
|
|
|
|
}
|
|
|
|
}
|