2021-11-01 12:30:35 +01:00
// SPDX-License-Identifier: MIT
pragma solidity ^ 0 . 8 . 0 ;
contract Proofs {
2022-03-02 15:44:58 +01:00
uint256 private immutable period ;
uint256 private immutable timeout ;
2022-03-10 10:19:21 +01:00
uint8 private immutable downtime ;
2022-03-02 15:44:58 +01:00
2022-03-10 10:19:21 +01:00
constructor (
uint256 __period ,
uint256 __timeout ,
uint8 __downtime
) {
2022-03-09 11:21:19 +01:00
require ( block . number > 256 , " Insufficient block height " ) ;
2022-03-02 15:44:58 +01:00
period = __period ;
timeout = __timeout ;
2022-03-10 10:19:21 +01:00
downtime = __downtime ;
2022-03-02 15:44:58 +01:00
}
2022-02-09 14:17:23 +01:00
mapping ( bytes32 => bool ) private ids ;
mapping ( bytes32 => uint256 ) private starts ;
mapping ( bytes32 => uint256 ) private ends ;
2022-09-21 19:57:26 +10:00
mapping ( bytes32 => bytes32 ) private idEnds ;
2022-03-08 15:58:08 +01:00
mapping ( bytes32 => uint256 ) private probabilities ;
2022-02-09 14:17:23 +01: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 12:30:35 +01:00
2022-03-02 15:44:58 +01:00
function _period ( ) internal view returns ( uint256 ) {
return period ;
2021-11-01 12:30:35 +01:00
}
2022-03-02 15:44:58 +01:00
function _timeout ( ) internal view returns ( uint256 ) {
return timeout ;
2021-11-01 12:30:35 +01:00
}
2022-09-21 19:57:26 +10:00
function _end ( bytes32 endId ) internal view returns ( uint256 ) {
uint256 end = ends [ endId ] ;
require ( end > 0 , " Proof ending doesn ' t exist " ) ;
return ends [ endId ] ;
}
function _endId ( bytes32 id ) internal view returns ( bytes32 ) {
bytes32 endId = idEnds [ id ] ;
require ( endId > 0 , " endId for given id doesn ' t exist " ) ;
return endId ;
}
function _endFromId ( bytes32 id ) internal view returns ( uint256 ) {
bytes32 endId = _endId ( id ) ;
return _end ( endId ) ;
2021-11-03 13:24:50 +01:00
}
2022-02-09 14:17:23 +01:00
function _missed ( bytes32 id ) internal view returns ( uint256 ) {
2021-11-01 12:30:35 +01:00
return missed [ id ] ;
}
2022-03-08 15:58:08 +01: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 19:57:26 +10:00
/// @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 endId Identifies the id of the proof expectation ending. Typically a request id. Different from id because the proof ending is shared amongst many ids.
/// @param probability The probability that a proof should be expected
/// @param duration Duration, from now, for which proofs should be expected
2022-03-08 15:58:08 +01:00
function _expectProofs (
2022-09-21 19:57:26 +10:00
bytes32 id , // typically slot id
bytes32 endId , // typically request id, used so that the ending is global for all slots
2022-03-08 15:58:08 +01:00
uint256 probability ,
uint256 duration
) internal {
2021-11-01 12:30:35 +01:00
require ( ! ids [ id ] , " Proof id already in use " ) ;
ids [ id ] = true ;
2022-03-08 15:58:08 +01:00
starts [ id ] = block . timestamp ;
2022-09-21 19:57:26 +10:00
ends [ endId ] = block . timestamp + duration ;
2022-03-08 15:58:08 +01:00
probabilities [ id ] = probability ;
2022-02-09 14:17:23 +01:00
markers [ id ] = uint256 ( blockhash ( block . number - 1 ) ) % period ;
2022-09-21 19:57:26 +10:00
idEnds [ id ] = endId ;
2021-11-01 12:30:35 +01:00
}
2022-09-13 17:14:57 +10:00
function _unexpectProofs (
bytes32 id
) internal {
require ( ids [ id ] , " Proof id not in use " ) ;
ids [ id ] = false ;
}
2022-03-10 10:12:03 +01:00
function _getPointer ( bytes32 id , uint256 proofPeriod )
2022-02-09 14:17:23 +01:00
internal
view
2022-03-10 10:12:03 +01:00
returns ( uint8 )
2021-11-01 12:30:35 +01:00
{
2022-03-10 10:12:03 +01:00
uint256 blockNumber = block . number % 256 ;
uint256 periodNumber = proofPeriod % 256 ;
uint256 idOffset = uint256 ( id ) % 256 ;
uint256 pointer = ( blockNumber + periodNumber + idOffset ) % 256 ;
return uint8 ( pointer ) ;
}
2022-03-08 15:58:08 +01:00
2022-03-10 13:35:41 +01:00
function _getPointer ( bytes32 id ) internal view returns ( uint8 ) {
return _getPointer ( id , currentPeriod ( ) ) ;
}
2022-03-10 10:12:03 +01: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 15:58:08 +01:00
2022-03-10 10:12:03 +01:00
function _getChallenge ( bytes32 id , uint256 proofPeriod )
internal
view
returns ( bytes32 )
{
return _getChallenge ( _getPointer ( id , proofPeriod ) ) ;
2021-11-01 12:30:35 +01:00
}
2022-03-10 13:04:46 +01:00
function _getChallenge ( bytes32 id ) internal view returns ( bytes32 ) {
return _getChallenge ( id , currentPeriod ( ) ) ;
}
2022-04-05 11:27:02 +02:00
function _getProofRequirement ( bytes32 id , uint256 proofPeriod )
2022-03-08 15:58:08 +01:00
internal
view
2022-04-05 11:27:02 +02:00
returns ( bool isRequired , uint8 pointer )
2022-03-08 15:58:08 +01:00
{
2022-03-10 10:12:03 +01:00
if ( proofPeriod <= periodOf ( starts [ id ] ) ) {
2022-04-05 11:27:02 +02:00
return ( false , 0 ) ;
2022-03-10 10:12:03 +01:00
}
2022-09-21 19:57:26 +10:00
uint256 end = _endFromId ( id ) ;
if ( proofPeriod >= periodOf ( end ) ) {
2022-04-05 11:27:02 +02:00
return ( false , 0 ) ;
2022-03-10 10:12:03 +01:00
}
2022-04-05 11:27:02 +02:00
pointer = _getPointer ( id , proofPeriod ) ;
2022-03-10 10:12:03 +01:00
bytes32 challenge = _getChallenge ( pointer ) ;
2022-03-10 10:19:21 +01:00
uint256 probability = ( probabilities [ id ] * ( 256 - downtime ) ) / 256 ;
2022-09-21 19:18:52 +10:00
// TODO: add test for below change
2022-09-13 17:14:57 +10:00
isRequired = ids [ id ] && uint256 ( challenge ) % probability == 0 ;
2022-04-05 11:27:02 +02:00
}
function _isProofRequired ( bytes32 id , uint256 proofPeriod )
internal
view
returns ( bool )
{
bool isRequired ;
uint8 pointer ;
( isRequired , pointer ) = _getProofRequirement ( id , proofPeriod ) ;
return isRequired && pointer >= downtime ;
2021-11-01 12:30:35 +01:00
}
2022-03-08 15:58:08 +01:00
function _isProofRequired ( bytes32 id ) internal view returns ( bool ) {
return _isProofRequired ( id , currentPeriod ( ) ) ;
}
2022-04-05 11:27:02 +02:00
function _willProofBeRequired ( bytes32 id ) internal view returns ( bool ) {
bool isRequired ;
uint8 pointer ;
( isRequired , pointer ) = _getProofRequirement ( id , currentPeriod ( ) ) ;
return isRequired && pointer < downtime ;
}
2022-04-12 08:43:47 +02:00
function _submitProof ( bytes32 id , bytes calldata proof ) internal {
require ( proof . length > 0 , " Invalid proof " ) ; // TODO: replace by actual check
2022-03-08 15:58:08 +01:00
require ( ! received [ id ] [ currentPeriod ( ) ] , " Proof already submitted " ) ;
received [ id ] [ currentPeriod ( ) ] = true ;
2022-04-12 08:43:47 +02:00
emit ProofSubmitted ( id , proof ) ;
2021-11-01 12:30:35 +01:00
}
2022-03-08 15:58:08 +01: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 12:30:35 +01:00
missed [ id ] += 1 ;
}
2022-04-12 08:43:47 +02:00
2022-09-21 19:57:26 +10:00
/// @notice Extends the proof end time
/// @dev The id must have a mapping to an end id, the end must exist, and the end must not have elapsed yet
/// @param id the id of the proofs to extend. Typically a slot id, the id is mapped to an endId.
/// @param ending the new end time (in seconds)
function _extendProofEndTo ( bytes32 id , uint256 ending ) internal {
bytes32 endId = _endId ( id ) ;
uint256 end = ends [ endId ] ;
// TODO: create type aliases for id and endId so that _end() can return
// EndId storage and we don't need to replicate the below require here
require ( end > 0 , " Proof ending doesn ' t exist " ) ;
require ( block . timestamp <= end , " Proof already ended " ) ;
ends [ endId ] = ending ;
}
2022-04-12 08:43:47 +02:00
event ProofSubmitted ( bytes32 id , bytes proof ) ;
2021-11-01 12:30:35 +01:00
}