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
contract Proofs {
2022-09-29 10:18:02 +00:00
type ProofId is bytes32 ;
2022-09-29 10:31:57 +00:00
type EndId is bytes32 ;
2022-09-29 10:18:02 +00:00
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
2022-03-10 09:19:21 +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
}
2022-09-29 10:18:02 +00:00
mapping ( ProofId => bool ) private ids ;
mapping ( ProofId => uint256 ) private starts ;
mapping ( EndId => uint256 ) private ends ;
mapping ( ProofId => EndId ) private idEnds ;
mapping ( ProofId => uint256 ) private probabilities ;
mapping ( ProofId => uint256 ) private markers ;
mapping ( ProofId => uint256 ) private missed ;
mapping ( ProofId => mapping ( uint256 => bool ) ) private received ;
mapping ( ProofId => 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-09-29 10:31:57 +00:00
function _end ( EndId endId ) internal view returns ( uint256 ) {
2022-09-29 10:07:55 +00:00
uint256 end = ends [ endId ] ;
2022-09-21 09:57:26 +00:00
require ( end > 0 , " Proof ending doesn ' t exist " ) ;
2022-09-29 10:07:55 +00:00
return ends [ endId ] ;
}
2022-09-29 10:18:02 +00:00
function _endId ( ProofId id ) internal view returns ( EndId ) {
EndId endId = idEnds [ id ] ;
2022-09-29 10:31:57 +00:00
require ( EndId . unwrap ( endId ) > 0 , " endId for given id doesn ' t exist " ) ;
2022-09-29 10:07:55 +00:00
return endId ;
}
2022-09-29 10:18:02 +00:00
function _endFromId ( ProofId id ) internal view returns ( uint256 ) {
EndId endId = _endId ( id ) ;
2022-09-29 10:07:55 +00:00
return _end ( endId ) ;
2021-11-03 12:24:50 +00:00
}
2022-09-29 10:18:02 +00:00
function _missed ( ProofId 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 ) ;
}
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 id identifies the proof expectation, typically a slot id
2022-09-29 10:07:55 +00:00
/// @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.
2022-09-21 09:57:26 +00:00
/// @param probability The probability that a proof should be expected
2022-03-08 14:58:08 +00:00
function _expectProofs (
2022-09-29 10:18:02 +00:00
ProofId id , // typically slot id
EndId endId , // typically request id, used so that the ending is global for all slots
2022-09-29 10:07:55 +00:00
uint256 probability
2022-03-08 14:58:08 +00:00
) 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 ;
probabilities [ id ] = probability ;
2022-02-09 13:17:23 +00:00
markers [ id ] = uint256 ( blockhash ( block . number - 1 ) ) % period ;
2022-09-29 10:07:55 +00:00
idEnds [ id ] = endId ;
2021-11-01 11:30:35 +00:00
}
2022-09-13 07:14:57 +00:00
function _unexpectProofs (
2022-09-29 10:18:02 +00:00
ProofId id
2022-09-13 07:14:57 +00:00
) internal {
require ( ids [ id ] , " Proof id not in use " ) ;
ids [ id ] = false ;
}
2022-09-29 10:18:02 +00:00
function _getPointer ( ProofId id , uint256 proofPeriod )
2022-02-09 13:17:23 +00:00
internal
view
2022-03-10 09:12:03 +00:00
returns ( uint8 )
2021-11-01 11:30:35 +00:00
{
2022-03-10 09:12:03 +00:00
uint256 blockNumber = block . number % 256 ;
uint256 periodNumber = proofPeriod % 256 ;
2022-09-29 10:18:02 +00:00
uint256 idOffset = uint256 ( ProofId . 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
2022-09-29 10:18:02 +00:00
function _getPointer ( ProofId 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
2022-09-29 10:18:02 +00:00
function _getChallenge ( ProofId id , uint256 proofPeriod )
2022-03-10 09:12:03 +00:00
internal
view
returns ( bytes32 )
{
return _getChallenge ( _getPointer ( id , proofPeriod ) ) ;
2021-11-01 11:30:35 +00:00
}
2022-09-29 10:18:02 +00:00
function _getChallenge ( ProofId id ) internal view returns ( bytes32 ) {
2022-03-10 12:04:46 +00:00
return _getChallenge ( id , currentPeriod ( ) ) ;
}
2022-09-29 10:18:02 +00:00
function _getProofRequirement ( ProofId id , uint256 proofPeriod )
2022-03-08 14:58:08 +00:00
internal
view
2022-04-05 09:27:02 +00:00
returns ( bool isRequired , uint8 pointer )
2022-03-08 14:58:08 +00:00
{
2022-03-10 09:12:03 +00:00
if ( proofPeriod <= periodOf ( starts [ id ] ) ) {
2022-04-05 09:27:02 +00:00
return ( false , 0 ) ;
2022-03-10 09:12:03 +00:00
}
2022-09-29 10:07:55 +00:00
uint256 end = _endFromId ( 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 ;
2022-09-13 07:14:57 +00:00
isRequired = ids [ id ] && uint256 ( challenge ) % probability == 0 ;
2022-04-05 09:27:02 +00:00
}
2022-09-29 10:18:02 +00:00
function _isProofRequired ( ProofId id , uint256 proofPeriod )
2022-04-05 09:27:02 +00:00
internal
view
returns ( bool )
{
bool isRequired ;
uint8 pointer ;
( isRequired , pointer ) = _getProofRequirement ( id , proofPeriod ) ;
return isRequired && pointer >= downtime ;
2021-11-01 11:30:35 +00:00
}
2022-09-29 10:18:02 +00:00
function _isProofRequired ( ProofId id ) internal view returns ( bool ) {
2022-03-08 14:58:08 +00:00
return _isProofRequired ( id , currentPeriod ( ) ) ;
}
2022-09-29 10:18:02 +00:00
function _willProofBeRequired ( ProofId 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 ;
}
2022-09-29 10:18:02 +00:00
function _submitProof ( ProofId id , bytes calldata proof ) internal {
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
}
2022-09-29 10:18:02 +00:00
function _markProofAsMissing ( ProofId 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
2022-09-29 10:07:55 +00:00
/// @notice Sets the proof end time
/// @dev Can only be set once
/// @param endId the endId of the proofs to extend (typically a request id).
/// @param ending the new end time (in seconds)
2022-09-29 10:31:57 +00:00
function _setProofEnd ( EndId endId , uint256 ending ) internal {
2022-09-29 10:07:55 +00:00
// 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 ( ends [ endId ] == 0 || ending < block . timestamp , " End exists or must be past " ) ;
ends [ endId ] = ending ;
}
2022-09-29 10:18:02 +00:00
event ProofSubmitted ( ProofId id , bytes proof ) ;
2021-11-01 11:30:35 +00:00
}