// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; import { IWitOracle, IWitOracleAppliance, IWitOracleQueriable, IWitOracleRadonRegistry, Witnet } from "../WitOracle.sol"; import { IWitRandomness, IWitRandomnessAdmin, IWitRandomnessConsumer, WitRandomness } from "../WitRandomness.sol"; import {Clonable} from "../patterns/Clonable.sol"; import {Ownable, Ownable2Step} from "../patterns/Ownable2Step.sol"; /// @title WitRandomnessV3: Unbiased, EVM-agnostic and provably-fair random seeds from the Witnet blockchain. /// /// 64-bit entropy seeds can be permissionless pulled at anytime from smart contracts and externally-owned EVM accounts. /// /// Randomness provisioning is securely protected against race-condition and malleability attacks by node validators /// on both EVM and Witnet blockchains. /// /// This contract acts also as a public-good registry, openly preserving all random seeds generated in the past, /// and providing after-the-fact traceability proof to the actual witnessing committees and punishable acts that took /// place on the Witnet blockchain for generating every single one of them. /// /// @dev Protection against race-condition and malleability attacks requires randomness queried to this contract /// to be solved is an asynchronous way, meaning that after some randomness is requested, the result provided /// from the Wit/Oracle sidechain won't be ready until a certain amount of EVM blocks have elapsed. /// This protection also implies that eventually failing randomness requests on the Wit/Oracle sidechain /// cannot be retried but from this very same contract. Failing randomness requests will get all automatically /// and unbiasedly solved as soon as some new randomness request gets solved, even if paid by a different requester. /// /// Querying for new randomness requires paying a fee in native EVM gas currency, as to cover the implicit cost of /// solving unmalleable randomness on the Wit/Oracle sidechain and report it back to the EVM storage. The actual /// randomize fee depends on the expected gas price to pay for the EVM transaction that would eventually include /// a call to any of the `randomize(..)` methods of this contract. The randomize fee can be computed by passing /// the expected gas price value to the `estimateRandomizeFee(uint256)` method. /// /// > Note [1]: On highly volatile gas price markets, especially on some Layer-2 EVM chains, is highly recommended /// to estimate the randomize fee before a `randomize()` call, and pass a fresh EVM gas price value. Otherwise /// the call to `randomize()` could potentially revert with either "insufficient reward" or "too much reward". /// /// > Note [2]: Contracts requiring some external entity to report Witnet-certified randomness only when required, /// would need to implement the `IWitRandomnessConsumer`. The external entity would then have to clone the /// public-domain address, attach it to the consumer contract address, and get the `randomize()` function /// called on the cloned instance as many times as required. /// /// @author The Witnet Foundation. contract WitRandomnessV3 is Clonable, Ownable2Step, WitRandomness { using Witnet for Witnet.DataResult; /// @notice Unique identifier of the RNG data request used on the Witnet blockchain for solving randomness. /// @dev Can be used to track all randomness requests solved so far on the Witnet blockchain. Witnet.RadonHash internal immutable __WIT_ORACLE_RNG_RADON_HASH; /// @dev Default number of blocks after a randomize request to consider it delayed if not solved yet. uint16 internal constant _DEFAULT_RANDOMIZE_WAITING_BLOCKS = 256; /// @dev Minimum acceptable callback gas limit that can be settled by the `owner()`. uint24 internal constant _WIT_ORACLE_MIN_CALLBACK_GAS_LIMIT = 50000; /// @dev Size of successful CBOR-encoded randomness generated by the Witnet blockchain. uint16 internal constant _WIT_ORACLE_RNG_MAX_RESULT_SIZE = 34; /// @dev Storage slot pointer bytes32 private constant _WIT_RANDOMNESS_STORAGE_SLOT = // keccak256("io.witnet.apps.randomness.v3") & ~bytes32(uint256(0xff) 0x43025ad356942e949bc1eab02ffb42d0642f59c8829febfec0169ebcda390700; /// Randomize records struct Randomize { uint64 queryId; uint64 finality; uint64 nextBlock; bytes8 randomness; uint64 prevBlock; } /// Initialization parameters for clones struct CloneSettings { uint24 callbackGasLimit; uint16 extraFeePercentage; uint16 witCommitteeSize; uint64 witInclusionFees; uint16 randomizeWaitingBlocks; } /// Data storage layout struct Storage { uint24 callbackGasLimit; uint16 extraFeePercentage; uint64 lastRandomizeBlock; uint16 witCommitteeSize; uint64 witInclusionFees; uint72 _0; address consumer; uint16 randomizeWaitingBlocks; uint80 _1; mapping (Witnet.QueryId => uint256) blocks; mapping (uint256 => Randomize) requests; } /// @notice Returns the WitOracle address that this appliance is bound to. address immutable internal __WIT_ORACLE; /// @notice Returns the name of the actual contract implementing the logic of this Witnet appliance. function class() virtual override public pure returns (string memory) { return type(WitRandomnessV3).name; } constructor( address _witOracle, address _operator ) Ownable(_operator) { _require( _witOracle.code.length > 0 && IWitOracleAppliance(_witOracle).specs() == ( type(IWitOracle).interfaceId ^ type(IWitOracleQueriable).interfaceId ), "uncompliant WitOracle" ); __WIT_ORACLE = _witOracle; // Build Witnet-compliant randomness request: IWitOracleRadonRegistry _witOracleRadonRegistry = IWitOracle(_witOracle).registry(); __WIT_ORACLE_RNG_RADON_HASH = _witOracleRadonRegistry.verifyRadonRequest( Witnet.intoMemArray([ _witOracleRadonRegistry.verifyRadonRetrieval( Witnet.RadonRetrievalMethods.RNG, "", // no request url "", // no request body new string[2][](0), // no request headers hex"80" // no request Radon script ) ]), Witnet.RadonReducer({ opcode: Witnet.RadonReduceOpcodes.Mode, filters: new Witnet.RadonFilter[](0) }), Witnet.RadonReducer({ opcode: Witnet.RadonReduceOpcodes.ConcatenateAndHash, filters: new Witnet.RadonFilter[](0) }) ); // Initialize settings: __storage().extraFeePercentage = 10; // 10 % __storage().callbackGasLimit = _WIT_ORACLE_MIN_CALLBACK_GAS_LIMIT; __storage().randomizeWaitingBlocks = _DEFAULT_RANDOMIZE_WAITING_BLOCKS; __storage().witCommitteeSize = 3; __storage().witInclusionFees = 0; } receive() virtual external payable { _revert("no transfers accepted"); } fallback() virtual external payable { _revert(string(abi.encodePacked( "not implemented: 0x", Witnet.toHexString(uint8(bytes1(msg.sig))), Witnet.toHexString(uint8(bytes1(msg.sig << 8))), Witnet.toHexString(uint8(bytes1(msg.sig << 16))), Witnet.toHexString(uint8(bytes1(msg.sig << 24))) ))); } function initializeClone(address _curator, CloneSettings calldata _settings) external onlyOnClones initializer returns (IWitRandomness) { _require( _curator != address(0), "zero curator" ); _transferOwnership(_curator); _require( _settings.witCommitteeSize > 0 && _settings.witCommitteeSize <= 127, "invalid settings" ); __storage().callbackGasLimit = _settings.callbackGasLimit; __storage().extraFeePercentage = _settings.extraFeePercentage; __storage().witCommitteeSize = _settings.witCommitteeSize; __storage().witInclusionFees = _settings.witInclusionFees; __storage().randomizeWaitingBlocks = _settings.randomizeWaitingBlocks; return IWitRandomness(address(this)); } /// =============================================================================================================== /// --- Clonable -------------------------------------------------------------------------------------------------- function initialized() virtual override public view returns (bool) { return __storage().witCommitteeSize > 0; } /// =============================================================================================================== /// --- IWitRandomness -------------------------------------------------------------------------------------------- /// Address of the underlying logic contract that can be potentially cloned. function base() virtual override (Clonable, IWitRandomness) public view returns (address) { return super.base(); } /// Creates a light-proxy clone to the `base()` logic address, to be owned by the specified `curator`address. /// Curators of cloned instances can optionally settle one single `IWitRandomnessConsumer` consuming contract. /// The consuming contract, if settled, will be immediately reported every time a new `randomize()` request /// gets solved and bridged back from Witnet. Either way, randomness resolutions will be reamin stored in the /// `WitRandomness` storage, as for future reference. /// @param _curator Address that will have authoritative access to `IWitRandomnessAdmin` methods. function clone(address _curator) virtual override external returns (IWitRandomness) { return WitRandomnessV3(payable(__clone())).initializeClone( _curator, CloneSettings({ callbackGasLimit: __storage().callbackGasLimit, extraFeePercentage: __storage().extraFeePercentage, witCommitteeSize: __storage().witCommitteeSize, witInclusionFees: __storage().witInclusionFees, randomizeWaitingBlocks: __storage().randomizeWaitingBlocks }) ); } /// Returns the consumer address where all valid randomize results will be reported to. /// @dev If zero, generated randomness will not be reported to any other external address. /// @dev The consumer contract must implement the `IWitRandomnessConsumer` interface, /// @dev and accept this instance as source of entropy. /// @dev It can only be settled by a curator on cloned instances. function consumer() virtual override external view returns (IWitRandomnessConsumer) { return IWitRandomnessConsumer(__storage().consumer); } /// Returns amount of wei required to be paid as a fee when requesting randomization with a /// transaction gas price as the one given. function estimateRandomizeFee(uint256 _evmGasPrice) public view virtual override returns (uint256) { uint256 _baseFee = IWitOracleQueriable(__WIT_ORACLE).estimateBaseFeeWithCallback( _evmGasPrice, __storage().callbackGasLimit ); return (_baseFee * (100 + __storage().extraFeePercentage)) / 100; } /// Retrieves the result of keccak256-hashing the given block number with the randomness value /// generated by the Witnet blockchain in response to the first non-failing randomize request solved /// after such block number. /// /// @dev Reverts if: /// i. no `randomize()` was queried on neither the given block, nor afterwards. /// ii. the first non-failing `randomize()` request found on or after the given block is not solved yet. /// iii. all `randomize()` requests that took place on or after the given block were solved with errors. /// /// @param _blockNumber Block number from which the search will start. function fetchRandomnessAfter(uint256 _blockNumber) public view virtual override returns (bytes32) { return keccak256( abi.encode( _blockNumber, _fetchRandomnessAfter(_blockNumber) ) ); } /// Retrieves the actual random value, unique hash and timestamp of the witnessing commit/reveal act /// that took place in the Witnet blockchain in response to the first non-failing randomize query /// solved after the given block number. /// /// @dev Reverts if: /// i. no `randomize()` was queried on neither the given block, nor afterwards. /// ii. the first non-failing `randomize()` request found on or after the given block is not solved yet. /// iii. all `randomize()` requests that took place on or after the given block were solved with errors. /// /// @param _blockNumber Block number from which the search will start. /// /// @return _witnetQueryUUID Universal identifier of the query posted from this contract that ultimately solved randomness. /// @return _witnetTimestamp Timestamp at which the randomness value was generated by the Witnet blockchain. /// @return _witnetDrTxHash Hash of the witnessing commit/reveal act that took place on the Witnet blockchain. /// @return _evmFinalityBlock EVM block number at which the generated randomness can be considered to be final. function fetchRandomnessAfterProof(uint256 _blockNumber) virtual override public view returns ( bytes32 _witnetQueryUUID, Witnet.Timestamp _witnetTimestamp, Witnet.TransactionHash _witnetDrTxHash, uint256 _evmFinalityBlock ) { (_witnetQueryUUID, _witnetDrTxHash, _witnetTimestamp, _evmFinalityBlock) = IWitOracleQueriable(__WIT_ORACLE) .getQueryResultTrails( _getRandomizeQueryId(_blockNumber) ); } /// Returns last block number on which a randomize was requested. function getLastRandomizeBlock() virtual override external view returns (uint256) { return __storage().lastRandomizeBlock; } /// Retrieves metadata related to the randomize request that got queried to the`WitOracle` contract /// on the specified block number, if any. /// /// @dev Returns zero values if no randomize request was actually queried on the specified block number. /// /// @param _blockNumber Block number from which the search will start. /// /// @return _queryId Identifier of the underlying Wit/Oracle query created on the specified block number. /// @return _prevRandomizeBlock Block number in which a randomize request got queried just before this one. 0 if none. /// @return _nextRandomizeBlock Block number in which a randomize request got queried just after this one, 0 if none. function getRandomizeData(uint256 _blockNumber) external view virtual override returns ( Witnet.QueryId _queryId, uint256 _prevRandomizeBlock, uint256 _nextRandomizeBlock ) { Randomize storage __request = __storage().requests[_blockNumber]; _queryId = Witnet.QueryId.wrap(__request.queryId); _prevRandomizeBlock = __request.prevBlock != 0 ? __request.prevBlock : getRandomizePrevBlock(_blockNumber); _nextRandomizeBlock = __request.nextBlock != 0 ? __request.nextBlock : getRandomizeNextBlock(_blockNumber); } /// Returns the number of the next block in which a randomize request was posted after the given one. /// @param _blockNumber Block number from which the search will start. /// @return Number of the first block found after the given one, or `0` otherwise. function getRandomizeNextBlock(uint256 _blockNumber) public view virtual override returns (uint256) { return ( __storage().requests[_blockNumber].queryId == 0 ? _searchNextBlock(_blockNumber, __storage().lastRandomizeBlock) : __storage().requests[_blockNumber].nextBlock ); } /// Returns the number of the previous block in which a randomize request was posted before the given one. /// @param _blockNumber Block number from which the search will start. Cannot be zero. /// @return First block found before the given one, or `0` otherwise. function getRandomizePrevBlock(uint256 _blockNumber) public view virtual override returns (uint256) { uint256 _latest = __storage().lastRandomizeBlock; return (_blockNumber > _latest ? _latest // start search from the latest block : _searchPrevBlock(_blockNumber, __storage().requests[_latest].prevBlock) ); } /// Returns the identifier of the Wit/Oracle query that has either solved randomness /// for the specified block number, is currently attending randomness for the specified block number, /// of the one that attempted to solve it. /// @dev Returns zero if no randomize was request on or after the specified block number. function getRandomizeQueryId(uint256 _blockNumber) virtual override public view returns (Witnet.QueryId) { return Witnet.QueryId.wrap( _getRandomizeQueryId(_blockNumber) ); } /// Explains why the last attempt of generating randomness for the specified block number failed. function getRandomizeQueryErrorDescription(uint256 _blockNumber) virtual override public view returns (string memory _reason) { RandomizeStatus _status = getRandomizeStatus(_blockNumber); if (_status == RandomizeStatus.Error) { _reason = IWitOracleQueriable(__WIT_ORACLE).getQueryResultStatusDescription( _getRandomizeQueryId(_blockNumber) ); } } /// Returns security and liveness parameters required to the Witnet blockchain /// when solving randomness requests, if no others are specified. /// @return _callbackGasLimit Max. expendable gas upon randomness delivery. /// @return _extraFeePercentage Overhead percentage applied when estimating the randomize request fee. /// @return _witCommitteeSize Number of Witnet witnessing nodes required to generate unbiased randomness. /// @return _witInclusionFees Minimum amount of fees in $nanoWIT to be paid on the Witnet blockchain. function getRandomizeQueryParams() virtual override public view returns ( uint24 _callbackGasLimit, uint16 _extraFeePercentage, uint16 _witCommitteeSize, uint64 _witInclusionFees ) { return ( __storage().callbackGasLimit, __storage().extraFeePercentage, __storage().witCommitteeSize, __storage().witInclusionFees ); } /// Returns the immutable bytecode of the Radon Request that's being used /// for solving randomness requests on the Witnet blockchain. function getRandomizeRadonBytecode() virtual override external view returns (bytes memory) { return IWitOracle(__WIT_ORACLE).registry().lookupRadonRequestBytecode( getRandomizeRadonHash() ); } /// Returns the unique identifier of the Radon Request that's being used /// for solving request randomness requests on the Witnet blockchain. function getRandomizeRadonHash() virtual override public view returns (Witnet.RadonHash) { return __WIT_ORACLE_RNG_RADON_HASH; } /// Returns status of the first non-errored randomize request queried on or after the given block number. /// - 0 -> Void: no randomize request was actually queried on or after the given block number. /// - 1 -> Awaiting: a randomize request was found but it's not yet solved by the Wit/Oracle. /// - 2 -> Ready: a successfull randomize value was reported and is ready to be read. /// - 3 -> Error: all attempted randomize requests at or after the given block were solved with errors. /// - 4 -> Finalizing: a randomize result was relayed already but cannot yet be considered to be final. function getRandomizeStatus(uint256 _blockNumber) virtual override public view returns (RandomizeStatus) { Randomize storage __request = __storage().requests[_blockNumber]; if (_blockNumber == 0) { return RandomizeStatus.Void; } else if (__request.queryId == 0) { // if no randomize was requested on the specified block number, // return the current status of the first randomize request after it, if any: return getRandomizeStatus( getRandomizeNextBlock(_blockNumber) ); } else if (__request.queryId == type(uint64).max) { // if the resolution to the randomize request on the specified block number failed, // return the current status of the randomize request after it, if any, // or `Error` otherwise: if (__request.nextBlock > 0) { return getRandomizeStatus(__request.nextBlock); } else { return RandomizeStatus.Error; } } else { if (__request.randomness == bytes32(0)) { // if there was a randomize request on the specified block number, // but it hasn't been solved after some settled number of blocks, // return the current status of the later randomize request if any, // or still `Awaiting` otherwise: if ( __storage().randomizeWaitingBlocks > 0 && block.number > _blockNumber + __storage().randomizeWaitingBlocks && __request.nextBlock > 0 ) { return getRandomizeStatus(__request.nextBlock); } else { return RandomizeStatus.Awaiting; } } else { // if there was a randomize request on the specified block number, // and it has already been resolved, return `Ready` or `Finalizing` depending // on whether the randomness finality block has already been reached: return ( block.number >= __request.finality ? RandomizeStatus.Ready : RandomizeStatus.Finalizing ); } } } /// Return the number of EVM blocks after a randomize requests that have to elapse before /// considering such request to be delayed. Results to delayed requests can potentially be /// provided by later requests, if solved earlier. A value of zero means that randomize /// requests will never expire. function getRandomizeWaitingBlocks() virtual override external view returns (uint16) { return __storage().randomizeWaitingBlocks; } /// Returns `true` only if a successfull resolution from the Witnet blockchain is found for the first /// non-errored randomize request posted on or after the given block number. function isRandomized(uint256 _blockNumber) public view virtual override returns (bool) { return ( getRandomizeStatus(_blockNumber) == RandomizeStatus.Ready ); } /// Requests the Witnet blockchain to generate an unbiased 64-bit random seed. /// @dev Only one randomize request per block will get ultimately relayed to the Witnet blockchain. /// @return _evmUsedFunds EVM funds actually paid as randomize fee. function randomize() external payable virtual override returns (uint256 _evmUsedFunds) { uint256 _queryId; uint256 _blockNumber = block.number; if (__storage().lastRandomizeBlock < _blockNumber) { _evmUsedFunds = msg.value; // Post the Witnet Randomness request: _queryId = IWitOracleQueriable(__WIT_ORACLE).queryDataWithCallback{ value: msg.value }( __WIT_ORACLE_RNG_RADON_HASH, Witnet.QuerySLA({ witResultMaxSize: _WIT_ORACLE_RNG_MAX_RESULT_SIZE, witCommitteeSize: __storage().witCommitteeSize, witUnitaryReward: __storage().witInclusionFees }), Witnet.QueryCallback({ consumer: address(this), gasLimit: __storage().callbackGasLimit }) ); // Save Randomize metadata in storage: uint256 _prevBlock = __storage().lastRandomizeBlock; Randomize storage __request = __storage().requests[_blockNumber]; __request.queryId = uint64(_queryId); __request.prevBlock = uint64(_prevBlock); __storage().requests[_prevBlock].nextBlock = uint64(_blockNumber); __storage().lastRandomizeBlock = uint64(_blockNumber); __storage().blocks[Witnet.QueryId.wrap(uint64(_queryId))] = _blockNumber; // Emit event only on first successful randomize within one block: // solhint-disable-next-line avoid-tx-origin emit Randomizing( _msgSender(), block.number, Witnet.QueryId.wrap(uint64(_queryId)) ); } else { _queryId = __storage().requests[_blockNumber].queryId; } // Transfer back unused funds: if (_evmUsedFunds < msg.value) { payable(msg.sender).transfer(msg.value - _evmUsedFunds); } } /// Verifies that the specified randomness seed was actually generated on the Witnet blockchain, /// and was actually used for producing the randomness seed returned by `fetchRandomnessAfter`. function verifyRandomnessAfter(uint256 blockNumber, bytes32 witnetRandomness) external view virtual override returns (bool) { return ( isRandomized(blockNumber) && fetchRandomnessAfter(blockNumber) == keccak256(abi.encode( blockNumber, bytes8(witnetRandomness) )) ); } /// The Wit/Oracle core address accepted as source of entropy. function witOracle() virtual override external view returns (address) { return __WIT_ORACLE; } /// =============================================================================================================== /// --- IWitRandomnessAdmin ------------------------------------------------------------------------------------- function acceptOwnership() virtual override (IWitRandomnessAdmin, Ownable2Step) public { Ownable2Step.acceptOwnership(); } function owner() virtual override (IWitRandomnessAdmin, Ownable) public view returns (address) { return Ownable.owner(); } function pendingOwner() virtual override (IWitRandomnessAdmin, Ownable2Step) public view returns (address) { return Ownable2Step.pendingOwner(); } function transferOwnership(address _newOwner) virtual override (IWitRandomnessAdmin, Ownable2Step) public onlyOwner { Ownable.transferOwnership(_newOwner); } function settleConsumer(address _consumer, uint24 _maxCallbackGasLimit) virtual override external onlyOnClones onlyOwner { if ( _consumer != address(0) && _consumer.code.length > 0 ) { try IWitRandomnessConsumer(_consumer).witRandomness() returns (IWitRandomness _witRandomness) { if (address(_witRandomness) == address(this)) { _require(_maxCallbackGasLimit >= _WIT_ORACLE_MIN_CALLBACK_GAS_LIMIT, "low gas limit"); __storage().consumer = _consumer; __storage().callbackGasLimit = _maxCallbackGasLimit; return; } } catch (bytes memory) {} } _revert("invalid consumer"); } function settleQueryParams( uint24 _callbackGasLimit, uint16 _extraFeePercentage, uint16 _minWitCommitteeSize, uint64 _minWitInclusionFees ) virtual override external onlyOwner { // validate ranges: _require( _callbackGasLimit >= _WIT_ORACLE_MIN_CALLBACK_GAS_LIMIT && _minWitCommitteeSize > 0 && _minWitCommitteeSize <= 127, "invalid params" ); __storage().callbackGasLimit = _callbackGasLimit; __storage().extraFeePercentage = _extraFeePercentage; __storage().witCommitteeSize = _minWitCommitteeSize; __storage().witInclusionFees = _minWitInclusionFees; } function settleRandomizeWaitingBlocks(uint16 _randomizeWaitingBlocks) virtual override external onlyOwner { __storage().randomizeWaitingBlocks = _randomizeWaitingBlocks; } // ================================================================================================================ // --- IWitOracleQueriableConsumer -------------------------------------------------------------------------------- /// Method to be called from the witOracle contract as soon as the given Witnet `queryId` gets reported. /// @dev It should revert if called from an address different to the WitOracle instance being used by consumer. /// @param queryId The unique identifier of the Witnet query being reported. /// @param queryResult Abi-encoded Witnet.DataResult containing the CBOR-encoded query's result, and metadata. function reportWitOracleQueryResult( uint256 queryId, bytes calldata queryResult ) virtual override external { uint256 _randomizeBlock = __storage().blocks[Witnet.QueryId.wrap(uint64(queryId))]; _require(_randomizeBlock > 0, "invalid query"); _require(reportableFrom(msg.sender), "invalid oracle"); Randomize storage __request = __storage().requests[_randomizeBlock]; Witnet.DataResult memory _queryResult = abi.decode(queryResult, (Witnet.DataResult)); if ( __request.randomness == 0 && __storage().randomizeWaitingBlocks > 0 && block.number > _randomizeBlock + __storage().randomizeWaitingBlocks && __request.nextBlock > 0 ) { // ignore the query result if it's provided too late and there was a later randomize attempt: __request.queryId = type(uint64).max; } else { bytes8 _randomness = bytes8(0); if (_queryResult.status == Witnet.ResultStatus.NoErrors) { if (uint64(queryId) != __request.queryId) { // if a non-finalized error was disputed and eventually won: __request.queryId = uint64(queryId); } _randomness = bytes8(_queryResult.fetchBytes32()); } else { __request.queryId = type(uint64).max; } __request.finality = uint64(_queryResult.finality); __request.randomness = _randomness; address _consumer = __storage().consumer; if (_consumer != address(0)) { try IWitRandomnessConsumer(_consumer).reportRandomness( _randomness, _randomizeBlock, _queryResult.finality, _queryResult.timestamp, _queryResult.drTxHash ) {} catch (bytes memory) { // no matter if reporting randomness to the consumer fails, // this transaction must prevail so already verified randomness // gets preserved in storage. } } emit Randomized( _randomizeBlock, _queryResult.finality, _randomness ); } } /// Determines if Witnet queries can be reported from given address. /// @dev In practice, must only be true on the WitOracle address that's being used by /// @dev the consumer contract to post data queries. function reportableFrom(address _from) virtual override public view returns (bool) { return ( _from == address(__WIT_ORACLE) ); } // ================================================================================================================ // --- Internal methods ------------------------------------------------------------------------------------------- function _getRandomizeQueryId(uint256 _blockNumber) virtual internal view returns (uint64) { Randomize storage __request = __storage().requests[_blockNumber]; if (__request.queryId == 0) { return _getRandomizeQueryId( getRandomizeNextBlock(_blockNumber) ); } else if (__request.queryId == type(uint64).max) { return ( __request.nextBlock > 0 ? _getRandomizeQueryId(__request.nextBlock) : __request.queryId ); } else { if (__request.randomness == bytes8(0)) { if ( __storage().randomizeWaitingBlocks > 0 && block.number > _blockNumber + __storage().randomizeWaitingBlocks && __request.nextBlock > 0 ) { return _getRandomizeQueryId(__request.nextBlock); } } return __request.queryId; } } function _fetchRandomnessAfter(uint256 _blockNumber) virtual internal view returns (bytes8 _randomness) { Randomize storage __request = __storage().requests[_blockNumber]; if (__request.queryId == 0) { return _fetchRandomnessAfter( getRandomizeNextBlock(_blockNumber) ); } else if (__request.queryId == type(uint64).max) { _require( __request.nextBlock > 0, "failed: retry randomize" ); return _fetchRandomnessAfter( __request.nextBlock ); } else if (__request.randomness == bytes8(0)) { if ( __storage().randomizeWaitingBlocks > 0 && block.number > _blockNumber + __storage().randomizeWaitingBlocks ) { _require( __request.nextBlock > 0, "delayed: keep waiting or retry randomize" ); return _fetchRandomnessAfter(__request.nextBlock); } else { _revert("pending randomize"); } } else { _require( block.number >= __request.finality, string(abi.encodePacked( "finalizing on block #", Witnet.toString(uint256(__request.finality)) )) ); return __request.randomness; } } /// @dev Recursively searches for the number of the first block after the given one in which a Witnet /// @dev randomness request was posted. Returns 0 if none found. function _searchNextBlock(uint256 _target, uint256 _latest) internal view returns (uint256) { return (_target >= _latest ? __storage().requests[_latest].nextBlock : _searchNextBlock(_target, __storage().requests[_latest].prevBlock) ); } /// @dev Recursively searches for the number of the first block before the given one in which a Witnet /// @dev randomness request was posted. Returns 0 if none found. function _searchPrevBlock(uint256 _target, uint256 _latest) internal view returns (uint256) { return (_target > _latest ? _latest : _searchPrevBlock(_target, __storage().requests[_latest].prevBlock) ); } function __storage() internal pure returns (Storage storage _ptr) { assembly { _ptr.slot := _WIT_RANDOMNESS_STORAGE_SLOT } } }