mirror of
https://github.com/status-im/dagger-contracts.git
synced 2025-01-15 00:57:26 +00:00
fd74268a8a
1. Remove proof extension as it is not needed. Host are required to provide proofs from the moment they fill a slot, for the duration specified by the contract. This means that the ending of their requirements will be staggered at the end, as they were at the start, but this is more predicable for determining the cost of a request. 2. The proof end time was modified so that if the request state is not accepting proofs, it takes the min of the slot proof end time, the request end time, or block.timestamp - 1, which ensures that it returns a time in the past. If the slot is accepting proofs, it returns the slot end time. 3. Modify marketplace tests so that `waitUntilFinished` advances time to the proof ending of the last slot filled.
174 lines
5.2 KiB
Solidity
174 lines
5.2 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.0;
|
|
|
|
contract Proofs {
|
|
uint256 private immutable period;
|
|
uint256 private immutable timeout;
|
|
uint8 private immutable downtime;
|
|
|
|
constructor(
|
|
uint256 __period,
|
|
uint256 __timeout,
|
|
uint8 __downtime
|
|
) {
|
|
require(block.number > 256, "Insufficient block height");
|
|
period = __period;
|
|
timeout = __timeout;
|
|
downtime = __downtime;
|
|
}
|
|
|
|
mapping(bytes32 => bool) private ids;
|
|
mapping(bytes32 => uint256) private starts;
|
|
mapping(bytes32 => uint256) private ends;
|
|
mapping(bytes32 => uint256) private probabilities;
|
|
mapping(bytes32 => uint256) private markers;
|
|
mapping(bytes32 => uint256) private missed;
|
|
mapping(bytes32 => mapping(uint256 => bool)) private received;
|
|
mapping(bytes32 => mapping(uint256 => bool)) private missing;
|
|
|
|
function _period() internal view returns (uint256) {
|
|
return period;
|
|
}
|
|
|
|
function _timeout() internal view returns (uint256) {
|
|
return timeout;
|
|
}
|
|
|
|
function _end(bytes32 id) internal view returns (uint256) {
|
|
uint256 end = ends[id];
|
|
require(end > 0, "Proof ending doesn't exist");
|
|
return ends[id];
|
|
}
|
|
|
|
function _missed(bytes32 id) internal view returns (uint256) {
|
|
return missed[id];
|
|
}
|
|
|
|
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 id identifies the proof expectation, typically a slot id
|
|
/// @param probability The probability that a proof should be expected
|
|
/// @param duration Duration, from now, for which proofs should be expected
|
|
function _expectProofs(
|
|
bytes32 id, // typically slot id
|
|
uint256 probability,
|
|
uint256 duration
|
|
) internal {
|
|
require(!ids[id], "Proof id already in use");
|
|
ids[id] = true;
|
|
starts[id] = block.timestamp;
|
|
ends[id] = block.timestamp + duration;
|
|
probabilities[id] = probability;
|
|
markers[id] = uint256(blockhash(block.number - 1)) % period;
|
|
}
|
|
|
|
function _unexpectProofs(
|
|
bytes32 id
|
|
) internal {
|
|
require(ids[id], "Proof id not in use");
|
|
ids[id] = false;
|
|
}
|
|
|
|
function _getPointer(bytes32 id, uint256 proofPeriod)
|
|
internal
|
|
view
|
|
returns (uint8)
|
|
{
|
|
uint256 blockNumber = block.number % 256;
|
|
uint256 periodNumber = proofPeriod % 256;
|
|
uint256 idOffset = uint256(id) % 256;
|
|
uint256 pointer = (blockNumber + periodNumber + idOffset) % 256;
|
|
return uint8(pointer);
|
|
}
|
|
|
|
function _getPointer(bytes32 id) internal view returns (uint8) {
|
|
return _getPointer(id, currentPeriod());
|
|
}
|
|
|
|
function _getChallenge(uint8 pointer) internal view returns (bytes32) {
|
|
bytes32 hash = blockhash(block.number - 1 - pointer);
|
|
assert(uint256(hash) != 0);
|
|
return keccak256(abi.encode(hash));
|
|
}
|
|
|
|
function _getChallenge(bytes32 id, uint256 proofPeriod)
|
|
internal
|
|
view
|
|
returns (bytes32)
|
|
{
|
|
return _getChallenge(_getPointer(id, proofPeriod));
|
|
}
|
|
|
|
function _getChallenge(bytes32 id) internal view returns (bytes32) {
|
|
return _getChallenge(id, currentPeriod());
|
|
}
|
|
|
|
function _getProofRequirement(bytes32 id, uint256 proofPeriod)
|
|
internal
|
|
view
|
|
returns (bool isRequired, uint8 pointer)
|
|
{
|
|
if (proofPeriod <= periodOf(starts[id])) {
|
|
return (false, 0);
|
|
}
|
|
uint256 end = _end(id);
|
|
if (proofPeriod >= periodOf(end)) {
|
|
return (false, 0);
|
|
}
|
|
pointer = _getPointer(id, proofPeriod);
|
|
bytes32 challenge = _getChallenge(pointer);
|
|
uint256 probability = (probabilities[id] * (256 - downtime)) / 256;
|
|
isRequired = ids[id] && uint256(challenge) % probability == 0;
|
|
}
|
|
|
|
function _isProofRequired(bytes32 id, uint256 proofPeriod)
|
|
internal
|
|
view
|
|
returns (bool)
|
|
{
|
|
bool isRequired;
|
|
uint8 pointer;
|
|
(isRequired, pointer) = _getProofRequirement(id, proofPeriod);
|
|
return isRequired && pointer >= downtime;
|
|
}
|
|
|
|
function _isProofRequired(bytes32 id) internal view returns (bool) {
|
|
return _isProofRequired(id, currentPeriod());
|
|
}
|
|
|
|
function _willProofBeRequired(bytes32 id) internal view returns (bool) {
|
|
bool isRequired;
|
|
uint8 pointer;
|
|
(isRequired, pointer) = _getProofRequirement(id, currentPeriod());
|
|
return isRequired && pointer < downtime;
|
|
}
|
|
|
|
function _submitProof(bytes32 id, bytes calldata proof) internal {
|
|
require(proof.length > 0, "Invalid proof"); // TODO: replace by actual check
|
|
require(!received[id][currentPeriod()], "Proof already submitted");
|
|
received[id][currentPeriod()] = true;
|
|
emit ProofSubmitted(id, proof);
|
|
}
|
|
|
|
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;
|
|
missed[id] += 1;
|
|
}
|
|
|
|
event ProofSubmitted(bytes32 id, bytes proof);
|
|
}
|