From 03c39e58f5515799269a4009e39ce687b0013de8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 2 Oct 2024 19:05:43 +0200 Subject: [PATCH 1/8] refactor: WitnetDeployer* inheritance tree --- contracts/core/WitnetDeployer.sol | 8 ++-- contracts/core/WitnetDeployerCfxCore.sol | 31 ++++------------ contracts/core/WitnetDeployerMeter.sol | 47 +++--------------------- 3 files changed, 16 insertions(+), 70 deletions(-) diff --git a/contracts/core/WitnetDeployer.sol b/contracts/core/WitnetDeployer.sol index ddcda5a8..827298ce 100644 --- a/contracts/core/WitnetDeployer.sol +++ b/contracts/core/WitnetDeployer.sol @@ -18,7 +18,7 @@ contract WitnetDeployer { /// @param _salt Arbitrary value to modify resulting address. /// @return _deployed Just deployed contract address. function deploy(bytes memory _initCode, bytes32 _salt) - external + virtual public returns (address _deployed) { _deployed = determineAddr(_initCode, _salt); @@ -35,7 +35,7 @@ contract WitnetDeployer { /// @param _salt Arbitrary value to modify resulting address. /// @return Deterministic contract address. function determineAddr(bytes memory _initCode, bytes32 _salt) - public view + virtual public view returns (address) { return address( @@ -51,14 +51,14 @@ contract WitnetDeployer { } function determineProxyAddr(bytes32 _salt) - public view + virtual public view returns (address) { return Create3.determineAddr(_salt); } function proxify(bytes32 _proxySalt, address _firstImplementation, bytes memory _initData) - external + virtual external returns (WitnetProxy) { address _proxyAddr = determineProxyAddr(_proxySalt); diff --git a/contracts/core/WitnetDeployerCfxCore.sol b/contracts/core/WitnetDeployerCfxCore.sol index d5cf870a..b29583b6 100644 --- a/contracts/core/WitnetDeployerCfxCore.sol +++ b/contracts/core/WitnetDeployerCfxCore.sol @@ -2,38 +2,20 @@ pragma solidity >=0.8.0 <0.9.0; -import "./WitnetProxy.sol"; +import "./WitnetDeployer.sol"; -/// @notice WitnetDeployer contract used both as CREATE2 factory (EIP-1014) for Witnet artifacts, +/// @notice WitnetDeployerCfxCore contract used both as CREATE2 factory (EIP-1014) for Witnet artifacts, /// @notice and CREATE3 factory (EIP-3171) for Witnet proxies, on the Conflux Core Ecosystem. /// @author Guillermo Díaz -contract WitnetDeployerCfxCore { - - /// @notice Use given `_initCode` and `_salt` to deploy a contract into a deterministic address. - /// @dev The address of deployed address will be determined by both the `_initCode` and the `_salt`, but not the address - /// @dev nor the nonce of the caller (i.e. see EIP-1014). - /// @param _initCode Creation code, including construction logic and input parameters. - /// @param _salt Arbitrary value to modify resulting address. - /// @return _deployed Just deployed contract address. - function deploy(bytes memory _initCode, bytes32 _salt) - public - returns (address _deployed) - { - _deployed = determineAddr(_initCode, _salt); - if (_deployed.code.length == 0) { - assembly { - _deployed := create2(0, add(_initCode, 0x20), mload(_initCode), _salt) - } - require(_deployed != address(0), "WitnetDeployer: deployment failed"); - } - } +contract WitnetDeployerCfxCore is WitnetDeployer { /// @notice Determine counter-factual address of the contract that would be deployed by the given `_initCode` and a `_salt`. /// @param _initCode Creation code, including construction logic and input parameters. /// @param _salt Arbitrary value to modify resulting address. /// @return Deterministic contract address. function determineAddr(bytes memory _initCode, bytes32 _salt) + virtual override public view returns (address) { @@ -51,6 +33,7 @@ contract WitnetDeployerCfxCore { } function determineProxyAddr(bytes32 _salt) + virtual override public view returns (address) { @@ -58,7 +41,7 @@ contract WitnetDeployerCfxCore { } function proxify(bytes32 _proxySalt, address _firstImplementation, bytes memory _initData) - external + virtual override external returns (WitnetProxy) { address _proxyAddr = determineProxyAddr(_proxySalt); @@ -78,7 +61,7 @@ contract WitnetDeployerCfxCore { ); return WitnetProxy(payable(_proxyAddr)); } else { - revert("WitnetDeployer: already proxified"); + revert("WitnetDeployerCfxCore: already proxified"); } } diff --git a/contracts/core/WitnetDeployerMeter.sol b/contracts/core/WitnetDeployerMeter.sol index e60620a6..af201ccd 100644 --- a/contracts/core/WitnetDeployerMeter.sol +++ b/contracts/core/WitnetDeployerMeter.sol @@ -2,54 +2,16 @@ pragma solidity >=0.8.0 <0.9.0; -import "./WitnetProxy.sol"; +import "./WitnetDeployer.sol"; -/// @notice WitnetDeployer contract used both as CREATE2 factory (EIP-1014) for Witnet artifacts, +/// @notice WitnetDeployerMeter contract used both as CREATE2 factory (EIP-1014) for Witnet artifacts, /// @notice and CREATE3 factory (EIP-3171) for Witnet proxies, on the Meter Ecosystem. /// @author Guillermo Díaz -contract WitnetDeployerMeter { - - /// @notice Use given `_initCode` and `_salt` to deploy a contract into a deterministic address. - /// @dev The address of deployed address will be determined by both the `_initCode` and the `_salt`, but not the address - /// @dev nor the nonce of the caller (i.e. see EIP-1014). - /// @param _initCode Creation code, including construction logic and input parameters. - /// @param _salt Arbitrary value to modify resulting address. - /// @return _deployed Just deployed contract address. - function deploy(bytes memory _initCode, bytes32 _salt) - public - returns (address _deployed) - { - _deployed = determineAddr(_initCode, _salt); - if (_deployed.code.length == 0) { - assembly { - _deployed := create2(0, add(_initCode, 0x20), mload(_initCode), _salt) - } - require(_deployed != address(0), "WitnetDeployerMeter: deployment failed"); - } - } - - /// @notice Determine counter-factual address of the contract that would be deployed by the given `_initCode` and a `_salt`. - /// @param _initCode Creation code, including construction logic and input parameters. - /// @param _salt Arbitrary value to modify resulting address. - /// @return Deterministic contract address. - function determineAddr(bytes memory _initCode, bytes32 _salt) - public view - returns (address) - { - return address( - uint160(uint(keccak256( - abi.encodePacked( - bytes1(0xff), - address(this), - _salt, - keccak256(_initCode) - ) - ))) - ); - } +contract WitnetDeployerMeter is WitnetDeployer { function determineProxyAddr(bytes32 _salt) + virtual override public view returns (address) { @@ -57,6 +19,7 @@ contract WitnetDeployerMeter { } function proxify(bytes32 _proxySalt, address _firstImplementation, bytes memory _initData) + virtual override external returns (WitnetProxy) { From 485f1727ec9ad758275467685b69ed6b0a4719c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 2 Oct 2024 19:06:45 +0200 Subject: [PATCH 2/8] chore: lighten size of WitOracleTrustable* contracts --- .../core/trustable/WitOracleTrustableBase.sol | 168 ++--------------- contracts/data/WitOracleDataLib.sol | 178 +++++++++++++++++- 2 files changed, 183 insertions(+), 163 deletions(-) diff --git a/contracts/core/trustable/WitOracleTrustableBase.sol b/contracts/core/trustable/WitOracleTrustableBase.sol index b596dc44..13947e48 100644 --- a/contracts/core/trustable/WitOracleTrustableBase.sol +++ b/contracts/core/trustable/WitOracleTrustableBase.sol @@ -32,7 +32,7 @@ abstract contract WitOracleTrustableBase using Witnet for Witnet.QueryResponse; using Witnet for Witnet.RadonSLA; using Witnet for Witnet.Result; - using WitnetCBOR for WitnetCBOR.CBOR; + // using WitnetCBOR for WitnetCBOR.CBOR; WitOracleRequestFactory public immutable override factory; WitOracleRadonRegistry public immutable override registry; @@ -736,8 +736,7 @@ abstract contract WitOracleTrustableBase { // validate timestamp _require( - _resultTimestamp > 0 - && _resultTimestamp <= block.timestamp, + _resultTimestamp > 0, "bad timestamp" ); // results cannot be empty @@ -767,7 +766,7 @@ abstract contract WitOracleTrustableBase onlyReporters returns (uint256 _batchReward) { - for ( uint _i = 0; _i < _batchResults.length; _i ++) { + for (uint _i = 0; _i < _batchResults.length; _i ++) { if ( WitOracleDataLib.seekQueryStatus(_batchResults[_i].queryId) != Witnet.QueryStatus.Posted @@ -889,72 +888,15 @@ abstract contract WitOracleTrustableBase bytes calldata _resultCborBytes ) virtual internal - returns (uint256 _evmReward) + returns (uint256) { - // read requester address and whether a callback was requested: - Witnet.QueryRequest storage __request = WitOracleDataLib.seekQueryRequest(_queryId); - - // read query EVM reward: - _evmReward = __request.evmReward; - - // set EVM reward right now as to avoid re-entrancy attacks: - __request.evmReward = 0; - - // determine whether a callback is required - if (__request.gasCallback > 0) { - ( - uint256 _evmCallbackActualGas, - bool _evmCallbackSuccess, - string memory _evmCallbackRevertMessage - ) = __reportResultCallback( - _queryId, - _resultTimestamp, - _resultTallyHash, - _resultCborBytes, - __request.requester, - __request.gasCallback - ); - if (_evmCallbackSuccess) { - // => the callback run successfully - emit WitOracleQueryReponseDelivered( - _queryId, - _getGasPrice(), - _evmCallbackActualGas - ); - } else { - // => the callback reverted - emit WitOracleQueryResponseDeliveryFailed( - _queryId, - _getGasPrice(), - _evmCallbackActualGas, - bytes(_evmCallbackRevertMessage).length > 0 - ? _evmCallbackRevertMessage - : "WitOracle: callback exceeded gas limit", - _resultCborBytes - ); - } - // upon delivery, successfull or not, the audit trail is saved into storage, - // but not the actual result which was intended to be passed over to the requester: - __writeQueryQueryResponse( - _queryId, - _resultTimestamp, - _resultTallyHash, - hex"" - ); - } else { - // => no callback is involved - emit WitOracleQueryResponse( - _queryId, - _getGasPrice() - ); - // write query result and audit trail data into storage - __writeQueryQueryResponse( - _queryId, - _resultTimestamp, - _resultTallyHash, - _resultCborBytes - ); - } + return WitOracleDataLib.reportResult( + _getGasPrice(), + _queryId, + _resultTimestamp, + _resultTallyHash, + _resultCborBytes + ); } function __reportResultAndReward( @@ -979,77 +921,6 @@ abstract contract WitOracleTrustableBase ); } - function __reportResultCallback( - uint256 _queryId, - uint64 _resultTimestamp, - bytes32 _resultTallyHash, - bytes calldata _resultCborBytes, - address _evmRequester, - uint256 _evmCallbackGasLimit - ) - virtual internal - returns ( - uint256 _evmCallbackActualGas, - bool _evmCallbackSuccess, - string memory _evmCallbackRevertMessage - ) - { - _evmCallbackActualGas = gasleft(); - if (_resultCborBytes[0] == bytes1(0xd8)) { - WitnetCBOR.CBOR[] memory _errors = WitnetCBOR.fromBytes(_resultCborBytes).readArray(); - if (_errors.length < 2) { - // try to report result with unknown error: - try IWitOracleConsumer(_evmRequester).reportWitOracleResultError{gas: _evmCallbackGasLimit}( - _queryId, - _resultTimestamp, - _resultTallyHash, - block.number, - Witnet.ResultErrorCodes.Unknown, - WitnetCBOR.CBOR({ - buffer: WitnetBuffer.Buffer({ data: hex"", cursor: 0}), - initialByte: 0, - majorType: 0, - additionalInformation: 0, - len: 0, - tag: 0 - }) - ) { - _evmCallbackSuccess = true; - } catch Error(string memory err) { - _evmCallbackRevertMessage = err; - } - } else { - // try to report result with parsable error: - try IWitOracleConsumer(_evmRequester).reportWitOracleResultError{gas: _evmCallbackGasLimit}( - _queryId, - _resultTimestamp, - _resultTallyHash, - block.number, - Witnet.ResultErrorCodes(_errors[0].readUint()), - _errors[0] - ) { - _evmCallbackSuccess = true; - } catch Error(string memory err) { - _evmCallbackRevertMessage = err; - } - } - } else { - // try to report result result with no error : - try IWitOracleConsumer(_evmRequester).reportWitOracleResultValue{gas: _evmCallbackGasLimit}( - _queryId, - _resultTimestamp, - _resultTallyHash, - block.number, - WitnetCBOR.fromBytes(_resultCborBytes) - ) { - _evmCallbackSuccess = true; - } catch Error(string memory err) { - _evmCallbackRevertMessage = err; - } catch (bytes memory) {} - } - _evmCallbackActualGas -= gasleft(); - } - function __setReporters(address[] memory _reporters) virtual internal { @@ -1065,21 +936,4 @@ abstract contract WitOracleTrustableBase return WitOracleDataLib.data(); } - function __writeQueryQueryResponse( - uint256 _queryId, - uint32 _resultTimestamp, - bytes32 _resultTallyHash, - bytes memory _resultCborBytes - ) - virtual internal - { - WitOracleDataLib.seekQuery(_queryId).response = Witnet.QueryResponse({ - reporter: msg.sender, - finality: uint64(block.number), - resultTimestamp: _resultTimestamp, - resultTallyHash: _resultTallyHash, - resultCborBytes: _resultCborBytes - }); - } - } \ No newline at end of file diff --git a/contracts/data/WitOracleDataLib.sol b/contracts/data/WitOracleDataLib.sol index 184505f5..d471f2ae 100644 --- a/contracts/data/WitOracleDataLib.sol +++ b/contracts/data/WitOracleDataLib.sol @@ -3,6 +3,9 @@ pragma solidity >=0.7.0 <0.9.0; import "../WitOracleRadonRegistry.sol"; +import "../interfaces/IWitOracleConsumer.sol"; +import "../interfaces/IWitOracleEvents.sol"; +import "../interfaces/IWitOracleReporter.sol"; import "../libs/Witnet.sol"; /// @title Witnet Request Board base data model library @@ -10,6 +13,7 @@ import "../libs/Witnet.sol"; library WitOracleDataLib { using Witnet for Witnet.QueryRequest; + using WitnetCBOR for WitnetCBOR.CBOR; bytes32 internal constant _WIT_ORACLE_DATA_SLOTHASH = /* keccak256("io.witnet.boards.data") */ @@ -36,19 +40,36 @@ library WitOracleDataLib { return data().reporters[addr]; } + /// Saves query response into storage. + function saveQueryResponse( + uint256 queryId, + uint32 resultTimestamp, + bytes32 resultTallyHash, + bytes memory resultCborBytes + ) internal + { + seekQuery(queryId).response = Witnet.QueryResponse({ + reporter: msg.sender, + finality: uint64(block.number), + resultTimestamp: resultTimestamp, + resultTallyHash: resultTallyHash, + resultCborBytes: resultCborBytes + }); + } + /// Gets query storage by query id. - function seekQuery(uint256 _queryId) internal view returns (Witnet.Query storage) { - return data().queries[_queryId]; + function seekQuery(uint256 queryId) internal view returns (Witnet.Query storage) { + return data().queries[queryId]; } /// Gets the Witnet.QueryRequest part of a given query. - function seekQueryRequest(uint256 _queryId) internal view returns (Witnet.QueryRequest storage) { - return data().queries[_queryId].request; + function seekQueryRequest(uint256 queryId) internal view returns (Witnet.QueryRequest storage) { + return data().queries[queryId].request; } /// Gets the Witnet.Result part of a given query. - function seekQueryResponse(uint256 _queryId) internal view returns (Witnet.QueryResponse storage) { - return data().queries[_queryId].response; + function seekQueryResponse(uint256 queryId) internal view returns (Witnet.QueryResponse storage) { + return data().queries[queryId].response; } function seekQueryStatus(uint256 queryId) internal view returns (Witnet.QueryStatus) { @@ -126,4 +147,149 @@ library WitOracleDataLib { return "bad mood"; } } + + function reportResult( + uint256 evmGasPrice, + uint256 queryId, + uint32 resultTimestamp, + bytes32 resultTallyHash, + bytes calldata resultCborBytes + ) + public returns (uint256 evmReward) + { + // read requester address and whether a callback was requested: + Witnet.QueryRequest storage __request = seekQueryRequest(queryId); + + // read query EVM reward: + evmReward = __request.evmReward; + + // set EVM reward right now as to avoid re-entrancy attacks: + __request.evmReward = 0; + + // determine whether a callback is required + if (__request.gasCallback > 0) { + ( + uint256 evmCallbackActualGas, + bool evmCallbackSuccess, + string memory evmCallbackRevertMessage + ) = reportResultCallback( + __request.requester, + __request.gasCallback, + queryId, + resultTimestamp, + resultTallyHash, + resultCborBytes + ); + if (evmCallbackSuccess) { + // => the callback run successfully + emit IWitOracleEvents.WitOracleQueryReponseDelivered( + queryId, + evmGasPrice, + evmCallbackActualGas + ); + } else { + // => the callback reverted + emit IWitOracleEvents.WitOracleQueryResponseDeliveryFailed( + queryId, + evmGasPrice, + evmCallbackActualGas, + bytes(evmCallbackRevertMessage).length > 0 + ? evmCallbackRevertMessage + : "WitOracleDataLib: callback exceeded gas limit", + resultCborBytes + ); + } + // upon delivery, successfull or not, the audit trail is saved into storage, + // but not the actual result which was intended to be passed over to the requester: + saveQueryResponse( + queryId, + resultTimestamp, + resultTallyHash, + hex"" + ); + } else { + // => no callback is involved + emit IWitOracleEvents.WitOracleQueryResponse( + queryId, + evmGasPrice + ); + // write query result and audit trail data into storage + saveQueryResponse( + queryId, + resultTimestamp, + resultTallyHash, + resultCborBytes + ); + } + } + + function reportResultCallback( + address evmRequester, + uint256 evmCallbackGasLimit, + uint256 queryId, + uint64 resultTimestamp, + bytes32 resultTallyHash, + bytes calldata resultCborBytes + ) + public returns ( + uint256 evmCallbackActualGas, + bool evmCallbackSuccess, + string memory evmCallbackRevertMessage + ) + { + evmCallbackActualGas = gasleft(); + if (resultCborBytes[0] == bytes1(0xd8)) { + WitnetCBOR.CBOR[] memory _errors = WitnetCBOR.fromBytes(resultCborBytes).readArray(); + if (_errors.length < 2) { + // try to report result with unknown error: + try IWitOracleConsumer(evmRequester).reportWitOracleResultError{gas: evmCallbackGasLimit}( + queryId, + resultTimestamp, + resultTallyHash, + block.number, + Witnet.ResultErrorCodes.Unknown, + WitnetCBOR.CBOR({ + buffer: WitnetBuffer.Buffer({ data: hex"", cursor: 0}), + initialByte: 0, + majorType: 0, + additionalInformation: 0, + len: 0, + tag: 0 + }) + ) { + evmCallbackSuccess = true; + } catch Error(string memory err) { + evmCallbackRevertMessage = err; + } + } else { + // try to report result with parsable error: + try IWitOracleConsumer(evmRequester).reportWitOracleResultError{gas: evmCallbackGasLimit}( + queryId, + resultTimestamp, + resultTallyHash, + block.number, + Witnet.ResultErrorCodes(_errors[0].readUint()), + _errors[0] + ) { + evmCallbackSuccess = true; + } catch Error(string memory err) { + evmCallbackRevertMessage = err; + } + } + } else { + // try to report result result with no error : + try IWitOracleConsumer(evmRequester).reportWitOracleResultValue{gas: evmCallbackGasLimit}( + queryId, + resultTimestamp, + resultTallyHash, + block.number, + WitnetCBOR.fromBytes(resultCborBytes) + ) { + evmCallbackSuccess = true; + } catch Error(string memory err) { + evmCallbackRevertMessage = err; + } catch (bytes memory) {} + } + evmCallbackActualGas -= gasleft(); + } } From b1df26d4249040b7278ccfa1e027b42a0816b445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 2 Oct 2024 19:07:02 +0200 Subject: [PATCH 3/8] chore: upgrade solc version to 0.8.27 --- settings/solidity.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings/solidity.js b/settings/solidity.js index aa764479..d36c0692 100644 --- a/settings/solidity.js +++ b/settings/solidity.js @@ -1,6 +1,6 @@ module.exports = { default: { - version: "0.8.25", + version: "0.8.27", settings: { optimizer: { enabled: true, From 204a4d0fe842d049e865f26d9e0f0a15a346fc90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 4 Oct 2024 11:51:45 +0200 Subject: [PATCH 4/8] feat(factory): enable programmable construction of "modal" requests and templates --- .../core/trustable/WitOracleTrustableBase.sol | 1 - .../WitOracleRadonRegistryDefault.sol | 226 ++++++++++---- .../WitOracleRequestFactoryDefault.sol | 281 +++++++++++------- .../interfaces/IWitOracleRadonRegistry.sol | 26 +- .../IWitOracleRadonRegistryEvents.sol | 2 +- .../interfaces/IWitOracleRequestFactory.sol | 54 +++- .../interfaces/IWitOracleRequestTemplate.sol | 45 ++- contracts/libs/WitOracleRadonEncodingLib.sol | 59 +++- contracts/libs/WitnetBuffer.sol | 113 ++++++- 9 files changed, 617 insertions(+), 190 deletions(-) diff --git a/contracts/core/trustable/WitOracleTrustableBase.sol b/contracts/core/trustable/WitOracleTrustableBase.sol index 13947e48..22426324 100644 --- a/contracts/core/trustable/WitOracleTrustableBase.sol +++ b/contracts/core/trustable/WitOracleTrustableBase.sol @@ -32,7 +32,6 @@ abstract contract WitOracleTrustableBase using Witnet for Witnet.QueryResponse; using Witnet for Witnet.RadonSLA; using Witnet for Witnet.Result; - // using WitnetCBOR for WitnetCBOR.CBOR; WitOracleRequestFactory public immutable override factory; WitOracleRadonRegistry public immutable override registry; diff --git a/contracts/core/trustless/WitOracleRadonRegistryDefault.sol b/contracts/core/trustless/WitOracleRadonRegistryDefault.sol index 9acb9c15..b5465911 100644 --- a/contracts/core/trustless/WitOracleRadonRegistryDefault.sol +++ b/contracts/core/trustless/WitOracleRadonRegistryDefault.sol @@ -60,6 +60,10 @@ contract WitOracleRadonRegistryDefault ) {} + function _witOracleHash(bytes memory chunk) virtual internal pure returns (bytes32) { + return sha256(chunk); + } + receive() external payable { _revert("no transfers"); } @@ -195,12 +199,13 @@ contract WitOracleRadonRegistryDefault } function hashOf(bytes calldata _radBytecode) external pure override returns (bytes32) { - // todo: validate correctness of _radBytecode + // todo?: validate correctness of _radBytecode return _witOracleHash(_radBytecode); } function lookupRadonReducer(bytes32 _hash) - override public view + virtual override + public view returns (Witnet.RadonReducer memory _reducer) { _reducer = __database().reducers[_hash]; @@ -208,7 +213,7 @@ contract WitOracleRadonRegistryDefault } function lookupRadonRetrieval(bytes32 _hash) - override external view + override public view radonRetrievalExists(_hash) returns (Witnet.RadonRetrieval memory _source) { @@ -235,35 +240,34 @@ contract WitOracleRadonRegistryDefault override external view returns (Witnet.RadonRequest memory) { - RadonRequestPacked storage __packed = __database().requests[_radHash]; return Witnet.RadonRequest({ retrieve: lookupRadonRequestRetrievals(_radHash), - aggregate: lookupRadonReducer(bytes32(bytes16(__packed.aggregateTallyHashes))), - tally: lookupRadonReducer(bytes32(bytes16(__packed.aggregateTallyHashes << 128))) + aggregate: lookupRadonRequestAggregator(_radHash), + tally: lookupRadonRequestTally(_radHash) }); } function lookupRadonRequestAggregator(bytes32 _radHash) - override external view + override public view radonRequestExists(_radHash) returns (Witnet.RadonReducer memory) { if (__requests(_radHash).legacyTallyHash != bytes32(0)) { return lookupRadonReducer(__requests(_radHash).aggregateTallyHashes); } else { - return lookupRadonReducer(bytes32(bytes16(__requests(_radHash).aggregateTallyHashes))); + return lookupRadonReducer(bytes16(__requests(_radHash).aggregateTallyHashes)); } } function lookupRadonRequestTally(bytes32 _radHash) - override external view + override public view radonRequestExists(_radHash) returns (Witnet.RadonReducer memory) { if (__requests(_radHash).legacyTallyHash != bytes32(0)) { return lookupRadonReducer(__requests(_radHash).legacyTallyHash); } else { - return lookupRadonReducer(bytes32(bytes16(__requests(_radHash).aggregateTallyHashes << 128))); + return lookupRadonReducer(bytes16(__requests(_radHash).aggregateTallyHashes << 128)); } } @@ -301,12 +305,11 @@ contract WitOracleRadonRegistryDefault } } - function verifyRadonReducer(Witnet.RadonReducer memory _reducer) virtual override public - returns (bytes16 hash) + returns (bytes32 hash) { - hash = bytes16(keccak256(abi.encode(_reducer))); + hash = bytes32(bytes16(keccak256(abi.encode(_reducer)))); Witnet.RadonReducer storage __reducer = __database().reducers[hash]; if ( uint8(__reducer.opcode) == 0 @@ -315,39 +318,82 @@ contract WitOracleRadonRegistryDefault _reducer.validate(); __reducer.opcode = _reducer.opcode; __pushRadonReducerFilters(__reducer, _reducer.filters); - emit NewRadonReducer(hash); + emit NewRadonReducer(bytes16(hash)); } } function verifyRadonRequest( - bytes32[] calldata retrieveHashes, - Witnet.RadonReducer calldata aggregate, - Witnet.RadonReducer calldata tally + bytes32[] calldata _retrieveHashes, + Witnet.RadonReducer calldata _aggregateReducer, + Witnet.RadonReducer calldata _tallyReducer ) override external returns (bytes32 radHash) { - // TODO + return __verifyRadonRequest( + _retrieveHashes, + new string[][](_retrieveHashes.length), + _aggregateReducer, + _tallyReducer + ); + } + + function verifyRadonRequest( + bytes32[] calldata _retrieveHashes, + bytes32 _aggregateReducerHash, + bytes32 _tallyReducerHash + ) + override external + returns (bytes32 radHash) + { + return __verifyRadonRequest( + _retrieveHashes, + new string[][](_retrieveHashes.length), + _aggregateReducerHash, + _tallyReducerHash + ); } function verifyRadonRequest( bytes32[] calldata _retrieveHashes, - string[][] calldata _retrieveArgs, - Witnet.RadonReducer calldata _aggregate, - Witnet.RadonReducer calldata _tally + string[][] calldata _retrieveArgsValues, + Witnet.RadonReducer calldata _aggregateReducer, + Witnet.RadonReducer calldata _tallyReducer ) override external - returns (bytes32 _radHash) + returns (bytes32) { - // TODO + return __verifyRadonRequest( + _retrieveHashes, + _retrieveArgsValues, + _aggregateReducer, + _tallyReducer + ); + } + + function verifyRadonRequest( + bytes32[] calldata _retrieveHashes, + string[][] calldata _retrieveArgsValues, + bytes32 _aggregateReducerHash, + bytes32 _tallyReducerHash + ) + override external + returns (bytes32) + { + return __verifyRadonRequest( + _retrieveHashes, + _retrieveArgsValues, + _aggregateReducerHash, + _tallyReducerHash + ); } function verifyRadonRetrieval( Witnet.RadonRetrievalMethods _requestMethod, - string calldata _requestURL, - string calldata _requestBody, + string memory _requestURL, + string memory _requestBody, string[2][] memory _requestHeaders, - bytes calldata _requestRadonScript + bytes memory _requestRadonScript ) virtual override public returns (bytes32 hash) @@ -398,13 +444,42 @@ contract WitOracleRadonRegistryDefault } } + function verifyRadonRetrieval( + bytes32 _baseRetrieveHash, + string calldata _lastArgValue + ) + override external + returns (bytes32) + { + Witnet.RadonRetrieval memory _retrieval = lookupRadonRetrieval(_baseRetrieveHash); + _require( + _retrieval.argsCount > 0, + "non-parameterized radon retrieval" + ); + _retrieval = _retrieval.replaceWildcards( + _retrieval.argsCount - 1, + _lastArgValue + ); + return verifyRadonRetrieval( + _retrieval.method, + _retrieval.url, + _retrieval.body, + _retrieval.headers, + _retrieval.radonScript + ); + } + // ================================================================================================================ // --- IWitOracleRadonRegistryLegacy --------------------------------------------------------------------------------- - function lookupRadonRequestResultMaxSize(bytes32) override external pure returns (uint16) { + function lookupRadonRequestResultMaxSize(bytes32 _radHash) + override external view + radonRequestExists(_radHash) + returns (uint16) + { return 32; - } + } function lookupRadonRequestSources(bytes32 _radHash) override external view @@ -423,31 +498,61 @@ contract WitOracleRadonRegistryDefault } function verifyRadonRequest( - bytes32[] memory _retrieveHashes, - bytes32 _aggregateReduceHash, - bytes32 _tallyReduceHash, + bytes32[] calldata _retrieveHashes, + bytes32 _aggregateReducerHash, + bytes32 _tallyReducerHash, uint16, - string[][] memory _retrieveArgs + string[][] calldata _retrieveArgsValues ) virtual override public - returns (bytes32 _radHash) + returns (bytes32) { - // check reducers are valid: - _require( - uint8(__database().reducers[_aggregateReduceHash].opcode) != 0, - "unknown aggregate reducer" + return __verifyRadonRequest( + _retrieveHashes, + _retrieveArgsValues, + lookupRadonReducer(_aggregateReducerHash), + lookupRadonReducer(_tallyReducerHash) ); - _require( - uint8(__database().reducers[_tallyReduceHash].opcode) != 0, - "unknown tally reducer" + } + + + // ================================================================================================================ + // --- Internal methods ------------------------------------------------------------------------------------------- + + function __verifyRadonRequest( + bytes32[] calldata _retrieveHashes, + string[][] memory _retrieveArgsValues, + bytes32 _aggregateReducerHash, + bytes32 _tallyReducerHash + ) + virtual internal + returns (bytes32 _radHash) + { + return __verifyRadonRequest( + _retrieveHashes, + _retrieveArgsValues, + lookupRadonReducer(_aggregateReducerHash), + lookupRadonReducer(_tallyReducerHash) ); + } - // calculate unique hash: + function __verifyRadonRequest( + bytes32[] calldata _retrieveHashes, + string[][] memory _retrieveArgsValues, + Witnet.RadonReducer memory _aggregateReducer, + Witnet.RadonReducer memory _tallyReducer + ) + virtual internal + returns (bytes32 _radHash) + { + // calculate unique hashes: + bytes32 _aggregateReducerHash = verifyRadonReducer(_aggregateReducer); + bytes32 _tallyReducerHash = verifyRadonReducer(_tallyReducer); bytes32 hash = keccak256(abi.encode( _retrieveHashes, - _aggregateReduceHash, - _tallyReduceHash, - _retrieveArgs + _aggregateReducerHash, + _tallyReducerHash, + _retrieveArgsValues )); // verify, compose and register only if hash is not yet known: @@ -462,14 +567,10 @@ contract WitOracleRadonRegistryDefault // Check that number of args arrays matches the number of sources: _require( - _retrieveHashes.length == _retrieveArgs.length, + _retrieveHashes.length == _retrieveArgsValues.length, "args mismatch" ); - // Check sources and tally reducers: - Witnet.RadonReducer memory _aggregator = __database().reducers[_aggregateReduceHash]; - Witnet.RadonReducer memory _tally = __database().reducers[_tallyReduceHash]; - // Check result type consistency among all sources: Witnet.RadonDataTypes _resultDataType; Witnet.RadonRetrieval[] memory _retrievals = new Witnet.RadonRetrieval[](_retrieveHashes.length); @@ -486,17 +587,20 @@ contract WitOracleRadonRegistryDefault _revert("mismatching retrievals"); } // check enough args are provided for each source - if (_retrieveArgs[_ix].length < uint(_retrievals[_ix].argsCount)) { - _revert("missing args"); + if (_retrieveArgsValues[_ix].length != uint(_retrievals[_ix].argsCount)) { + _revert(string(abi.encodePacked( + "mismatching args count on retrieval #", + Witnet.toString(_ix + 1) + ))); } } // Build radon retrieval bytecode: bytes memory _bytecode = _retrievals.encode( - _retrieveArgs, - _aggregator.encode(), - _tally.encode(), - 0 //_resultMaxSize + _retrieveArgsValues, + _aggregateReducer.encode(), + _tallyReducer.encode(), + 0 ); _require( _bytecode.length <= 65535, @@ -509,7 +613,8 @@ contract WitOracleRadonRegistryDefault __database().radsBytecode[_radHash] = _bytecode; __database().requests[_radHash] = RadonRequestPacked({ _args: new string[][](0), - aggregateTallyHashes: (_aggregateReduceHash & 0xffffffffffffffffffffffffffffffff00000000000000000000000000000000) | (_tallyReduceHash >> 128), + aggregateTallyHashes: (_aggregateReducerHash & 0xffffffffffffffffffffffffffffffff00000000000000000000000000000000) + | (_tallyReducerHash >> 128), _radHash: bytes32(0), _resultDataType: Witnet.RadonDataTypes.Any, _resultMaxSize: 0, @@ -522,24 +627,15 @@ contract WitOracleRadonRegistryDefault } } - - // ================================================================================================================ - // --- Internal state-modifying methods --------------------------------------------------------------------------- - function __pushRadonReducerFilters( Witnet.RadonReducer storage __reducer, Witnet.RadonFilter[] memory _filters ) internal - virtual { for (uint _ix = 0; _ix < _filters.length; _ix ++) { __reducer.filters.push(_filters[_ix]); } } - function _witOracleHash(bytes memory chunk) virtual internal pure returns (bytes32) { - return sha256(chunk); - } - } \ No newline at end of file diff --git a/contracts/core/trustless/WitOracleRequestFactoryDefault.sol b/contracts/core/trustless/WitOracleRequestFactoryDefault.sol index c3b4e1c3..8242d392 100644 --- a/contracts/core/trustless/WitOracleRequestFactoryDefault.sol +++ b/contracts/core/trustless/WitOracleRequestFactoryDefault.sol @@ -93,20 +93,20 @@ contract WitOracleRequestFactoryDefault function initializeWitOracleRequestTemplate( bytes32[] calldata _retrieveHashes, - bytes16 _aggregateReduceHash, - bytes16 _tallyReduceHash + bytes16 _aggregateReducerHash, + bytes16 _tallyReducerHash ) virtual public initializer returns (address) { _require(_retrieveHashes.length > 0, "no retrievals?"); - _require(_aggregateReduceHash != bytes16(0), "no aggregate reducer?"); - _require(_tallyReduceHash != bytes16(0), "no tally reducer?"); + _require(_aggregateReducerHash != bytes16(0), "no aggregate reducer?"); + _require(_tallyReducerHash != bytes16(0), "no tally reducer?"); WitOracleRequestTemplateStorage storage __data = __witOracleRequestTemplate(); __data.retrieveHashes = _retrieveHashes; - __data.aggregateReduceHash = _aggregateReduceHash; - __data.tallyReduceHash = _tallyReduceHash; + __data.aggregateReduceHash = _aggregateReducerHash; + __data.tallyReduceHash = _tallyReducerHash; return address(this); } @@ -367,50 +367,43 @@ contract WitOracleRequestFactoryDefault function buildWitOracleRequest( bytes32[] calldata _retrieveHashes, - Witnet.RadonReducer calldata _aggregate, - Witnet.RadonReducer calldata _tally + Witnet.RadonReducer calldata _aggregateReducer, + Witnet.RadonReducer calldata _tallyReducer ) virtual override external onlyOnFactory - returns (address) + returns (IWitOracleRequest) { - WitOracleRadonRegistry _registry = _getWitOracleRadonRegistry(); - - // TODO: checks and reducers verification should be done by the registry instead ... - - // Check input retrievals: - _require( - !_checkParameterizedRadonRetrievals(_registry, _retrieveHashes), - "parameterized retrievals" - ); - - // Check input reducers: - bytes16 _aggregateReduceHash = _registry.verifyRadonReducer(_aggregate); - bytes16 _tallyReduceHash = _registry.verifyRadonReducer(_tally); - - // Verify Radon Request: - bytes32 _radHash = IWitOracleRadonRegistryLegacy(address(_registry)).verifyRadonRequest( - _retrieveHashes, - bytes32(_aggregateReduceHash), - bytes32(_tallyReduceHash), - uint16(0), - new string[][](0) + return __buildWitOracleRequest( + _getWitOracleRadonRegistry().verifyRadonRequest( + _retrieveHashes, + _aggregateReducer, + _tallyReducer + ) ); + } - // Determine request's minimal-proxy counter-factual salt and address: - (address _requestAddr, bytes32 _requestSalt) = _determineWitOracleRequestAddressAndSalt(_radHash); - - // Create and initialize counter-factual request just once: - if (_requestAddr.code.length == 0) { - _requestAddr = WitOracleRequestFactoryDefault(_cloneDeterministic(_requestSalt)) - .initializeWitOracleRequest( - _radHash - ); + function buildWitOracleRequestModal( + bytes32 _baseRetrieveHash, + string[][] calldata _retrieveArgsValues, + Witnet.RadonFilter[] calldata _tallySlashingFiltres + ) + virtual override external + onlyOnFactory + returns (IWitOracleRequest) + { + bytes32[] memory _retrieveHashes = new bytes32[](_retrieveArgsValues.length); + for (uint _ix = 0; _ix < _retrieveHashes.length; _ix ++) { + _retrieveHashes[_ix] = _baseRetrieveHash; } - - // Emit event even when building same request more than once - emit WitOracleRequestBuilt(_requestAddr); - return _requestAddr; + return __buildWitOracleRequest( + _getWitOracleRadonRegistry().verifyRadonRequest( + _retrieveHashes, + _retrieveArgsValues, + Witnet.RadonReducer({ opcode: Witnet.RadonReduceOpcodes.Mode, filters: new Witnet.RadonFilter[](0) }), + Witnet.RadonReducer({ opcode: Witnet.RadonReduceOpcodes.Mode, filters: _tallySlashingFiltres }) + ) + ); } function buildWitOracleRequestTemplate( @@ -420,7 +413,7 @@ contract WitOracleRequestFactoryDefault ) virtual override external onlyOnFactory - returns (address) + returns (IWitOracleRequestTemplate) { WitOracleRadonRegistry _registry = _getWitOracleRadonRegistry(); @@ -430,32 +423,47 @@ contract WitOracleRequestFactoryDefault "non-parameterized retrievals" ); - // Check input reducers: - bytes16 _aggregateReduceHash = _registry.verifyRadonReducer(_aggregate); - bytes16 _tallyReduceHash = _registry.verifyRadonReducer(_tally); - - // Determine template's minimal-proxy counter-factual salt and address: - (address _templateAddr, bytes32 _templateSalt) = _determineWitOracleRequestTemplateAddressAndSalt( - _retrieveHashes, - _aggregateReduceHash, - _tallyReduceHash + return __buildWitOracleRequestTemplate( + _retrieveHashes, + bytes16(_registry.verifyRadonReducer(_aggregate)), + bytes16(_registry.verifyRadonReducer(_tally)) ); - - // Create and initialize counter-factual template just once: - if (_templateAddr.code.length == 0) { - _templateAddr = address( - WitOracleRequestFactoryDefault(_cloneDeterministic(_templateSalt)) - .initializeWitOracleRequestTemplate( - _retrieveHashes, - _aggregateReduceHash, - _tallyReduceHash - ) + } + + function buildWitOracleRequestTemplateModal( + bytes32 _baseRetrieveHash, + string[] calldata _lastArgValues, + Witnet.RadonFilter[] calldata _tallySlashingFilters + ) + virtual override external + returns (IWitOracleRequestTemplate) + { + WitOracleRadonRegistry _registry = _getWitOracleRadonRegistry(); + + // spawn retrievals by repeatedly setting different values to the last parameter + // of given retrieval: + bytes32[] memory _retrieveHashes = new bytes32[](_lastArgValues.length); + for (uint _ix = 0; _ix < _retrieveHashes.length; _ix ++) { + _retrieveHashes[_ix] = _registry.verifyRadonRetrieval( + _baseRetrieveHash, + _lastArgValues[_ix] ); } - - // Emit event even when building same template more than one - emit WitOracleRequestTemplateBuilt(_templateAddr); - return _templateAddr; + return __buildWitOracleRequestTemplate( + _retrieveHashes, + bytes16(_registry.verifyRadonReducer( + Witnet.RadonReducer({ + opcode: Witnet.RadonReduceOpcodes.Mode, + filters: new Witnet.RadonFilter[](0) + }) + )), + bytes16(_registry.verifyRadonReducer( + Witnet.RadonReducer({ + opcode: Witnet.RadonReduceOpcodes.Mode, + filters: _tallySlashingFilters + }) + )) + ); } function verifyRadonRetrieval( @@ -482,38 +490,32 @@ contract WitOracleRequestFactoryDefault /// =============================================================================================================== /// --- IWitOracleRequestTemplate implementation --------------------------------------------------------------------- - function buildWitOracleRequest(string[][] calldata _retrieveArgs) + function buildWitOracleRequest(string[][] calldata _retrieveArgsValues) override external onlyOnTemplates - returns (address _request) + returns (IWitOracleRequest) { - WitOracleRadonRegistry _registry = _getWitOracleRadonRegistry(); - WitOracleRequestTemplateStorage storage __template = __witOracleRequestTemplate(); - // Verify Radon Request using template's retrieve hashes, aggregate and tally reducers, // and given args: - bytes32 _radHash = IWitOracleRadonRegistryLegacy(address(_registry)).verifyRadonRequest( + WitOracleRequestTemplateStorage storage __template = __witOracleRequestTemplate(); + bytes32 _radHash = _getWitOracleRadonRegistry().verifyRadonRequest( __template.retrieveHashes, + _retrieveArgsValues, bytes32(__template.aggregateReduceHash), - bytes32(__template.tallyReduceHash), - 0, - _retrieveArgs + bytes32(__template.tallyReduceHash) ); - - // Determine request's minimal-proxy counter-factual salt and address: - (address _requestAddr, bytes32 _requestSalt) = _determineWitOracleRequestAddressAndSalt(_radHash); - // Create and initialize counter-factual request just once: - if (_requestAddr.code.length == 0) { - _requestAddr = WitOracleRequestFactoryDefault(_cloneDeterministic(_requestSalt)) - .initializeWitOracleRequest( - _radHash - ); - } + return __buildWitOracleRequest(_radHash); + } - // Emit event even when building same request more than once - emit WitOracleRequestBuilt(_requestAddr); - return _requestAddr; + function buildWitOracleRequest(string calldata _singleArgValue) + override external + onlyOnTemplates + returns (IWitOracleRequest) + { + return __buildWitOracleRequest( + __verifyRadonRequestFromTemplate(_singleArgValue) + ); } function getArgsCount() @@ -531,20 +533,27 @@ contract WitOracleRequestFactoryDefault } } - function verifyRadonRequest(string[][] calldata _args) + function verifyRadonRequest(string[][] calldata _retrieveArgsValues) override external onlyOnTemplates returns (bytes32) { - return IWitOracleRadonRegistryLegacy(address(_getWitOracleRadonRegistry())).verifyRadonRequest( + return _getWitOracleRadonRegistry().verifyRadonRequest( __witOracleRequestTemplate().retrieveHashes, + _retrieveArgsValues, bytes32(__witOracleRequestTemplate().aggregateReduceHash), - bytes32(__witOracleRequestTemplate().tallyReduceHash), - 0, - _args + bytes32(__witOracleRequestTemplate().tallyReduceHash) ); } + function verifyRadonRequest(string calldata _singleArgValue) + override external + onlyOnTemplates + returns (bytes32) + { + return __verifyRadonRequestFromTemplate(_singleArgValue); + } + /// =============================================================================================================== /// --- IWitOracleRequest implementation ----------------------------------------------------------------------------- @@ -569,6 +578,58 @@ contract WitOracleRequestFactoryDefault /// =============================================================================================================== /// --- Internal methods ------------------------------------------------------------------------------------------ + + function __buildWitOracleRequest(bytes32 _radHash) + virtual internal + returns (IWitOracleRequest) + { + // Determine request's minimal-proxy counter-factual salt and address: + (address _requestAddr, bytes32 _requestSalt) = _determineWitOracleRequestAddressAndSalt(_radHash); + + // Create and initialize counter-factual request just once: + if (_requestAddr.code.length == 0) { + _requestAddr = WitOracleRequestFactoryDefault(_cloneDeterministic(_requestSalt)) + .initializeWitOracleRequest( + _radHash + ); + } + + // Emit event even when building same request more than once + emit WitOracleRequestBuilt(_requestAddr); + return IWitOracleRequest(_requestAddr); + } + + function __buildWitOracleRequestTemplate( + bytes32[] memory _retrieveHashes, + bytes16 _aggregateReducerHash, + bytes16 _tallyReducerHash + ) + virtual internal + returns (IWitOracleRequestTemplate) + { + // Determine template's minimal-proxy counter-factual salt and address: + (address _templateAddr, bytes32 _templateSalt) = _determineWitOracleRequestTemplateAddressAndSalt( + _retrieveHashes, + _aggregateReducerHash, + _tallyReducerHash + ); + + // Create and initialize counter-factual template just once: + if (_templateAddr.code.length == 0) { + _templateAddr = address( + WitOracleRequestFactoryDefault(_cloneDeterministic(_templateSalt)) + .initializeWitOracleRequestTemplate( + _retrieveHashes, + _aggregateReducerHash, + _tallyReducerHash + ) + ); + } + + // Emit event even when building same template more than one + emit WitOracleRequestTemplateBuilt(_templateAddr); + return IWitOracleRequestTemplate(_templateAddr); + } function _checkParameterizedRadonRetrievals(WitOracleRadonRegistry _registry, bytes32[] calldata _retrieveHashes) internal view returns (bool _parameterized) @@ -613,9 +674,9 @@ contract WitOracleRequestFactoryDefault } function _determineWitOracleRequestTemplateAddressAndSalt( - bytes32[] calldata _retrieveHashes, - bytes16 _aggregateReduceHash, - bytes16 _tallyReduceHash + bytes32[] memory _retrieveHashes, + bytes16 _aggregateReducerHash, + bytes16 _tallyReducerHash ) virtual internal view returns (address, bytes32) @@ -627,8 +688,8 @@ contract WitOracleRequestFactoryDefault bytes4(_WITNET_UPGRADABLE_VERSION), // - different templates params: _retrieveHashes, - _aggregateReduceHash, - _tallyReduceHash + _aggregateReducerHash, + _tallyReducerHash ) ); return ( @@ -642,4 +703,24 @@ contract WitOracleRequestFactoryDefault )))), _salt ); } -} \ No newline at end of file + + function __verifyRadonRequestFromTemplate(string calldata _singleArgValue) + virtual internal + returns (bytes32) + { + WitOracleRadonRegistry _registry = _getWitOracleRadonRegistry(); + WitOracleRequestTemplateStorage storage __template = __witOracleRequestTemplate(); + bytes32[] memory _retrieveHashes = new bytes32[](__template.retrieveHashes.length); + for (uint _ix = 0; _ix < _retrieveHashes.length; _ix ++) { + _retrieveHashes[_ix] = _registry.verifyRadonRetrieval( + __template.retrieveHashes[_ix], + _singleArgValue + ); + } + return _registry.verifyRadonRequest( + _retrieveHashes, + bytes32(__template.aggregateReduceHash), + bytes32(__template.tallyReduceHash) + ); + } +} diff --git a/contracts/interfaces/IWitOracleRadonRegistry.sol b/contracts/interfaces/IWitOracleRadonRegistry.sol index 513a3c64..0ebbc09a 100644 --- a/contracts/interfaces/IWitOracleRadonRegistry.sol +++ b/contracts/interfaces/IWitOracleRadonRegistry.sol @@ -28,9 +28,12 @@ interface IWitOracleRadonRegistry { /// of the given data request payload. function hashOf(bytes calldata) external view returns (bytes32); + /// Returns the whole Witnet.RadonReducer metadata struct for the given hash. + /// Reverts if unknown. function lookupRadonReducer(bytes32 hash) external view returns (Witnet.RadonReducer memory); /// Returns the whole Witnet.RadonRequest metadata struct for the given RAD hash value. + /// Reverts if unknown. function lookupRadonRequest(bytes32 radHash) external view returns (Witnet.RadonRequest memory); /// Returns the Aggregate reducer that is applied to the data extracted from the data sources @@ -71,7 +74,7 @@ interface IWitOracleRadonRegistry { /// of Radon Requests in the Wit/oracle blockchain. Returns a unique hash that identifies the /// given Radon Reducer in the registry. Reverts if unsupported reducing or filtering methods /// are specified. - function verifyRadonReducer(Witnet.RadonReducer calldata reducer) external returns (bytes16 hash); + function verifyRadonReducer(Witnet.RadonReducer calldata reducer) external returns (bytes32 hash); /// Verifies and registers the specified Radon Request out of the given data sources (i.e. retrievals) /// and the aggregate and tally Radon Reducers. Returns a unique RAD hash that identifies the @@ -86,6 +89,12 @@ interface IWitOracleRadonRegistry { Witnet.RadonReducer calldata tally ) external returns (bytes32 radHash); + function verifyRadonRequest( + bytes32[] calldata retrieveHashes, + bytes32 aggregateReducerHash, + bytes32 tallyReducerHash + ) external returns (bytes32 radHash); + /// Verifies and registers the specified Radon Request out of the given data sources (i.e. retrievals), /// data sources parameters (if required), and the aggregate and tally Radon Reducers. Returns a unique /// RAD hash that identifies the verified Radon Request. @@ -101,6 +110,13 @@ interface IWitOracleRadonRegistry { Witnet.RadonReducer calldata tally ) external returns (bytes32 radHash); + function verifyRadonRequest( + bytes32[] calldata retrieveHashes, + string[][] calldata retrieveArgsValues, + bytes32 aggregateReducerHash, + bytes32 tallyReducerHash + ) external returns (bytes32 radHash); + /// Verifies and registers the specified Radon Retrieval (i.e. public data source) into this registry contract. /// Returns a unique retrieval hash that identifies the verified Radon Retrieval. /// All parameters but the retrieval method are parameterizable by using embedded wildcard \x\ substrings (with x='0'..'9'). @@ -115,4 +131,12 @@ interface IWitOracleRadonRegistry { string[2][] calldata requestHeaders, bytes calldata requestRadonScript ) external returns (bytes32 hash); + + /// Verifies a new Radon Retrieval by specifying the value to the highest indexed parameter of an already existing one. + /// Returns the unique hash that identifies the resulting Radon Retrieval. + /// Reverts if an unverified retrieval hash is passed. + function verifyRadonRetrieval( + bytes32 retrieveHash, + string calldata lastArgValue + ) external returns (bytes32 hash); } diff --git a/contracts/interfaces/IWitOracleRadonRegistryEvents.sol b/contracts/interfaces/IWitOracleRadonRegistryEvents.sol index eceba152..ba8b79ca 100644 --- a/contracts/interfaces/IWitOracleRadonRegistryEvents.sol +++ b/contracts/interfaces/IWitOracleRadonRegistryEvents.sol @@ -8,7 +8,7 @@ interface IWitOracleRadonRegistryEvents { /// Emitted every time a new Radon Reducer gets successfully verified and /// stored into the WitOracleRadonRegistry. - event NewRadonReducer(bytes16 hash); + event NewRadonReducer(bytes32 hash); /// Emitted every time a new Radon Retrieval gets successfully verified and /// stored into the WitOracleRadonRegistry. diff --git a/contracts/interfaces/IWitOracleRequestFactory.sol b/contracts/interfaces/IWitOracleRequestFactory.sol index 19c30ca8..39858b56 100644 --- a/contracts/interfaces/IWitOracleRequestFactory.sol +++ b/contracts/interfaces/IWitOracleRequestFactory.sol @@ -2,11 +2,11 @@ pragma solidity >=0.7.0 <0.9.0; -import "../libs/Witnet.sol"; +import "./IWitOracleRequestTemplate.sol"; interface IWitOracleRequestFactory { - /// @notice Builds a Witnet Request instance that will provide the bytecode + /// @notice Builds a WitOracleRequest instance that will provide the bytecode /// @notice and RAD hash of some Witnet-compliant data request, provably /// @notice made out of some previously verified Witnet Radon Retrievals /// @notice (i.e. data sources), aggregate and tally Witnet Radon Reducers. @@ -22,9 +22,32 @@ interface IWitOracleRequestFactory { bytes32[] calldata retrieveHashes, Witnet.RadonReducer calldata aggregate, Witnet.RadonReducer calldata tally - ) external returns (address request); + ) external returns (IWitOracleRequest); - /// @notice Builds a Witnet Request Template instance made out of one or more + /// @notice Builds a modal WitOracleRequest instance out of one single parameterized + /// @notice Witnet Radon Retrieval. Modal data requests apply the same data retrieval script + /// @notice to multiple data providers that are expected to produce exactly the same result. + /// @notice Moreover, modal data requests apply a Mode reducing function at both the + /// @notice Aggregate and Tally stages everytime they get eventually solved on the Wit/Oracle + /// @notice blockhain. You can optionally specify a list of filters to be applied at the Tally + /// @notice resolution stage (i.e. witnessing nodes on the Wit/Oracle blockchain reporting results + /// @notice that get ultimately filtered out on the Tally stage would get slashed by losing collateral). + /// @dev Reverts if: + /// @dev - unverified base Radon Retrieval is passed; + /// @dev - the specified base Radon Retrieval is not parameterized; + /// @dev - 2nd dimension's rank of the `requestArgs` array doesn't match the number of required parameters + /// @dev required by the given Radon Retrieval; + /// @dev - unsupported Radon Filters are passed. + /// @param baseRetrieveHash Hash of the parameterized Radon Retrieval upon which the new data request will be built. + /// @param requestArgs Parameters to be applied to each repetition of the given retrieval. + /// @param tallySlashingFilters Optional array of slashing filters to will be applied at the Tally stage. + function buildWitOracleRequestModal( + bytes32 baseRetrieveHash, + string[][] calldata requestArgs, + Witnet.RadonFilter[] calldata tallySlashingFilters + ) external returns (IWitOracleRequest); + + /// @notice Builds a WitOracleRequestTemplate instance made out of one or more /// @notice parameterized Witnet Radon Retrievals (i.e. data sources), aggregate /// @notice and tally Witnet Radon Reducers. /// @dev Reverts if: @@ -39,7 +62,28 @@ interface IWitOracleRequestFactory { bytes32[] calldata retrieveHashes, Witnet.RadonReducer calldata aggregate, Witnet.RadonReducer calldata tally - ) external returns (address template); + ) external returns (IWitOracleRequestTemplate); + + /// @notice Builds a modal WitOracleRequestTemplate instance out of one single parameterized + /// @notice Witnet Radon Retrieval. Modal request templates produce Witnet-compliant data requests + /// @notice that apply the same data retrieval script to multiple data providers that are expected to + /// @notice produce exactly the same result. Moreover, data requests built out of a modal request templates + /// @notice apply a Mode reducing function at both the Aggregate and Tally stages everytime they get + /// @notice eventually solved on the Wit/Oracle blockchain. You can optionally specify a list of filters + /// @notice to be applied at the Tally resolution stage (i.e. witnessing nodes on the Wit/Oracle blockchain + /// @notice reporting results that get ultimately filtred out on the Tally stage would get slashed by losing collateral). + /// @dev Reverts if: + /// @dev - unverified base Radon Retrieval is passed; + /// @dev - the specified base Radon Retrieval is either not parameterized or requires just one single parameter; + /// @dev - unsupported Radon Filters are passed. + /// @param baseRetrieveHash Hash of the parameterized Radon Retrieval upon which the new data request will be built. + /// @param lastArgValues Parameters to be applied to each repetition of the given retrieval. + /// @param tallySlashingFilters Optional array of slashing filters to will be applied at the Tally stage. + function buildWitOracleRequestTemplateModal( + bytes32 baseRetrieveHash, + string[] calldata lastArgValues, + Witnet.RadonFilter[] calldata tallySlashingFilters + ) external returns (IWitOracleRequestTemplate); /// @notice Verifies and registers the specified Witnet Radon Retrieval /// @notice (i.e. public data sources) into the WitOracleRadonRegistry of the diff --git a/contracts/interfaces/IWitOracleRequestTemplate.sol b/contracts/interfaces/IWitOracleRequestTemplate.sol index 1df28201..80dc2ff8 100644 --- a/contracts/interfaces/IWitOracleRequestTemplate.sol +++ b/contracts/interfaces/IWitOracleRequestTemplate.sol @@ -2,18 +2,23 @@ pragma solidity >=0.7.0 <0.9.0; -import "../libs/Witnet.sol"; +import "./IWitOracleRequest.sol"; interface IWitOracleRequestTemplate { - /// Build a WitOracleRequest instance that will provide the bytecode and RAD + /// Builds a WitOracleRequest instance that will provide the bytecode and RAD /// hash of some Witnet-compliant Radon Request, provably made out of the - /// data sources, aggregate and tally Radon Reducers that compose this WitOracleRequestTemplate. - /// Produced addresses are counter-factual to the given values. - /// Reverts if: - /// - the ranks of passed array don't match either the number of this template's - /// data sources, or the number of required parameters by each one of those. - function buildWitOracleRequest (string[][] calldata args) external returns (address); + /// data sources, aggregate and tally Radon Reducers that compose this instance. + /// Reverts if the ranks of passed array don't match either the number of this template's + /// data sources, or the number of required parameters by each one of those. + /// @dev Produced addresses are counter-factual to the template address and the given values. + function buildWitOracleRequest (string[][] calldata args) external returns (IWitOracleRequest); + + /// Builds a WitOracleRequest instance by specifying one single parameter value + /// that will be equally applied to all the template's data sources. + /// Reverts if any of the underlying data sources requires more than just one parameter. + /// @dev Produced addresses are counter-factual to the template address and the given value. + function buildWitOracleRequest (string calldata singleArgValue) external returns (IWitOracleRequest); /// Returns an array of integers telling the number of parameters required /// by every single data source (i.e. Radon Retrievals) that compose this @@ -42,10 +47,6 @@ interface IWitOracleRequestTemplate { /// any WitOracleRequest that gets built out of this WitOracleRequestTemplate. function getResultDataType() external view returns (Witnet.RadonDataTypes); - /// If built out of an upgradable factory, returns the SemVer tag of the - /// factory implementation at the time when this WitOracleRequestTemplate got built. - function version() external view returns (string memory); - /// Verifies into the bounded WitOracle's registry the actual bytecode /// and RAD hash of the Witnet-compliant Radon Request that gets provably /// made out of the data sources, aggregate and tally Radon Reducers that @@ -57,7 +58,23 @@ interface IWitOracleRequestTemplate { /// template's data sources, or the number of required parameters by /// each one of those. /// @dev This method requires less gas than buildWitOracleRequest(string[][]), and - /// it's usually preferred when parameterized data requests made out of this - /// template are intended to be used just once in lifetime. + /// @dev it's usually preferred when data requests built out of this template + /// @dev are intended to be used just once in lifetime. function verifyRadonRequest(string[][] calldata args) external returns (bytes32); + + /// Verifies into the bounded WitOracle's registry the actual bytecode + /// and RAD hash of the Witnet-compliant Radon Request that gets provably + /// made out as a result of applying the given parameter value to the underlying + /// data sources, aggregate and tally reducers that compose this template. + /// While no actual WitOracleRequest instance gets constructed, the returned value + /// will be accepted as a valid RAD hash on the bounded WitOracle contract from now on. + /// Reverts if any of the underlying data sources requires more than just one parameter. + /// @dev This method requires less gas than buildWitOracleRequest(string), and + /// @dev it's usually preferred when data requests built out of this template + /// @dev are intended to be used just once in lifetime. + function verifyRadonRequest(string calldata singleArgValue) external returns (bytes32); + + /// If built out of an upgradable factory, returns the SemVer tag of the + /// factory implementation at the time when this WitOracleRequestTemplate got built. + function version() external view returns (string memory); } diff --git a/contracts/libs/WitOracleRadonEncodingLib.sol b/contracts/libs/WitOracleRadonEncodingLib.sol index 0be91cda..4329d823 100644 --- a/contracts/libs/WitOracleRadonEncodingLib.sol +++ b/contracts/libs/WitOracleRadonEncodingLib.sol @@ -286,6 +286,26 @@ library WitOracleRadonEncodingLib { ); } + function replaceCborStringsFromBytes( + bytes memory data, + uint8 argIndex, + string memory argValue + ) + public pure + returns (bytes memory) + { + WitnetCBOR.CBOR memory cbor = WitnetCBOR.fromBytes(data); + while (!cbor.eof()) { + if (cbor.majorType == WitnetCBOR.MAJOR_TYPE_STRING) { + _replaceCborWildcard(cbor, argIndex, argValue); + cbor = cbor.settle(); + } else { + cbor = cbor.skip().settle(); + } + } + return cbor.buffer.data; + } + function replaceCborStringsFromBytes( bytes memory data, string[] memory args @@ -305,15 +325,32 @@ library WitOracleRadonEncodingLib { return cbor.buffer.data; } + function replaceWildcards(Witnet.RadonRetrieval memory self, uint8 argIndex, string memory argValue) + public pure + returns (Witnet.RadonRetrieval memory) + { + self.url = WitnetBuffer.replace(self.url, argIndex, argValue); + self.body = WitnetBuffer.replace(self.body, argIndex, argValue); + self.radonScript = replaceCborStringsFromBytes(self.radonScript, argIndex, argValue); + for (uint _ix = 0 ; _ix < self.headers.length; _ix ++) { + self.headers[_ix][0] = WitnetBuffer.replace(self.headers[_ix][0], argIndex, argValue); + self.headers[_ix][1] = WitnetBuffer.replace(self.headers[_ix][1], argIndex, argValue); + } + return self; + } + function replaceWildcards(Witnet.RadonRetrieval memory self, string[] memory args) public pure + returns (Witnet.RadonRetrieval memory) { self.url = WitnetBuffer.replace(self.url, args); self.body = WitnetBuffer.replace(self.body, args); self.radonScript = replaceCborStringsFromBytes(self.radonScript, args); for (uint _ix = 0 ; _ix < self.headers.length; _ix ++) { + self.headers[_ix][0] = WitnetBuffer.replace(self.headers[_ix][0], args); self.headers[_ix][1] = WitnetBuffer.replace(self.headers[_ix][1], args); } + return self; } function validate( @@ -472,6 +509,27 @@ library WitOracleRadonEncodingLib { /// =============================================================================================================== /// --- WitOracleRadonEncodingLib private methods --------------------------------------------------------------------------------- + function _replaceCborWildcard( + WitnetCBOR.CBOR memory self, + uint8 argIndex, + string memory argValue + ) private pure + { + uint _rewind = self.len; + uint _start = self.buffer.cursor; + bytes memory _peeks = bytes(self.readString()); + (bytes memory _pokes, uint _replacements) = WitnetBuffer.replace(_peeks, argIndex, argValue); + if (_replacements > 0) { + bytes memory _encodedPokes = encode(string(_pokes)); + self.buffer.cursor = _start - _rewind; + self.buffer.mutate( + _peeks.length + _rewind, + _encodedPokes + ); + self.buffer.cursor += _encodedPokes.length; + } + } + function _replaceCborWildcards( WitnetCBOR.CBOR memory self, string[] memory args @@ -491,7 +549,6 @@ library WitOracleRadonEncodingLib { self.buffer.cursor += _encodedPokes.length; } } - function _verifyRadonScriptResultDataType(WitnetCBOR.CBOR memory self, bool flip) private pure diff --git a/contracts/libs/WitnetBuffer.sol b/contracts/libs/WitnetBuffer.sol index f68ec996..640c97ae 100644 --- a/contracts/libs/WitnetBuffer.sol +++ b/contracts/libs/WitnetBuffer.sol @@ -523,7 +523,7 @@ library WitnetBuffer { } } - /// @notice Replace bytecode indexed wildcards by correspondent substrings. + /// @notice Replace indexed bytes-wildcards by correspondent substrings. /// @dev Wildcard format: "\#\", with # in ["0".."9"]. /// @param input Bytes array containing strings. /// @param args Array of substring values for replacing indexed wildcards. @@ -622,7 +622,102 @@ library WitnetBuffer { } } - /// @notice Replace string indexed wildcards by correspondent substrings. + /// @notice Replace indexed bytes-wildcard by given substring. + /// @dev Wildcard format: "\#\", with # in ["0".."9"]. + /// @param input Bytes array containing strings. + /// @param argIndex Index of the wildcard to be replaced. + /// @param argValue Replacing substring to be used. + /// @return output Resulting bytes array after replacing all wildcards. + /// @return hits Total number of replaced wildcards. + function replace(bytes memory input, uint8 argIndex, string memory argValue) + internal pure + returns (bytes memory output, uint hits) + { + uint ix = 0; uint lix = 0; + uint inputLength; + uint inputPointer; + uint outputLength; + uint outputPointer; + uint argValueLength; + uint argValuePointer; + + if (input.length < 3) { + return (input, 0); + } + + assembly { + // set starting input pointer + inputPointer := add(input, 32) + // get safe output location + output := mload(0x40) + // set starting output pointer + outputPointer := add(output, 32) + // set pointer to arg value substring + argValuePointer := add(argValue, 32) + // set arg value substring length + argValueLength := mload(argValue) + } + + unchecked { + uint length = input.length - 2; + for (; ix < length; ) { + if ( + input[ix] == bytes1("\\") + && input[ix + 2] == bytes1("\\") + && input[ix + 1] >= bytes1("0") + && input[ix + 1] <= bytes1("9") + && uint8(input[ix + 1]) - uint8(bytes1("0")) == argIndex + ) { + inputLength = (ix - lix); + if (ix > lix) { + memcpy( + outputPointer, + inputPointer, + inputLength + ); + inputPointer += inputLength + 3; + outputPointer += inputLength; + } else { + inputPointer += 3; + } + memcpy( + outputPointer, + argValuePointer, + argValueLength + ); + outputLength += inputLength + argValueLength; + outputPointer += argValueLength; + ix += 3; + lix = ix; + hits ++; + } else { + ix ++; + } + } + ix = input.length; + } + if (outputLength > 0) { + if (ix > lix ) { + memcpy( + outputPointer, + inputPointer, + ix - lix + ); + outputLength += (ix - lix); + } + assembly { + // set final output length + mstore(output, outputLength) + // protect output bytes + mstore(0x40, add(mload(0x40), add(outputLength, 32))) + } + } + else { + return (input, 0); + } + } + + /// @notice Replace indexed string wildcards by correspondent substrings. /// @dev Wildcard format: "\#\", with # in ["0".."9"]. /// @param input String potentially containing wildcards. /// @param args Array of substring values for replacing indexed wildcards. @@ -635,6 +730,20 @@ library WitnetBuffer { return string(_outputBytes); } + /// @notice Replace last indexed wildcard by given substring. + /// @dev Wildcard format: "\#\", with # in ["0".."9"]. + /// @param input String potentially containing wildcards. + /// @param argIndex Index of the wildcard to be replaced. + /// @param argValue Replacing string to be used. + /// @return output Resulting string after replacing all wildcards. + function replace(string memory input, uint8 argIndex, string memory argValue) + internal pure + returns (string memory) + { + (bytes memory _outputBytes, ) = replace(bytes(input), argIndex, argValue); + return string(_outputBytes); + } + /// @notice Move the inner cursor of the buffer to a relative or absolute position. /// @param buffer An instance of `Buffer`. /// @param offset How many bytes to move the cursor forward. From 2d6aed06e4977dc50e3106b4cc732ecd797fea55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 4 Oct 2024 11:52:36 +0200 Subject: [PATCH 5/8] test(libs): test WitnetBuffer.replace(string,uint8,string) --- test/TestWitnetBuffer.sol | 55 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/test/TestWitnetBuffer.sol b/test/TestWitnetBuffer.sol index aa0dc5cc..aa011013 100644 --- a/test/TestWitnetBuffer.sol +++ b/test/TestWitnetBuffer.sol @@ -153,6 +153,61 @@ contract TestWitnetBuffer { ); } + function testReplaceArgsByIndexFirst() external { + string memory input = "Test: \\2\\ \\0\\ \\3\\!"; + string memory phrase = WitnetBuffer.replace(input, 0, "decentralized"); + emit Log(phrase, bytes(phrase).length); + Assert.equal( + keccak256(bytes(phrase)), + keccak256(bytes("Test: \\2\\ decentralized \\3\\!")), + "String replacement not good :/" + ); + } + + function testReplaceArgsByIndexMiddle() external { + string memory input = "Test: \\2\\ \\0\\ \\3\\!"; + string memory phrase = WitnetBuffer.replace(input, 2, "Hello"); + emit Log(phrase, bytes(phrase).length); + Assert.equal( + keccak256(bytes(phrase)), + keccak256(bytes("Test: Hello \\0\\ \\3\\!")), + "String replacement not good :/" + ); + } + + function testReplaceArgsByIndexLast() external { + string memory input = "Test: \\2\\ \\0\\ \\3\\!"; + string memory phrase = WitnetBuffer.replace(input, 3, "world"); + emit Log(phrase, bytes(phrase).length); + Assert.equal( + keccak256(bytes(phrase)), + keccak256(bytes("Test: \\2\\ \\0\\ world!")), + "String replacement not good :/" + ); + } + + function testReplaceArgsByIndexInexistent() external { + string memory input = "Test: \\2\\ \\0\\ \\3\\!"; + string memory phrase = WitnetBuffer.replace(input, 1, "Bye"); + emit Log(phrase, bytes(phrase).length); + Assert.equal( + keccak256(bytes(phrase)), + keccak256(bytes("Test: \\2\\ \\0\\ \\3\\!")), + "String replacement not good :/" + ); + } + + function testReplaceArgsByIndexMultipleHits() external { + string memory input = "Test: \\2\\ \\2\\ \\2\\ \\0\\ \\3\\!"; + string memory phrase = WitnetBuffer.replace(input, 2, "Hello"); + emit Log(phrase, bytes(phrase).length); + Assert.equal( + keccak256(bytes(phrase)), + keccak256(bytes("Test: Hello Hello Hello \\0\\ \\3\\!")), + "String replacement not good :/" + ); + } + function testRead31bytes() external { bytes memory data = hex"58207eadcf3ba9a9a860b4421ee18caa6dca4738fef266aa7b3668a2ff97304cfcab"; WitnetBuffer.Buffer memory buf = WitnetBuffer.Buffer(data, 1); From 9248f0bad3c9b71debf739da2ab9bf103859d655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 4 Oct 2024 11:53:35 +0200 Subject: [PATCH 6/8] test(libs): WitOracleRadonEncodingLib.replaceCborStringFromBytes(bytes,uint8,string) --- test/TestWitOracleRadonEncodingLib.sol | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/test/TestWitOracleRadonEncodingLib.sol b/test/TestWitOracleRadonEncodingLib.sol index 6741fb72..8e109e97 100644 --- a/test/TestWitOracleRadonEncodingLib.sol +++ b/test/TestWitOracleRadonEncodingLib.sol @@ -131,6 +131,28 @@ contract TestWitOracleRadonEncodingLib { ); } + function testReplaceCborStringsFromBytesByArgIndex() external { + bytes memory radon = hex"861877821866646461746182186664706F6F6C821864635C305C8218571A000F4240185B"; + bytes memory newradon = WitOracleRadonEncodingLib.replaceCborStringsFromBytes(radon, 0, "token1Price"); + // emit Log(newradon, newradon.length); + Assert.equal( + keccak256(newradon), + keccak256(hex'861877821866646461746182186664706F6F6C8218646B746F6B656E3150726963658218571A000F4240185B'), + "not good :/" + ); + } + + function testReplaceCborStringsFromBytesByArgIndexInexistent() external { + bytes memory radon = hex"861877821866646461746182186664706F6F6C821864635C305C8218571A000F4240185B"; + bytes memory newradon = WitOracleRadonEncodingLib.replaceCborStringsFromBytes(radon, 1, "token1Price"); + // emit Log(newradon, newradon.length); + Assert.equal( + keccak256(newradon), + keccak256(hex'861877821866646461746182186664706F6F6C821864635C305C8218571A000F4240185B'), + "not good :/" + ); + } + function testVerifyRadonScriptOk1() external { Assert.equal( uint(WitOracleRadonEncodingLib.verifyRadonScriptResultDataType(hex"861877821866646461746182186664706f6f6c8218646b746f6b656e3150726963658218571a000f4240185b")), @@ -170,4 +192,5 @@ contract TestWitOracleRadonEncodingLib { "unexpected result data type" ); } -} \ No newline at end of file + +} From d2a0af9c9eb029e9b6f94007c40ee7ab026427aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 4 Oct 2024 11:54:06 +0200 Subject: [PATCH 7/8] test(registry): WitOracleRadonRegistry.verifyRadonRetriteval(bytes32,string) --- test/witOracleRadonRegistry.spec.js | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/test/witOracleRadonRegistry.spec.js b/test/witOracleRadonRegistry.spec.js index cb4fc8f9..4b59e74e 100644 --- a/test/witOracleRadonRegistry.spec.js +++ b/test/witOracleRadonRegistry.spec.js @@ -96,8 +96,8 @@ contract("WitOracleRadonRegistry", (accounts) => { let rngSourceHash let binanceTickerHash + let binanceUsdTickerHash let uniswapToken1PriceHash - let heavyRetrievalHash let rngHash @@ -130,7 +130,7 @@ contract("WitOracleRadonRegistry", (accounts) => { assert.equal(tx.logs.length, 0, "some unexpected event was emitted") }) it("generates proper hash upon offchain verification of already existing randmoness source", async () => { - const hash = await radonRegistry.verifyRadonRetrieval.call( + const hash = await radonRegistry.methods["verifyRadonRetrieval(uint8,string,string,string[2][],bytes)"].call( 2, // requestMethod "", // requestURL "", // requestBody @@ -185,6 +185,27 @@ contract("WitOracleRadonRegistry", (accounts) => { "NewRadonRetrieval" ) }) + it("fork existing retrieval by settling one out of two parameters", async () => { + const tx = await radonRegistry.verifyRadonRetrieval( + binanceTickerHash, + "USD" + ) + assert.equal(tx.logs.length, 1) + expectEvent( + tx.receipt, + "NewRadonRetrieval" + ) + binanceUsdTickerHash = tx.logs[0].args.hash + }) + it("metadata of forked retrieval gets stored as expected", async () => { + const ds = await radonRegistry.lookupRadonRetrieval.call(binanceUsdTickerHash) + assert.equal(ds.method, 1) // HTTP-GET + assert.equal(ds.dataType, 4) // Integer + assert.equal(ds.url, "https://api.binance.us/api/v3/ticker/price?symbol=\\0\\USD") + assert.equal(ds.body, "") + assert(ds.headers.length === 0) + assert.equal(ds.radonScript, "0x841877821864696c61737450726963658218571a000f4240185b") + }) }) context("Witnet.RadonRetrievalMethods.HttpPost", async () => { it( From ec01c19acd4fe18c1dd8d6f5cfe117337f7523fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 9 Oct 2024 12:29:55 +0200 Subject: [PATCH 8/8] chore: attend pr review comments --- contracts/core/trustless/WitOracleRadonRegistryDefault.sol | 4 ++-- contracts/interfaces/IWitOracleRequestFactory.sol | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/core/trustless/WitOracleRadonRegistryDefault.sol b/contracts/core/trustless/WitOracleRadonRegistryDefault.sol index b5465911..c47f2a5e 100644 --- a/contracts/core/trustless/WitOracleRadonRegistryDefault.sol +++ b/contracts/core/trustless/WitOracleRadonRegistryDefault.sol @@ -598,8 +598,8 @@ contract WitOracleRadonRegistryDefault // Build radon retrieval bytecode: bytes memory _bytecode = _retrievals.encode( _retrieveArgsValues, - _aggregateReducer.encode(), - _tallyReducer.encode(), + _aggregateReducer.encode(), + _tallyReducer.encode(), 0 ); _require( diff --git a/contracts/interfaces/IWitOracleRequestFactory.sol b/contracts/interfaces/IWitOracleRequestFactory.sol index 39858b56..bc4a9d74 100644 --- a/contracts/interfaces/IWitOracleRequestFactory.sol +++ b/contracts/interfaces/IWitOracleRequestFactory.sol @@ -68,10 +68,10 @@ interface IWitOracleRequestFactory { /// @notice Witnet Radon Retrieval. Modal request templates produce Witnet-compliant data requests /// @notice that apply the same data retrieval script to multiple data providers that are expected to /// @notice produce exactly the same result. Moreover, data requests built out of a modal request templates - /// @notice apply a Mode reducing function at both the Aggregate and Tally stages everytime they get + /// @notice apply a Mode reducing function at both the Aggregate and Tally stages every time they get /// @notice eventually solved on the Wit/Oracle blockchain. You can optionally specify a list of filters /// @notice to be applied at the Tally resolution stage (i.e. witnessing nodes on the Wit/Oracle blockchain - /// @notice reporting results that get ultimately filtred out on the Tally stage would get slashed by losing collateral). + /// @notice reporting results that get ultimately filtered out on the Tally stage would get slashed by losing collateral). /// @dev Reverts if: /// @dev - unverified base Radon Retrieval is passed; /// @dev - the specified base Radon Retrieval is either not parameterized or requires just one single parameter;