From 92fd32b7ff4a58e344e11a6342d7d75d1e60a79a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Tue, 8 Nov 2022 10:02:41 +0100 Subject: [PATCH 001/119] chore: refactor V1 contracts --- README.md | 6 +- contracts/WitnetRequestBoard.sol | 6 +- contracts/data/WitnetBoardData.sol | 6 - .../WitnetRequestBoardUpgradableBase.sol | 61 ------- contracts/impls/WitnetUpgradableBase.sol | 108 ++++++++++++ .../WitnetRequestBoardTrustableBase.sol | 61 ++++--- .../WitnetRequestBoardTrustableBoba.sol | 2 +- .../WitnetRequestBoardTrustableDefault.sol | 2 +- .../WitnetRequestBoardTrustableOvm2.sol | 0 .../WitnetRequestBoardTrustableReef.sol | 0 contracts/interfaces/IWitnetRequestParser.sol | 3 +- contracts/libs/Witnet.sol | 19 +-- contracts/libs/WitnetBuffer.sol | 58 ++++--- .../{WitnetDecoderLib.sol => WitnetCBOR.sol} | 153 +++++++++-------- .../{WitnetParserLib.sol => WitnetLib.sol} | 39 +++-- contracts/patterns/ERC165.sol | 3 + contracts/patterns/Ownable2Step.sol | 3 + contracts/patterns/Payable.sol | 4 +- contracts/patterns/Proxiable.sol | 2 +- contracts/patterns/ReentrancyGuard.sol | 3 + contracts/patterns/Upgradable.sol | 5 +- migrations/scripts/1_deploy_wrb.js | 39 ++--- migrations/witnet.addresses.json | 92 +++++----- migrations/witnet.settings.js | 3 +- package.json | 5 +- scripts/utils.js | 10 ++ test/TestWitnetBuffer.sol | 36 ++-- ...itnetDecoderLib.sol => TestWitnetCBOR.sol} | 158 +++++++++--------- ...tWitnetParserLib.sol => TestWitnetLib.sol} | 96 +++++------ test/helpers/WitnetRequestBoardTestHelper.sol | 2 +- test/using_witnet.test.js | 10 +- test/wrb_proxy.test.js | 6 +- 32 files changed, 532 insertions(+), 469 deletions(-) delete mode 100644 contracts/impls/WitnetRequestBoardUpgradableBase.sol create mode 100644 contracts/impls/WitnetUpgradableBase.sol rename contracts/impls/{ => boards}/trustable/WitnetRequestBoardTrustableBase.sol (94%) rename contracts/impls/{ => boards}/trustable/WitnetRequestBoardTrustableBoba.sol (99%) rename contracts/impls/{ => boards}/trustable/WitnetRequestBoardTrustableDefault.sol (98%) rename contracts/impls/{ => boards}/trustable/WitnetRequestBoardTrustableOvm2.sol (100%) rename contracts/impls/{ => boards}/trustable/WitnetRequestBoardTrustableReef.sol (100%) rename contracts/libs/{WitnetDecoderLib.sol => WitnetCBOR.sol} (58%) rename contracts/libs/{WitnetParserLib.sol => WitnetLib.sol} (90%) create mode 100644 contracts/patterns/ERC165.sol create mode 100644 contracts/patterns/Ownable2Step.sol create mode 100644 contracts/patterns/ReentrancyGuard.sol rename test/{TestWitnetDecoderLib.sol => TestWitnetCBOR.sol} (59%) rename test/{TestWitnetParserLib.sol => TestWitnetLib.sol} (61%) diff --git a/README.md b/README.md index 9f4f4c6ec..a01f08039 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This repository provides several deployable contracts: -- `WitnetParserLib`, helper library useful for parsing Witnet-solved results to previously posted Witnet Data Requests. +- `WitnetLib`, helper library useful for parsing Witnet-solved results to previously posted Witnet Data Requests. - `WitnetProxy`, a delegate-proxy contract that routes Witnet Data Requests to a currently active `WitnetRequestBoard` implementation. - Multiple implementations of the `WitnetRequestBoard` interface (WRB), which declares all required functionality to relay encapsulated [Witnet Data Requests](https://docs.witnet.io/intro/about/architecture#capabilities-of-data-requests/) from an EVM compatible chain to the Witnet mainnet, as well as to relay Witnet-solved results back to Ethereum. @@ -295,9 +295,9 @@ Please, have a look at the [`witnet/truffle-box`](https://github.com/witnet/truf ·······································|···························|·············|·············|·············|··············|·············· | Deployments · · % of limit · │ ···································································|·············|·············|·············|··············|·············· -| WitnetDecoderLib · - · - · 2036059 · 30.3 % · - │ +| WitnetCBOR · - · - · 2036059 · 30.3 % · - │ ···································································|·············|·············|·············|··············|·············· -| WitnetParserLib · - · - · 2627933 · 39.1 % · - │ +| WitnetLib · - · - · 2627933 · 39.1 % · - │ ···································································|·············|·············|·············|··············|·············· | WitnetProxy · - · - · 574794 · 8.6 % · - │ ···································································|·············|·············|·············|··············|·············· diff --git a/contracts/WitnetRequestBoard.sol b/contracts/WitnetRequestBoard.sol index cca27bed0..f5c08f791 100644 --- a/contracts/WitnetRequestBoard.sol +++ b/contracts/WitnetRequestBoard.sol @@ -17,8 +17,4 @@ abstract contract WitnetRequestBoard is IWitnetRequestBoardRequestor, IWitnetRequestBoardView, IWitnetRequestParser -{ - receive() external payable { - revert("WitnetRequestBoard: no transfers accepted"); - } -} +{} \ No newline at end of file diff --git a/contracts/data/WitnetBoardData.sol b/contracts/data/WitnetBoardData.sol index 17b8a15bd..f2208a679 100644 --- a/contracts/data/WitnetBoardData.sol +++ b/contracts/data/WitnetBoardData.sol @@ -39,12 +39,6 @@ abstract contract WitnetBoardData { _; } - /// Asserts caller corresponds to the current owner. - modifier onlyOwner { - require(msg.sender == _state().owner, "WitnetBoardData: only owner"); - _; - } - /// Asserts the give query was actually posted before calling this method. modifier wasPosted(uint256 _queryId) { require(_queryId > 0 && _queryId <= _state().numQueries, "WitnetBoardData: not yet posted"); diff --git a/contracts/impls/WitnetRequestBoardUpgradableBase.sol b/contracts/impls/WitnetRequestBoardUpgradableBase.sol deleted file mode 100644 index fb22b414f..000000000 --- a/contracts/impls/WitnetRequestBoardUpgradableBase.sol +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.7.0 <0.9.0; -pragma experimental ABIEncoderV2; - -/* solhint-disable var-name-mixedcase */ - -// Inherits from: -import "../WitnetRequestBoard.sol"; -import "../patterns/Proxiable.sol"; -import "../patterns/Upgradable.sol"; - -// Eventual deployment dependencies: -import "./WitnetProxy.sol"; - -/// @title Witnet Request Board base contract, with an Upgradable (and Destructible) touch. -/// @author The Witnet Foundation. -abstract contract WitnetRequestBoardUpgradableBase - is - Proxiable, - Upgradable, - WitnetRequestBoard -{ - bytes32 internal immutable _VERSION; - - constructor( - bool _upgradable, - bytes32 _versionTag - ) - Upgradable(_upgradable) - { - _VERSION = _versionTag; - } - - /// @dev Reverts if proxy delegatecalls to unexistent method. - fallback() external payable { - revert("WitnetRequestBoardUpgradableBase: not implemented"); - } - - // ================================================================================================================ - // --- Overrides 'Proxiable' -------------------------------------------------------------------------------------- - - /// @dev Gets immutable "heritage blood line" (ie. genotype) as a Proxiable, and eventually Upgradable, contract. - /// If implemented as an Upgradable touch, upgrading this contract to another one with a different - /// `proxiableUUID()` value should fail. - function proxiableUUID() external pure override returns (bytes32) { - return ( - /* keccak256("io.witnet.proxiable.board") */ - 0x9969c6aff411c5e5f0807500693e8f819ce88529615cfa6cab569b24788a1018 - ); - } - - // ================================================================================================================ - // --- Overrides 'Upgradable' -------------------------------------------------------------------------------------- - - /// Retrieves human-readable version tag of current implementation. - function version() public view override returns (bytes32) { - return _VERSION; - } - -} diff --git a/contracts/impls/WitnetUpgradableBase.sol b/contracts/impls/WitnetUpgradableBase.sol new file mode 100644 index 000000000..d11515345 --- /dev/null +++ b/contracts/impls/WitnetUpgradableBase.sol @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: MIT +// solhint-disable var-name-mixedcase + +pragma solidity >=0.8.0 <0.9.0; + +import "../patterns/ERC165.sol"; +import "../patterns/Ownable2Step.sol"; +import "../patterns/ReentrancyGuard.sol"; +import "../patterns/Upgradable.sol"; + +import "./WitnetProxy.sol"; + +/// @title Witnet Request Board base contract, with an Upgradable (and Destructible) touch. +/// @author The Witnet Foundation. +abstract contract WitnetUpgradableBase + is + ERC165, + Ownable2Step, + Upgradable, + ReentrancyGuard +{ + bytes32 internal immutable _WITNET_UPGRADABLE_VERSION; + + constructor( + bool _upgradable, + bytes32 _versionTag, + string memory _proxiableUUID + ) + Upgradable(_upgradable) + { + _WITNET_UPGRADABLE_VERSION = _versionTag; + proxiableUUID = keccak256(bytes(_proxiableUUID)); + } + + receive() payable external virtual; + + /// @dev Reverts if proxy delegatecalls to unexistent method. + fallback() payable external { + revert("WitnetUpgradableBase: not implemented"); + } + + + // ================================================================================================================ + // --- Overrides IERC165 interface -------------------------------------------------------------------------------- + + /// @dev See {IERC165-supportsInterface}. + function supportsInterface(bytes4 _interfaceId) + public view + virtual override + returns (bool) + { + return _interfaceId == type(Ownable2Step).interfaceId + || _interfaceId == type(Upgradable).interfaceId + || super.supportsInterface(_interfaceId); + } + + // ================================================================================================================ + // --- Overrides 'Proxiable' -------------------------------------------------------------------------------------- + + /// @dev Gets immutable "heritage blood line" (ie. genotype) as a Proxiable, and eventually Upgradable, contract. + /// If implemented as an Upgradable touch, upgrading this contract to another one with a different + /// `proxiableUUID()` value should fail. + bytes32 public immutable override proxiableUUID; + + + // ================================================================================================================ + // --- Overrides 'Upgradable' -------------------------------------------------------------------------------------- + + /// Retrieves human-readable version tag of current implementation. + function version() public view override returns (string memory) { + return _toString(_WITNET_UPGRADABLE_VERSION); + } + + + // ================================================================================================================ + // --- Internal methods ------------------------------------------------------------------------------------------- + + /// Converts bytes32 into string. + function _toString(bytes32 _bytes32) + internal pure + returns (string memory) + { + bytes memory _bytes = new bytes(_toStringLength(_bytes32)); + for (uint _i = 0; _i < _bytes.length;) { + _bytes[_i] = _bytes32[_i]; + unchecked { + _i ++; + } + } + return string(_bytes); + } + + // Calculate length of string-equivalent to given bytes32. + function _toStringLength(bytes32 _bytes32) + internal pure + returns (uint _length) + { + for (; _length < 32; ) { + if (_bytes32[_length] == 0) { + break; + } + unchecked { + _length ++; + } + } + } + +} \ No newline at end of file diff --git a/contracts/impls/trustable/WitnetRequestBoardTrustableBase.sol b/contracts/impls/boards/trustable/WitnetRequestBoardTrustableBase.sol similarity index 94% rename from contracts/impls/trustable/WitnetRequestBoardTrustableBase.sol rename to contracts/impls/boards/trustable/WitnetRequestBoardTrustableBase.sol index fb7dcbf68..4692f04ee 100644 --- a/contracts/impls/trustable/WitnetRequestBoardTrustableBase.sol +++ b/contracts/impls/boards/trustable/WitnetRequestBoardTrustableBase.sol @@ -3,12 +3,12 @@ pragma solidity >=0.7.0 <0.9.0; pragma experimental ABIEncoderV2; -import "../WitnetRequestBoardUpgradableBase.sol"; -import "../../data/WitnetBoardDataACLs.sol"; -import "../../interfaces/IWitnetRequestBoardAdmin.sol"; -import "../../interfaces/IWitnetRequestBoardAdminACLs.sol"; -import "../../libs/WitnetParserLib.sol"; -import "../../patterns/Payable.sol"; +import "../../WitnetUpgradableBase.sol"; +import "../../../WitnetRequestBoard.sol"; +import "../../../data/WitnetBoardDataACLs.sol"; +import "../../../interfaces/IWitnetRequestBoardAdminACLs.sol"; +import "../../../libs/WitnetLib.sol"; +import "../../../patterns/Payable.sol"; /// @title Witnet Request Board "trustable" base implementation contract. /// @notice Contract to bridge requests to Witnet Decentralized Oracle Network. @@ -17,20 +17,43 @@ import "../../patterns/Payable.sol"; /// @author The Witnet Foundation abstract contract WitnetRequestBoardTrustableBase is - Payable, - IWitnetRequestBoardAdmin, - IWitnetRequestBoardAdminACLs, + WitnetUpgradableBase, + WitnetRequestBoard, WitnetBoardDataACLs, - WitnetRequestBoardUpgradableBase + IWitnetRequestBoardAdminACLs, + Payable { using Witnet for bytes; - using WitnetParserLib for Witnet.Result; + using WitnetLib for Witnet.Result; - constructor(bool _upgradable, bytes32 _versionTag, address _currency) + constructor( + bool _upgradable, + bytes32 _versionTag, + address _currency + ) Payable(_currency) - WitnetRequestBoardUpgradableBase(_upgradable, _versionTag) + WitnetUpgradableBase(_upgradable, _versionTag, "io.witnet.proxiable.board") {} + receive() external payable override { + revert("WitnetRequestBoardTrustableBase: no transfers accepted"); + } + + + // ================================================================================================================ + // --- Overrides IERC165 interface -------------------------------------------------------------------------------- + + /// @dev See {IERC165-supportsInterface}. + function supportsInterface(bytes4 _interfaceId) + public view + virtual override + returns (bool) + { + return _interfaceId == type(WitnetRequestBoard).interfaceId + || _interfaceId == type(IWitnetRequestBoardAdminACLs).interfaceId + || super.supportsInterface(_interfaceId); + } + // ================================================================================================================ // --- Overrides 'Upgradable' ------------------------------------------------------------------------------------- @@ -85,7 +108,7 @@ abstract contract WitnetRequestBoardTrustableBase /// Transfers ownership. function transferOwnership(address _newOwner) - external + public virtual override onlyOwner { @@ -226,7 +249,7 @@ abstract contract WitnetRequestBoardTrustableBase /// - data request result in raw bytes. /// @param _verbose If true, emits a BatchReportError event for every failing report, if any. function reportResultBatch( - BatchResult[] memory _batchResults, + IWitnetRequestBoardReporter.BatchResult[] memory _batchResults, bool _verbose ) external @@ -537,7 +560,7 @@ abstract contract WitnetRequestBoardTrustableBase returns (Witnet.Result memory) { Witnet.Response storage _response = _getResponseData(_queryId); - return WitnetParserLib.resultFromCborBytes(_response.cborBytes); + return WitnetLib.resultFromCborBytes(_response.cborBytes); } /// Retrieves the timestamp in which the result to the referred query was solved by the Witnet DON. @@ -564,18 +587,18 @@ abstract contract WitnetRequestBoardTrustableBase override returns (Witnet.Result memory) { - return WitnetParserLib.resultFromCborBytes(_cborBytes); + return WitnetLib.resultFromCborBytes(_cborBytes); } /// Decode a CBOR value into a Witnet.Result instance. /// @param _cborValue An instance of `Witnet.CBOR`. /// @return A `Witnet.Result` instance. - function resultFromCborValue(Witnet.CBOR memory _cborValue) + function resultFromCborValue(WitnetCBOR.CBOR memory _cborValue) external pure override returns (Witnet.Result memory) { - return WitnetParserLib.resultFromCborValue(_cborValue); + return WitnetLib.resultFromCborValue(_cborValue); } /// Tell if a Witnet.Result is successful. diff --git a/contracts/impls/trustable/WitnetRequestBoardTrustableBoba.sol b/contracts/impls/boards/trustable/WitnetRequestBoardTrustableBoba.sol similarity index 99% rename from contracts/impls/trustable/WitnetRequestBoardTrustableBoba.sol rename to contracts/impls/boards/trustable/WitnetRequestBoardTrustableBoba.sol index da19ba51b..f6ab79959 100644 --- a/contracts/impls/trustable/WitnetRequestBoardTrustableBoba.sol +++ b/contracts/impls/boards/trustable/WitnetRequestBoardTrustableBoba.sol @@ -9,7 +9,7 @@ pragma experimental ABIEncoderV2; import "./WitnetRequestBoardTrustableBase.sol"; // Uses: -import "../../interfaces/IERC20.sol"; +import "../../../interfaces/IERC20.sol"; /// @title Witnet Request Board OVM-compatible (Optimism) "trustable" implementation. /// @notice Contract to bridge requests to Witnet Decentralized Oracle Network. diff --git a/contracts/impls/trustable/WitnetRequestBoardTrustableDefault.sol b/contracts/impls/boards/trustable/WitnetRequestBoardTrustableDefault.sol similarity index 98% rename from contracts/impls/trustable/WitnetRequestBoardTrustableDefault.sol rename to contracts/impls/boards/trustable/WitnetRequestBoardTrustableDefault.sol index 0a66cb200..ee7dea562 100644 --- a/contracts/impls/trustable/WitnetRequestBoardTrustableDefault.sol +++ b/contracts/impls/boards/trustable/WitnetRequestBoardTrustableDefault.sol @@ -6,7 +6,7 @@ pragma solidity >=0.7.0 <0.9.0; pragma experimental ABIEncoderV2; import "./WitnetRequestBoardTrustableBase.sol"; -import "../../patterns/Destructible.sol"; +import "../../../patterns/Destructible.sol"; /// @title Witnet Request Board "trustable" implementation contract. /// @notice Contract to bridge requests to Witnet Decentralized Oracle Network. diff --git a/contracts/impls/trustable/WitnetRequestBoardTrustableOvm2.sol b/contracts/impls/boards/trustable/WitnetRequestBoardTrustableOvm2.sol similarity index 100% rename from contracts/impls/trustable/WitnetRequestBoardTrustableOvm2.sol rename to contracts/impls/boards/trustable/WitnetRequestBoardTrustableOvm2.sol diff --git a/contracts/impls/trustable/WitnetRequestBoardTrustableReef.sol b/contracts/impls/boards/trustable/WitnetRequestBoardTrustableReef.sol similarity index 100% rename from contracts/impls/trustable/WitnetRequestBoardTrustableReef.sol rename to contracts/impls/boards/trustable/WitnetRequestBoardTrustableReef.sol diff --git a/contracts/interfaces/IWitnetRequestParser.sol b/contracts/interfaces/IWitnetRequestParser.sol index 0cef865c9..4de3c88b1 100644 --- a/contracts/interfaces/IWitnetRequestParser.sol +++ b/contracts/interfaces/IWitnetRequestParser.sol @@ -4,6 +4,7 @@ pragma solidity >=0.7.0 <0.9.0; pragma experimental ABIEncoderV2; import "../libs/Witnet.sol"; +import "../libs/WitnetCBOR.sol"; /// @title The Witnet interface for decoding Witnet-provided request to Data Requests. /// This interface exposes functions to check for the success/failure of @@ -20,7 +21,7 @@ interface IWitnetRequestParser { /// Decode a CBOR value into a Witnet.Result instance. /// @param _cborValue An instance of `Witnet.CBOR`. /// @return A `Witnet.Result` instance. - function resultFromCborValue(Witnet.CBOR memory _cborValue) external pure returns (Witnet.Result memory); + function resultFromCborValue(WitnetCBOR.CBOR memory _cborValue) external pure returns (Witnet.Result memory); /// Tell if a Witnet.Result is successful. /// @param _result An instance of Witnet.Result. diff --git a/contracts/libs/Witnet.sol b/contracts/libs/Witnet.sol index f9e934877..0c3b02764 100644 --- a/contracts/libs/Witnet.sol +++ b/contracts/libs/Witnet.sol @@ -3,6 +3,7 @@ pragma solidity >=0.7.0 <0.9.0; pragma experimental ABIEncoderV2; +import "./WitnetCBOR.sol"; import "../interfaces/IWitnetRequest.sol"; library Witnet { @@ -48,23 +49,7 @@ library Witnet { /// Data struct containing the Witnet-provided result to a Data Request. struct Result { bool success; // Flag stating whether the request could get solved successfully, or not. - CBOR value; // Resulting value, in CBOR-serialized bytes. - } - - /// Data struct following the RFC-7049 standard: Concise Binary Object Representation. - struct CBOR { - Buffer buffer; - uint8 initialByte; - uint8 majorType; - uint8 additionalInformation; - uint64 len; - uint64 tag; - } - - /// Iterable bytes buffer. - struct Buffer { - bytes data; - uint32 cursor; + WitnetCBOR.CBOR value; // Resulting value, in CBOR-serialized bytes. } /// Witnet error codes table. diff --git a/contracts/libs/WitnetBuffer.sol b/contracts/libs/WitnetBuffer.sol index 78ee55849..192f8d88f 100644 --- a/contracts/libs/WitnetBuffer.sol +++ b/contracts/libs/WitnetBuffer.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.7.0 <0.9.0; - -import "./Witnet.sol"; +pragma solidity >=0.8.0 <0.9.0; /// @title A convenient wrapper around the `bytes memory` type that exposes a buffer-like interface /// @notice The buffer has an inner cursor that tracks the final offset of every read, i.e. any subsequent read will @@ -12,6 +10,12 @@ import "./Witnet.sol"; /// @author The Witnet Foundation. library WitnetBuffer { + /// Iterable bytes buffer. + struct Buffer { + bytes data; + uint32 cursor; + } + // Ensures we access an existing index in an array modifier notOutOfBounds(uint32 index, uint256 length) { require(index < length, "WitnetBuffer: Tried to read from a consumed Buffer (must rewind it first)"); @@ -19,10 +23,10 @@ library WitnetBuffer { } /// @notice Read and consume a certain amount of bytes from the buffer. - /// @param _buffer An instance of `Witnet.Buffer`. + /// @param _buffer An instance of `Buffer`. /// @param _length How many bytes to read and consume from the buffer. /// @return A `bytes memory` containing the first `_length` bytes from the buffer, counting from the cursor position. - function read(Witnet.Buffer memory _buffer, uint32 _length) internal pure returns (bytes memory) { + function read(Buffer memory _buffer, uint32 _length) internal pure returns (bytes memory) { // Make sure not to read out of the bounds of the original bytes require(_buffer.cursor + _length <= _buffer.data.length, "WitnetBuffer: Not enough bytes in buffer when reading"); @@ -51,21 +55,21 @@ library WitnetBuffer { } /// @notice Read and consume the next byte from the buffer. - /// @param _buffer An instance of `Witnet.Buffer`. + /// @param _buffer An instance of `Buffer`. /// @return The next byte in the buffer counting from the cursor position. - function next(Witnet.Buffer memory _buffer) internal pure notOutOfBounds(_buffer.cursor, _buffer.data.length) returns (bytes1) { + function next(Buffer memory _buffer) internal pure notOutOfBounds(_buffer.cursor, _buffer.data.length) returns (bytes1) { // Return the byte at the position marked by the cursor and advance the cursor all at once return _buffer.data[_buffer.cursor++]; } /// @notice Move the inner cursor of the buffer to a relative or absolute position. - /// @param _buffer An instance of `Witnet.Buffer`. + /// @param _buffer An instance of `Buffer`. /// @param _offset How many bytes to move the cursor forward. /// @param _relative Whether to count `_offset` from the last position of the cursor (`true`) or the beginning of the /// buffer (`true`). /// @return The final position of the cursor (will equal `_offset` if `_relative` is `false`). // solium-disable-next-line security/no-assign-params - function seek(Witnet.Buffer memory _buffer, uint32 _offset, bool _relative) internal pure returns (uint32) { + function seek(Buffer memory _buffer, uint32 _offset, bool _relative) internal pure returns (uint32) { // Deal with relative offsets if (_relative) { require(_offset + _buffer.cursor > _offset, "WitnetBuffer: Integer overflow when seeking"); @@ -79,23 +83,23 @@ library WitnetBuffer { /// @notice Move the inner cursor a number of bytes forward. /// @dev This is a simple wrapper around the relative offset case of `seek()`. - /// @param _buffer An instance of `Witnet.Buffer`. + /// @param _buffer An instance of `Buffer`. /// @param _relativeOffset How many bytes to move the cursor forward. /// @return The final position of the cursor. - function seek(Witnet.Buffer memory _buffer, uint32 _relativeOffset) internal pure returns (uint32) { + function seek(Buffer memory _buffer, uint32 _relativeOffset) internal pure returns (uint32) { return seek(_buffer, _relativeOffset, true); } /// @notice Move the inner cursor back to the first byte in the buffer. - /// @param _buffer An instance of `Witnet.Buffer`. - function rewind(Witnet.Buffer memory _buffer) internal pure { + /// @param _buffer An instance of `Buffer`. + function rewind(Buffer memory _buffer) internal pure { _buffer.cursor = 0; } /// @notice Read and consume the next byte from the buffer as an `uint8`. - /// @param _buffer An instance of `Witnet.Buffer`. + /// @param _buffer An instance of `Buffer`. /// @return The `uint8` value of the next byte in the buffer counting from the cursor position. - function readUint8(Witnet.Buffer memory _buffer) internal pure notOutOfBounds(_buffer.cursor, _buffer.data.length) returns (uint8) { + function readUint8(Buffer memory _buffer) internal pure notOutOfBounds(_buffer.cursor, _buffer.data.length) returns (uint8) { bytes memory bytesValue = _buffer.data; uint32 offset = _buffer.cursor; uint8 value; @@ -108,9 +112,9 @@ library WitnetBuffer { } /// @notice Read and consume the next 2 bytes from the buffer as an `uint16`. - /// @param _buffer An instance of `Witnet.Buffer`. + /// @param _buffer An instance of `Buffer`. /// @return The `uint16` value of the next 2 bytes in the buffer counting from the cursor position. - function readUint16(Witnet.Buffer memory _buffer) internal pure notOutOfBounds(_buffer.cursor + 1, _buffer.data.length) returns (uint16) { + function readUint16(Buffer memory _buffer) internal pure notOutOfBounds(_buffer.cursor + 1, _buffer.data.length) returns (uint16) { bytes memory bytesValue = _buffer.data; uint32 offset = _buffer.cursor; uint16 value; @@ -123,9 +127,9 @@ library WitnetBuffer { } /// @notice Read and consume the next 4 bytes from the buffer as an `uint32`. - /// @param _buffer An instance of `Witnet.Buffer`. + /// @param _buffer An instance of `Buffer`. /// @return The `uint32` value of the next 4 bytes in the buffer counting from the cursor position. - function readUint32(Witnet.Buffer memory _buffer) internal pure notOutOfBounds(_buffer.cursor + 3, _buffer.data.length) returns (uint32) { + function readUint32(Buffer memory _buffer) internal pure notOutOfBounds(_buffer.cursor + 3, _buffer.data.length) returns (uint32) { bytes memory bytesValue = _buffer.data; uint32 offset = _buffer.cursor; uint32 value; @@ -138,9 +142,9 @@ library WitnetBuffer { } /// @notice Read and consume the next 8 bytes from the buffer as an `uint64`. - /// @param _buffer An instance of `Witnet.Buffer`. + /// @param _buffer An instance of `Buffer`. /// @return The `uint64` value of the next 8 bytes in the buffer counting from the cursor position. - function readUint64(Witnet.Buffer memory _buffer) internal pure notOutOfBounds(_buffer.cursor + 7, _buffer.data.length) returns (uint64) { + function readUint64(Buffer memory _buffer) internal pure notOutOfBounds(_buffer.cursor + 7, _buffer.data.length) returns (uint64) { bytes memory bytesValue = _buffer.data; uint32 offset = _buffer.cursor; uint64 value; @@ -153,9 +157,9 @@ library WitnetBuffer { } /// @notice Read and consume the next 16 bytes from the buffer as an `uint128`. - /// @param _buffer An instance of `Witnet.Buffer`. + /// @param _buffer An instance of `Buffer`. /// @return The `uint128` value of the next 16 bytes in the buffer counting from the cursor position. - function readUint128(Witnet.Buffer memory _buffer) internal pure notOutOfBounds(_buffer.cursor + 15, _buffer.data.length) returns (uint128) { + function readUint128(Buffer memory _buffer) internal pure notOutOfBounds(_buffer.cursor + 15, _buffer.data.length) returns (uint128) { bytes memory bytesValue = _buffer.data; uint32 offset = _buffer.cursor; uint128 value; @@ -169,8 +173,8 @@ library WitnetBuffer { /// @notice Read and consume the next 32 bytes from the buffer as an `uint256`. /// @return The `uint256` value of the next 32 bytes in the buffer counting from the cursor position. - /// @param _buffer An instance of `Witnet.Buffer`. - function readUint256(Witnet.Buffer memory _buffer) internal pure notOutOfBounds(_buffer.cursor + 31, _buffer.data.length) returns (uint256) { + /// @param _buffer An instance of `Buffer`. + function readUint256(Buffer memory _buffer) internal pure notOutOfBounds(_buffer.cursor + 31, _buffer.data.length) returns (uint256) { bytes memory bytesValue = _buffer.data; uint32 offset = _buffer.cursor; uint256 value; @@ -188,9 +192,9 @@ library WitnetBuffer { /// by 5 decimal orders so as to get a fixed precision of 5 decimal positions, which should be OK for most `float16` /// use cases. In other words, the integer output of this method is 10,000 times the actual value. The input bytes are /// expected to follow the 16-bit base-2 format (a.k.a. `binary16`) in the IEEE 754-2008 standard. - /// @param _buffer An instance of `Witnet.Buffer`. + /// @param _buffer An instance of `Buffer`. /// @return The `uint32` value of the next 4 bytes in the buffer counting from the cursor position. - function readFloat16(Witnet.Buffer memory _buffer) internal pure returns (int32) { + function readFloat16(Buffer memory _buffer) internal pure returns (int32) { uint32 bytesValue = readUint16(_buffer); // Get bit at position 0 uint32 sign = bytesValue & 0x8000; diff --git a/contracts/libs/WitnetDecoderLib.sol b/contracts/libs/WitnetCBOR.sol similarity index 58% rename from contracts/libs/WitnetDecoderLib.sol rename to contracts/libs/WitnetCBOR.sol index ad89ef6a0..8492cb328 100644 --- a/contracts/libs/WitnetDecoderLib.sol +++ b/contracts/libs/WitnetCBOR.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.7.0 <0.9.0; -pragma experimental ABIEncoderV2; +pragma solidity >=0.8.0 <0.9.0; import "./WitnetBuffer.sol"; @@ -16,32 +15,42 @@ import "./WitnetBuffer.sol"; /// TODO: add support for Float32 (majorType = 7, additionalInformation = 26) /// TODO: add support for Float64 (majorType = 7, additionalInformation = 27) -library WitnetDecoderLib { +library WitnetCBOR { - using WitnetBuffer for Witnet.Buffer; + using WitnetBuffer for WitnetBuffer.Buffer; + + /// Data struct following the RFC-7049 standard: Concise Binary Object Representation. + struct CBOR { + WitnetBuffer.Buffer buffer; + uint8 initialByte; + uint8 majorType; + uint8 additionalInformation; + uint64 len; + uint64 tag; + } uint32 constant internal _UINT32_MAX = type(uint32).max; uint64 constant internal _UINT64_MAX = type(uint64).max; - /// @notice Decode a `Witnet.CBOR` structure into a native `bool` value. - /// @param _cborValue An instance of `Witnet.CBOR`. + /// @notice Decode a `CBOR` structure into a native `bool` value. + /// @param _cborValue An instance of `CBOR`. /// @return The value represented by the input, as a `bool` value. - function decodeBool(Witnet.CBOR memory _cborValue) public pure returns(bool) { + function decodeBool(CBOR memory _cborValue) internal pure returns(bool) { _cborValue.len = readLength(_cborValue.buffer, _cborValue.additionalInformation); - require(_cborValue.majorType == 7, "WitnetDecoderLib: Tried to read a `bool` value from a `Witnet.CBOR` with majorType != 7"); + require(_cborValue.majorType == 7, "WitnetCBOR: Tried to read a `bool` value from a `CBOR` with majorType != 7"); if (_cborValue.len == 20) { return false; } else if (_cborValue.len == 21) { return true; } else { - revert("WitnetDecoderLib: Tried to read `bool` from a `Witnet.CBOR` with len different than 20 or 21"); + revert("WitnetCBOR: Tried to read `bool` from a `CBOR` with len different than 20 or 21"); } } - /// @notice Decode a `Witnet.CBOR` structure into a native `bytes` value. - /// @param _cborValue An instance of `Witnet.CBOR`. + /// @notice Decode a `CBOR` structure into a native `bytes` value. + /// @param _cborValue An instance of `CBOR`. /// @return The value represented by the input, as a `bytes` value. - function decodeBytes(Witnet.CBOR memory _cborValue) public pure returns(bytes memory) { + function decodeBytes(CBOR memory _cborValue) internal pure returns(bytes memory) { _cborValue.len = readLength(_cborValue.buffer, _cborValue.additionalInformation); if (_cborValue.len == _UINT32_MAX) { bytes memory bytesData; @@ -61,10 +70,10 @@ library WitnetDecoderLib { } } - /// @notice Decode a `Witnet.CBOR` structure into a native `bytes32` value. - /// @param _cborValue An instance of `Witnet.CBOR`. + /// @notice Decode a `CBOR` structure into a native `bytes32` value. + /// @param _cborValue An instance of `CBOR`. /// @return _bytes32 The value represented by the input, as a `bytes32` value. - function decodeBytes32(Witnet.CBOR memory _cborValue) public pure returns(bytes32 _bytes32) { + function decodeBytes32(CBOR memory _cborValue) internal pure returns(bytes32 _bytes32) { bytes memory _bb = decodeBytes(_cborValue); uint _len = _bb.length > 32 ? 32 : _bb.length; for (uint _i = 0; _i < _len; _i ++) { @@ -72,41 +81,41 @@ library WitnetDecoderLib { } } - /// @notice Decode a `Witnet.CBOR` structure into a `fixed16` value. + /// @notice Decode a `CBOR` structure into a `fixed16` value. /// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values /// by 5 decimal orders so as to get a fixed precision of 5 decimal positions, which should be OK for most `fixed16` /// use cases. In other words, the output of this method is 10,000 times the actual value, encoded into an `int32`. - /// @param _cborValue An instance of `Witnet.CBOR`. + /// @param _cborValue An instance of `CBOR`. /// @return The value represented by the input, as an `int128` value. - function decodeFixed16(Witnet.CBOR memory _cborValue) public pure returns(int32) { - require(_cborValue.majorType == 7, "WitnetDecoderLib: Tried to read a `fixed` value from a `WT.CBOR` with majorType != 7"); - require(_cborValue.additionalInformation == 25, "WitnetDecoderLib: Tried to read `fixed16` from a `WT.CBOR` with additionalInformation != 25"); + function decodeFixed16(CBOR memory _cborValue) internal pure returns(int32) { + require(_cborValue.majorType == 7, "WitnetCBOR: Tried to read a `fixed` value from a `WT.CBOR` with majorType != 7"); + require(_cborValue.additionalInformation == 25, "WitnetCBOR: Tried to read `fixed16` from a `WT.CBOR` with additionalInformation != 25"); return _cborValue.buffer.readFloat16(); } - /// @notice Decode a `Witnet.CBOR` structure into a native `int128[]` value whose inner values follow the same convention. + /// @notice Decode a `CBOR` structure into a native `int128[]` value whose inner values follow the same convention. /// as explained in `decodeFixed16`. - /// @param _cborValue An instance of `Witnet.CBOR`. + /// @param _cborValue An instance of `CBOR`. /// @return The value represented by the input, as an `int128[]` value. - function decodeFixed16Array(Witnet.CBOR memory _cborValue) external pure returns(int32[] memory) { - require(_cborValue.majorType == 4, "WitnetDecoderLib: Tried to read `int128[]` from a `Witnet.CBOR` with majorType != 4"); + function decodeFixed16Array(CBOR memory _cborValue) internal pure returns(int32[] memory) { + require(_cborValue.majorType == 4, "WitnetCBOR: Tried to read `int128[]` from a `CBOR` with majorType != 4"); uint64 length = readLength(_cborValue.buffer, _cborValue.additionalInformation); - require(length < _UINT64_MAX, "WitnetDecoderLib: Indefinite-length CBOR arrays are not supported"); + require(length < _UINT64_MAX, "WitnetCBOR: Indefinite-length CBOR arrays are not supported"); int32[] memory array = new int32[](length); for (uint64 i = 0; i < length; i++) { - Witnet.CBOR memory item = valueFromBuffer(_cborValue.buffer); + CBOR memory item = valueFromBuffer(_cborValue.buffer); array[i] = decodeFixed16(item); } return array; } - /// @notice Decode a `Witnet.CBOR` structure into a native `int128` value. - /// @param _cborValue An instance of `Witnet.CBOR`. + /// @notice Decode a `CBOR` structure into a native `int128` value. + /// @param _cborValue An instance of `CBOR`. /// @return The value represented by the input, as an `int128` value. - function decodeInt128(Witnet.CBOR memory _cborValue) public pure returns(int128) { + function decodeInt128(CBOR memory _cborValue) internal pure returns(int128) { if (_cborValue.majorType == 1) { uint64 length = readLength(_cborValue.buffer, _cborValue.additionalInformation); return int128(-1) - int128(uint128(length)); @@ -115,31 +124,31 @@ library WitnetDecoderLib { // a uniform API for positive and negative numbers return int128(uint128(decodeUint64(_cborValue))); } - revert("WitnetDecoderLib: Tried to read `int128` from a `Witnet.CBOR` with majorType not 0 or 1"); + revert("WitnetCBOR: Tried to read `int128` from a `CBOR` with majorType not 0 or 1"); } - /// @notice Decode a `Witnet.CBOR` structure into a native `int128[]` value. - /// @param _cborValue An instance of `Witnet.CBOR`. + /// @notice Decode a `CBOR` structure into a native `int128[]` value. + /// @param _cborValue An instance of `CBOR`. /// @return The value represented by the input, as an `int128[]` value. - function decodeInt128Array(Witnet.CBOR memory _cborValue) external pure returns(int128[] memory) { - require(_cborValue.majorType == 4, "WitnetDecoderLib: Tried to read `int128[]` from a `Witnet.CBOR` with majorType != 4"); + function decodeInt128Array(CBOR memory _cborValue) internal pure returns(int128[] memory) { + require(_cborValue.majorType == 4, "WitnetCBOR: Tried to read `int128[]` from a `CBOR` with majorType != 4"); uint64 length = readLength(_cborValue.buffer, _cborValue.additionalInformation); - require(length < _UINT64_MAX, "WitnetDecoderLib: Indefinite-length CBOR arrays are not supported"); + require(length < _UINT64_MAX, "WitnetCBOR: Indefinite-length CBOR arrays are not supported"); int128[] memory array = new int128[](length); for (uint64 i = 0; i < length; i++) { - Witnet.CBOR memory item = valueFromBuffer(_cborValue.buffer); + CBOR memory item = valueFromBuffer(_cborValue.buffer); array[i] = decodeInt128(item); } return array; } - /// @notice Decode a `Witnet.CBOR` structure into a native `string` value. - /// @param _cborValue An instance of `Witnet.CBOR`. + /// @notice Decode a `CBOR` structure into a native `string` value. + /// @param _cborValue An instance of `CBOR`. /// @return The value represented by the input, as a `string` value. - function decodeString(Witnet.CBOR memory _cborValue) public pure returns(string memory) { + function decodeString(CBOR memory _cborValue) internal pure returns(string memory) { _cborValue.len = readLength(_cborValue.buffer, _cborValue.additionalInformation); if (_cborValue.len == _UINT64_MAX) { bytes memory textData; @@ -158,66 +167,66 @@ library WitnetDecoderLib { } } - /// @notice Decode a `Witnet.CBOR` structure into a native `string[]` value. - /// @param _cborValue An instance of `Witnet.CBOR`. + /// @notice Decode a `CBOR` structure into a native `string[]` value. + /// @param _cborValue An instance of `CBOR`. /// @return The value represented by the input, as an `string[]` value. - function decodeStringArray(Witnet.CBOR memory _cborValue) external pure returns(string[] memory) { - require(_cborValue.majorType == 4, "WitnetDecoderLib: Tried to read `string[]` from a `Witnet.CBOR` with majorType != 4"); + function decodeStringArray(CBOR memory _cborValue) internal pure returns(string[] memory) { + require(_cborValue.majorType == 4, "WitnetCBOR: Tried to read `string[]` from a `CBOR` with majorType != 4"); uint64 length = readLength(_cborValue.buffer, _cborValue.additionalInformation); - require(length < _UINT64_MAX, "WitnetDecoderLib: Indefinite-length CBOR arrays are not supported"); + require(length < _UINT64_MAX, "WitnetCBOR: Indefinite-length CBOR arrays are not supported"); string[] memory array = new string[](length); for (uint64 i = 0; i < length; i++) { - Witnet.CBOR memory item = valueFromBuffer(_cborValue.buffer); + CBOR memory item = valueFromBuffer(_cborValue.buffer); array[i] = decodeString(item); } return array; } - /// @notice Decode a `Witnet.CBOR` structure into a native `uint64` value. - /// @param _cborValue An instance of `Witnet.CBOR`. + /// @notice Decode a `CBOR` structure into a native `uint64` value. + /// @param _cborValue An instance of `CBOR`. /// @return The value represented by the input, as an `uint64` value. - function decodeUint64(Witnet.CBOR memory _cborValue) public pure returns(uint64) { - require(_cborValue.majorType == 0, "WitnetDecoderLib: Tried to read `uint64` from a `Witnet.CBOR` with majorType != 0"); + function decodeUint64(CBOR memory _cborValue) internal pure returns(uint64) { + require(_cborValue.majorType == 0, "WitnetCBOR: Tried to read `uint64` from a `CBOR` with majorType != 0"); return readLength(_cborValue.buffer, _cborValue.additionalInformation); } - /// @notice Decode a `Witnet.CBOR` structure into a native `uint64[]` value. - /// @param _cborValue An instance of `Witnet.CBOR`. + /// @notice Decode a `CBOR` structure into a native `uint64[]` value. + /// @param _cborValue An instance of `CBOR`. /// @return The value represented by the input, as an `uint64[]` value. - function decodeUint64Array(Witnet.CBOR memory _cborValue) external pure returns(uint64[] memory) { - require(_cborValue.majorType == 4, "WitnetDecoderLib: Tried to read `uint64[]` from a `Witnet.CBOR` with majorType != 4"); + function decodeUint64Array(CBOR memory _cborValue) internal pure returns(uint64[] memory) { + require(_cborValue.majorType == 4, "WitnetCBOR: Tried to read `uint64[]` from a `CBOR` with majorType != 4"); uint64 length = readLength(_cborValue.buffer, _cborValue.additionalInformation); - require(length < _UINT64_MAX, "WitnetDecoderLib: Indefinite-length CBOR arrays are not supported"); + require(length < _UINT64_MAX, "WitnetCBOR: Indefinite-length CBOR arrays are not supported"); uint64[] memory array = new uint64[](length); for (uint64 i = 0; i < length; i++) { - Witnet.CBOR memory item = valueFromBuffer(_cborValue.buffer); + CBOR memory item = valueFromBuffer(_cborValue.buffer); array[i] = decodeUint64(item); } return array; } - /// @notice Decode a Witnet.CBOR structure from raw bytes. - /// @dev This is the main factory for Witnet.CBOR instances, which can be later decoded into native EVM types. + /// @notice Decode a CBOR structure from raw bytes. + /// @dev This is the main factory for CBOR instances, which can be later decoded into native EVM types. /// @param _cborBytes Raw bytes representing a CBOR-encoded value. - /// @return A `Witnet.CBOR` instance containing a partially decoded value. - function valueFromBytes(bytes memory _cborBytes) external pure returns(Witnet.CBOR memory) { - Witnet.Buffer memory buffer = Witnet.Buffer(_cborBytes, 0); + /// @return A `CBOR` instance containing a partially decoded value. + function valueFromBytes(bytes memory _cborBytes) internal pure returns(CBOR memory) { + WitnetBuffer.Buffer memory buffer = WitnetBuffer.Buffer(_cborBytes, 0); return valueFromBuffer(buffer); } - /// @notice Decode a Witnet.CBOR structure from raw bytes. - /// @dev This is an alternate factory for Witnet.CBOR instances, which can be later decoded into native EVM types. + /// @notice Decode a CBOR structure from raw bytes. + /// @dev This is an alternate factory for CBOR instances, which can be later decoded into native EVM types. /// @param _buffer A Buffer structure representing a CBOR-encoded value. - /// @return A `Witnet.CBOR` instance containing a partially decoded value. - function valueFromBuffer(Witnet.Buffer memory _buffer) public pure returns(Witnet.CBOR memory) { - require(_buffer.data.length > 0, "WitnetDecoderLib: Found empty buffer when parsing CBOR value"); + /// @return A `CBOR` instance containing a partially decoded value. + function valueFromBuffer(WitnetBuffer.Buffer memory _buffer) internal pure returns(CBOR memory) { + require(_buffer.data.length > 0, "WitnetCBOR: Found empty buffer when parsing CBOR value"); uint8 initialByte; uint8 majorType = 255; @@ -239,9 +248,9 @@ library WitnetDecoderLib { } } - require(majorType <= 7, "WitnetDecoderLib: Invalid CBOR major type"); + require(majorType <= 7, "WitnetCBOR: Invalid CBOR major type"); - return Witnet.CBOR( + return CBOR( _buffer, initialByte, majorType, @@ -252,7 +261,7 @@ library WitnetDecoderLib { /// Reads the length of the next CBOR item from a buffer, consuming a different number of bytes depending on the /// value of the `additionalInformation` argument. - function readLength(Witnet.Buffer memory _buffer, uint8 additionalInformation) private pure returns(uint64) { + function readLength(WitnetBuffer.Buffer memory _buffer, uint8 additionalInformation) private pure returns(uint64) { if (additionalInformation < 24) { return additionalInformation; } @@ -271,25 +280,25 @@ library WitnetDecoderLib { if (additionalInformation == 31) { return _UINT64_MAX; } - revert("WitnetDecoderLib: Invalid length encoding (non-existent additionalInformation value)"); + revert("WitnetCBOR: Invalid length encoding (non-existent additionalInformation value)"); } /// Read the length of a CBOR indifinite-length item (arrays, maps, byte strings and text) from a buffer, consuming /// as many bytes as specified by the first byte. - function readIndefiniteStringLength(Witnet.Buffer memory _buffer, uint8 majorType) private pure returns(uint64) { + function readIndefiniteStringLength(WitnetBuffer.Buffer memory _buffer, uint8 majorType) private pure returns(uint64) { uint8 initialByte = _buffer.readUint8(); if (initialByte == 0xff) { return _UINT64_MAX; } uint64 length = readLength(_buffer, initialByte & 0x1f); - require(length < _UINT64_MAX && (initialByte >> 5) == majorType, "WitnetDecoderLib: Invalid indefinite length"); + require(length < _UINT64_MAX && (initialByte >> 5) == majorType, "WitnetCBOR: Invalid indefinite length"); return length; } /// Read a text string of a given length from a buffer. Returns a `bytes memory` value for the sake of genericness, /// but it can be easily casted into a string with `string(result)`. // solium-disable-next-line security/no-assign-params - function readText(Witnet.Buffer memory _buffer, uint64 _length) private pure returns(bytes memory) { + function readText(WitnetBuffer.Buffer memory _buffer, uint64 _length) private pure returns(bytes memory) { bytes memory result; for (uint64 index = 0; index < _length; index++) { uint8 value = _buffer.readUint8(); diff --git a/contracts/libs/WitnetParserLib.sol b/contracts/libs/WitnetLib.sol similarity index 90% rename from contracts/libs/WitnetParserLib.sol rename to contracts/libs/WitnetLib.sol index a2dc6bfdf..d3920739a 100644 --- a/contracts/libs/WitnetParserLib.sol +++ b/contracts/libs/WitnetLib.sol @@ -1,18 +1,17 @@ // SPDX-License-Identifier: MIT -pragma solidity >=0.7.0 <0.9.0; -pragma experimental ABIEncoderV2; +pragma solidity >=0.8.0 <0.9.0; -import "./WitnetDecoderLib.sol"; +import "./Witnet.sol"; /// @title A library for decoding Witnet request results /// @notice The library exposes functions to check the Witnet request success. /// and retrieve Witnet results from CBOR values into solidity types. /// @author The Witnet Foundation. -library WitnetParserLib { +library WitnetLib { - using WitnetDecoderLib for bytes; - using WitnetDecoderLib for Witnet.CBOR; + using WitnetCBOR for bytes; + using WitnetCBOR for WitnetCBOR.CBOR; /// @notice Decode raw CBOR bytes into a Witnet.Result instance. /// @param _cborBytes Raw bytes representing a CBOR-encoded value. @@ -21,14 +20,14 @@ library WitnetParserLib { external pure returns (Witnet.Result memory) { - Witnet.CBOR memory cborValue = _cborBytes.valueFromBytes(); + WitnetCBOR.CBOR memory cborValue = _cborBytes.valueFromBytes(); return resultFromCborValue(cborValue); } /// @notice Decode a CBOR value into a Witnet.Result instance. /// @param _cborValue An instance of `Witnet.Value`. /// @return A `Witnet.Result` instance. - function resultFromCborValue(Witnet.CBOR memory _cborValue) + function resultFromCborValue(WitnetCBOR.CBOR memory _cborValue) public pure returns (Witnet.Result memory) { @@ -65,7 +64,7 @@ library WitnetParserLib { external pure returns(bytes memory) { - require(_result.success, "WitnetParserLib: Tried to read bytes value from errored Witnet.Result"); + require(_result.success, "WitnetLib: Tried to read bytes value from errored Witnet.Result"); return _result.value.decodeBytes(); } @@ -76,7 +75,7 @@ library WitnetParserLib { external pure returns(bytes32) { - require(_result.success, "WitnetParserLib: tried to read bytes32 value from errored Witnet.Result"); + require(_result.success, "WitnetLib: tried to read bytes32 value from errored Witnet.Result"); return _result.value.decodeBytes32(); } @@ -227,7 +226,7 @@ library WitnetParserLib { { require( !_result.success, - "WitnetParserLib: Tried to read error code from successful Witnet.Result" + "WitnetLib: Tried to read error code from successful Witnet.Result" ); return _result.value.decodeUint64Array(); } @@ -239,7 +238,7 @@ library WitnetParserLib { external pure returns (bool) { - require(_result.success, "WitnetParserLib: Tried to read `bool` value from errored Witnet.Result"); + require(_result.success, "WitnetLib: Tried to read `bool` value from errored Witnet.Result"); return _result.value.decodeBool(); } @@ -253,7 +252,7 @@ library WitnetParserLib { external pure returns (int32) { - require(_result.success, "WitnetParserLib: Tried to read `fixed16` value from errored Witnet.Result"); + require(_result.success, "WitnetLib: Tried to read `fixed16` value from errored Witnet.Result"); return _result.value.decodeFixed16(); } @@ -264,7 +263,7 @@ library WitnetParserLib { external pure returns (int32[] memory) { - require(_result.success, "WitnetParserLib: Tried to read `fixed16[]` value from errored Witnet.Result"); + require(_result.success, "WitnetLib: Tried to read `fixed16[]` value from errored Witnet.Result"); return _result.value.decodeFixed16Array(); } @@ -275,7 +274,7 @@ library WitnetParserLib { external pure returns (int128) { - require(_result.success, "WitnetParserLib: Tried to read `int128` value from errored Witnet.Result"); + require(_result.success, "WitnetLib: Tried to read `int128` value from errored Witnet.Result"); return _result.value.decodeInt128(); } @@ -286,7 +285,7 @@ library WitnetParserLib { external pure returns (int128[] memory) { - require(_result.success, "WitnetParserLib: Tried to read `int128[]` value from errored Witnet.Result"); + require(_result.success, "WitnetLib: Tried to read `int128[]` value from errored Witnet.Result"); return _result.value.decodeInt128Array(); } @@ -297,7 +296,7 @@ library WitnetParserLib { external pure returns(string memory) { - require(_result.success, "WitnetParserLib: Tried to read `string` value from errored Witnet.Result"); + require(_result.success, "WitnetLib: Tried to read `string` value from errored Witnet.Result"); return _result.value.decodeString(); } @@ -308,7 +307,7 @@ library WitnetParserLib { external pure returns (string[] memory) { - require(_result.success, "WitnetParserLib: Tried to read `string[]` value from errored Witnet.Result"); + require(_result.success, "WitnetLib: Tried to read `string[]` value from errored Witnet.Result"); return _result.value.decodeStringArray(); } @@ -319,7 +318,7 @@ library WitnetParserLib { external pure returns(uint64) { - require(_result.success, "WitnetParserLib: Tried to read `uint64` value from errored Witnet.Result"); + require(_result.success, "WitnetLib: Tried to read `uint64` value from errored Witnet.Result"); return _result.value.decodeUint64(); } @@ -330,7 +329,7 @@ library WitnetParserLib { external pure returns (uint64[] memory) { - require(_result.success, "WitnetParserLib: Tried to read `uint64[]` value from errored Witnet.Result"); + require(_result.success, "WitnetLib: Tried to read `uint64[]` value from errored Witnet.Result"); return _result.value.decodeUint64Array(); } diff --git a/contracts/patterns/ERC165.sol b/contracts/patterns/ERC165.sol new file mode 100644 index 000000000..607071bbb --- /dev/null +++ b/contracts/patterns/ERC165.sol @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; +import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; diff --git a/contracts/patterns/Ownable2Step.sol b/contracts/patterns/Ownable2Step.sol new file mode 100644 index 000000000..79aab39ea --- /dev/null +++ b/contracts/patterns/Ownable2Step.sol @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; +import "@openzeppelin/contracts/access/Ownable2Step.sol"; diff --git a/contracts/patterns/Payable.sol b/contracts/patterns/Payable.sol index fff21030a..6ff570307 100644 --- a/contracts/patterns/Payable.sol +++ b/contracts/patterns/Payable.sol @@ -7,8 +7,8 @@ import "../interfaces/IERC20.sol"; abstract contract Payable { IERC20 public immutable currency; - event Received(address from, uint256 amount); - event Transfer(address to, uint256 amount); + event Received(address from, uint256 value); + event Transfer(address to, uint256 value); constructor(address _currency) { currency = IERC20(_currency); diff --git a/contracts/patterns/Proxiable.sol b/contracts/patterns/Proxiable.sol index 01872d34d..680607658 100644 --- a/contracts/patterns/Proxiable.sol +++ b/contracts/patterns/Proxiable.sol @@ -5,5 +5,5 @@ pragma solidity >=0.6.0 <0.9.0; interface Proxiable { /// @dev Complying with EIP-1822: Universal Upgradable Proxy Standard (UUPS) /// @dev See https://eips.ethereum.org/EIPS/eip-1822. - function proxiableUUID() external pure returns (bytes32); + function proxiableUUID() external view returns (bytes32); } diff --git a/contracts/patterns/ReentrancyGuard.sol b/contracts/patterns/ReentrancyGuard.sol new file mode 100644 index 000000000..56ff2e0ba --- /dev/null +++ b/contracts/patterns/ReentrancyGuard.sol @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; +import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; diff --git a/contracts/patterns/Upgradable.sol b/contracts/patterns/Upgradable.sol index 75eb6e801..f021ee0b4 100644 --- a/contracts/patterns/Upgradable.sol +++ b/contracts/patterns/Upgradable.sol @@ -22,7 +22,7 @@ abstract contract Upgradable is Initializable, Proxiable { address indexed from, address indexed baseAddr, bytes32 indexed baseCodehash, - bytes32 versionTag + string versionTag ); constructor (bool _isUpgradable) { @@ -39,7 +39,6 @@ abstract contract Upgradable is Initializable, Proxiable { /// @dev Tells whether provided address could eventually upgrade the contract. function isUpgradableFrom(address from) virtual external view returns (bool); - /// TODO: the following methods should be all declared as pure /// whenever this Solidity's PR gets merged and released: /// https://github.com/ethereum/solidity/pull/10240 @@ -62,5 +61,5 @@ abstract contract Upgradable is Initializable, Proxiable { } /// @dev Retrieves human-redable named version of current implementation. - function version() virtual public view returns (bytes32); + function version() virtual public view returns (string memory); } \ No newline at end of file diff --git a/migrations/scripts/1_deploy_wrb.js b/migrations/scripts/1_deploy_wrb.js index 4dbefc0f9..bb27b0cec 100644 --- a/migrations/scripts/1_deploy_wrb.js +++ b/migrations/scripts/1_deploy_wrb.js @@ -10,7 +10,7 @@ module.exports = async function (deployer, network, accounts) { const addresses = require("../witnet.addresses")[realm][network = network.split("-")[0]] const artifactsName = merge(settings.artifacts.default, settings.artifacts[realm]) - let WitnetParserLib, WitnetDecoderLib + let WitnetLib let WitnetProxy, WitnetRequestBoard let upgradeProxy = false @@ -50,36 +50,23 @@ module.exports = async function (deployer, network, accounts) { } } - /* Try to find 'WitnetParserLib' artifact, and deployed address if any */ + /* Try to find 'WitnetLib' artifact, and deployed address if any */ try { - WitnetParserLib = artifacts.require(artifactsName.WitnetParserLib) - if (!WitnetParserLib.isDeployed() || WitnetParserLib.address !== addresses.WitnetParserLib) { - // If the 'WitnetParserLib' is found, try to read deployed address from addresses file: - if (addresses) WitnetParserLib.address = addresses.WitnetParserLib + WitnetLib = artifacts.require(artifactsName.WitnetLib) + if (!WitnetLib.isDeployed() || WitnetLib.address !== addresses.Witnetib) { + // If the 'WitnetLib' is found, try to read deployed address from addresses file: + if (addresses) WitnetLib.address = addresses.WitnetLib } } catch { - console.error(`\n Fatal: '${artifactsName.WitnetParserLib}' artifact not found.\n`) + console.error(`\n Fatal: '${artifactsName.WitnetLib}' artifact not found.\n`) process.exit(1) } - /* Deploy new instance of 'WitnetParserLib', and 'WitnetDecoderLib', if neccesary */ - if (!WitnetParserLib.isDeployed() || utils.isNullAddress(WitnetParserLib.address)) { - // Fetch the 'WitnetDecoderLib' artifact: - try { - WitnetDecoderLib = artifacts.require(artifactsName.WitnetDecoderLib) - } catch { - console.error(`\n Fatal: '${artifactsName.WitnetDecoderLib}' artifact not found.\n`) - process.exit(1) - } - // Deploy the 'WitnetDecoderLib' artifact first, if not done yet: - if (!WitnetDecoderLib.isDeployed()) { - await deployer.deploy(WitnetDecoderLib) - // and link the just-deployed 'WitnetDecoderLib' to the 'WitnetParserLib' artifact: - await deployer.link(WitnetDecoderLib, WitnetParserLib) - } - await deployer.deploy(WitnetParserLib) + /* Deploy new instance of 'WitnetLib', if neccesary */ + if (!WitnetLib.isDeployed() || utils.isNullAddress(WitnetLib.address)) { + await deployer.deploy(WitnetLib) } else { - console.log(`\n Skipped: '${artifactsName.WitnetParserLib}' deployed at ${WitnetParserLib.address}.`) + console.log(`\n Skipped: '${artifactsName.WitnetLib}' deployed at ${WitnetLib.address}.`) } /* Deploy new instance of target 'WitnetRequestBoard' implementation */ @@ -93,7 +80,7 @@ module.exports = async function (deployer, network, accounts) { } } - await deployer.link(WitnetParserLib, WitnetRequestBoard) + await deployer.link(WitnetLib, WitnetRequestBoard) await deployer.deploy( WitnetRequestBoard, ...( @@ -131,7 +118,7 @@ module.exports = async function (deployer, network, accounts) { console.log(` >> WRB address: ${await proxy.implementation.call()}`) console.log(` >> WRB proxiableUUID: ${await wrb.proxiableUUID.call()}`) console.log(` >> WRB codehash: ${await wrb.codehash.call()}`) - console.log(` >> WRB version tag: ${web3.utils.hexToString(await wrb.version.call())}`) + console.log(` >> WRB version tag: ${await wrb.version.call()}`) } else { console.log(` >> WRB addresses: ${oldAddr} => ${await proxy.implementation.call()}`) console.log(` >> WRB proxiableUUID: ${await wrb.proxiableUUID.call()}`) diff --git a/migrations/witnet.addresses.json b/migrations/witnet.addresses.json index 0d58900e0..17df28251 100644 --- a/migrations/witnet.addresses.json +++ b/migrations/witnet.addresses.json @@ -1,19 +1,19 @@ { "default": { "ethereum.goerli": { - "WitnetParserLib": "0xd80ffbc599ad5f2Bb6b33AEaAC5E4Fac3cfaf0D1", + "WitnetLib": "0xd80ffbc599ad5f2Bb6b33AEaAC5E4Fac3cfaf0D1", "WitnetPriceRouter": "0x1cF3Aa9DBF4880d797945726B94B9d29164211BE", "WitnetRandomness": "0x6Eb87EcCe6218Cd0e97299331D2aa5d2e53da5cD", "WitnetRequestBoard": "0xb58D05247d16b3F1BD6B59c52f7f61fFef02BeC8" }, "ethereum.rinkeby": { - "WitnetParserLib": "0xF4fE7fA5c5e6CCa213377F10fD98b6b0DC00cd70", + "WitnetLib": "0xF4fE7fA5c5e6CCa213377F10fD98b6b0DC00cd70", "WitnetPriceRouter": "0xa50b17C2fc373c247C3b603f83df6A7800cB0DC9", "WitnetRandomness": "0x50F742Fbf9a445AE6B2136F5987414A4c5aeE921", "WitnetRequestBoard": "0x6cE42a35C61ccfb42907EEE57eDF14Bb69C7fEF4" }, "ethereum.mainnet": { - "WitnetParserLib": "0xaD18Fd3CC724A11c2B0D8cc7f1B108d8A3388416", + "WitnetLib": "0xaD18Fd3CC724A11c2B0D8cc7f1B108d8A3388416", "WitnetPriceRouter": "0x83A757eAe821Ad7B520D9A74952337138A80b2AF", "WitnetRandomness": "0x894907c7Ab64C1092620B5c8Ba039BB6E611eba8", "WitnetRequestBoard": "0x9E4fae1c7ac543a81E4E2a5486a0dDaad8194bdA" @@ -29,13 +29,13 @@ }, "avalanche": { "avalanche.testnet": { - "WitnetParserLib": "0x62B1BB81E57E9c0E22A0dc6FdeE456146a7D7083", + "WitnetLib": "0x62B1BB81E57E9c0E22A0dc6FdeE456146a7D7083", "WitnetPriceRouter": "0x99Af0CF37d1C6b9Bdfe33cc0A89C00D97D3c42F4", "WitnetRandomness": "0xD47fc24C99fD94f33bD2f33FE373b1447bB10724", "WitnetRequestBoard": "0xD9465D38f50f364b3263Cb219e58d4dB2D584530" }, "avalanche.mainnet": { - "WitnetParserLib": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", + "WitnetLib": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", "WitnetPriceRouter": "0xBaaF31F4AAc5ab5334b6E239a83bf4E855C55ea7", "WitnetRandomness": "0xa4A73a2A32320282a4d7dDe6a7467AeFA3B7950F", "WitnetRequestBoard": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079" @@ -43,25 +43,25 @@ }, "boba": { "boba.moonbeam.bobabase": { - "WitnetParserLib": "0x02Cd4089679EAA9431a88170fd784e7dE78A2425", + "WitnetLib": "0x02Cd4089679EAA9431a88170fd784e7dE78A2425", "WitnetPriceRouter": "0xD9465D38f50f364b3263Cb219e58d4dB2D584530", "WitnetRandomness": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", "WitnetRequestBoard": "0x0985FDe9f424fe4f5AC516F66bAf5591e18aCBEb" }, "boba.ethereum.goerli": { - "WitnetParserLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", + "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", "WitnetPriceRouter": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", "WitnetRandomness": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" }, "boba.ethereum.rinkeby": { - "WitnetParserLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", + "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", "WitnetPriceRouter": "0x36928Aeedaaf7D85bcA39aDfB2A39ec529ce221a", "WitnetRandomness": "0xeAcAcC48eDD5221EC7182E1789d8bFa9dF801dFF", "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" }, "boba.ethereum.mainnet": { - "WitnetParserLib": "0x6473063EBEabC0606A4159b7d9F79BB306ED0D2A", + "WitnetLib": "0x6473063EBEabC0606A4159b7d9F79BB306ED0D2A", "WitnetPriceRouter": "0x93f61D0D5F623144e7C390415B70102A9Cc90bA5", "WitnetRandomness": "0x3D7Ccf6518deBE3d2fd20c4b6AEc3FD904c0Ad29", "WitnetRequestBoard": "0xd3AD9a4b26527E3bA5Fc60B75Eb002D47D98e292" @@ -69,13 +69,13 @@ }, "celo": { "celo.alfajores": { - "WitnetParserLib": "0x1A58F1dAD4592814733913Dd59CcEbf55c45C6e1", + "WitnetLib": "0x1A58F1dAD4592814733913Dd59CcEbf55c45C6e1", "WitnetPriceRouter": "0x6f8A7E2bBc1eDb8782145cD1089251f6e2C738AE", "WitnetRequestBoard": "0x99a0B5eb260Fe3BfcF9d658850e3dD6d6B69183A", "WitnetRandomness": "0xbD804467270bCD832b4948242453CA66972860F5" }, "celo.mainnet": { - "WitnetParserLib": "0x46FF6e05fd0a5fb4D794B99eCAE41c43f4D62d15", + "WitnetLib": "0x46FF6e05fd0a5fb4D794B99eCAE41c43f4D62d15", "WitnetPriceRouter": "0x931673904eB6E69D775e35F522c0EA35575297Cb", "WitnetRandomness": "0x3D7Ccf6518deBE3d2fd20c4b6AEc3FD904c0Ad29", "WitnetRequestBoard": "0x03E709E6422E30C033456FCde38C70A12553E468" @@ -83,25 +83,25 @@ }, "conflux": { "conflux.core.testnet": { - "WitnetParserLib": "0x88E443F2CB310B24dd505AeBECA23e7aBA562326", + "WitnetLib": "0x88E443F2CB310B24dd505AeBECA23e7aBA562326", "WitnetPriceRouter": "0x8F61C7b18F69bB87D6151B8a5D733E1945ea6c25", "WitnetRandomness": "0x887bC8Db7D91413D1575071925Ee8d77fE2CBc81", "WitnetRequestBoard": "0x8aB653B73a0e0552dDdce8c76F97c6AA826EFbD4" }, "conflux.core.mainnet": { - "WitnetParserLib": "0x8A026e6956B4DB3E81bb113401798e59cFBEA4C6", + "WitnetLib": "0x8A026e6956B4DB3E81bb113401798e59cFBEA4C6", "WitnetPriceRouter": "0x806c8dFd322EE2d52b188CC472e0814F64304C32", "WitnetRandomness": "0x8C3824A9A6C3F5B0ac107E2c7dBc8d88c14aF6D9", "WitnetRequestBoard": "0x84C708bfd79bBC83Ad8753dAb1852EfE9D6712CC" }, "conflux.espace.testnet": { - "WitnetParserLib": "0x2881F0106A1894add7600B4B147e715078Fded03", + "WitnetLib": "0x2881F0106A1894add7600B4B147e715078Fded03", "WitnetPriceRouter": "0x49C0BCce51a8B28f92d008394F06d5B259657F33", "WitnetRandomness": "0xa784093826e2894Ab3Db315f4e05F0F26407BBfF", "WitnetRequestBoard": "0x0C4be6AA667df48de54BA174bE7948875fdf152B" }, "conflux.espace.mainnet": { - "WitnetParserLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", "WitnetRandomness": "0x1ebd93231a7fe551e1d6405404df34909eff4c2c", "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" @@ -109,13 +109,13 @@ }, "cronos": { "cronos.testnet": { - "WitnetParserLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", + "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", "WitnetPriceRouter": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", "WitnetRandomness": "0x0017A464A86f48B342Cae3b8Fe29cFCDaA7b0643", "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" }, "cronos.mainnet": { - "WitnetParserLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", "WitnetRandomness": "0x3737be6FcFf5B3B0f9DCc9a9ae1Da56561D0d0d3", "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" @@ -123,13 +123,13 @@ }, "cube": { "cube.testnet": { - "WitnetParserLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", + "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", "WitnetPriceRouter": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", "WitnetRandomness": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" }, "cube.mainnet": { - "WitnetParserLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", "WitnetPriceRouter": "0xE22f48DDdcb34BD34489fE224d7fFC1b0a361D87", "WitnetRandomness": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" @@ -137,7 +137,7 @@ }, "dogechain": { "dogechain.testnet": { - "WitnetParserLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", + "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", "WitnetPriceRouter": "0x9E943Ab1FD0D35B3BaDe31AA78D60C485EA1a604", "WitnetRandomness": "0xD9465D38f50f364b3263Cb219e58d4dB2D584530", "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" @@ -151,14 +151,14 @@ }, "harmony": { "harmony.testnet#0": { - "WitnetParserLib": "0x315cfa2F1108d1B490302d79AB4a5A99452e5800", + "WitnetLib": "0x315cfa2F1108d1B490302d79AB4a5A99452e5800", "WitnetPriceRouter": "0x08d479a544b05B297454e5CAc133abA3a584AB8E", "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" } }, "hsc": { "hsc.testnet": { - "WitnetParserLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", + "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", "WitnetPriceRouter": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", "WitnetRandomness": "0xe1330491bdC37fc4E8801843Bb3015815822F8A8", "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" @@ -166,13 +166,13 @@ }, "kava": { "kava.testnet": { - "WitnetParserLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", + "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", "WitnetPriceRouter": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", "WitnetRandomness": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" }, "kava.mainnet": { - "WitnetParserLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", "WitnetRandomness": "0x1ebD93231a7fE551E1d6405404Df34909eff4c2C", "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" @@ -180,13 +180,13 @@ }, "kcc": { "kcc.testnet": { - "WitnetParserLib": "0x351cEe820E3A393dCF126FbEE60928a80E99C2e1", + "WitnetLib": "0x351cEe820E3A393dCF126FbEE60928a80E99C2e1", "WitnetPriceRouter": "0xba7CF62498340fa3734EC51Ca8A69928F0d9E03a", "WitnetRandomness": "0x76c72518060952FAec3f90666F047e39E3333f7E", "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" }, "kcc.mainnet": { - "WitnetParserLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", "WitnetRandomness": "0x1ebd93231a7fe551e1d6405404df34909eff4c2c", "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" @@ -194,13 +194,13 @@ }, "klaytn": { "klaytn.testnet": { - "WitnetParserLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", + "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", "WitnetPriceRouter": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", "WitnetRandomness": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" }, "klaytn.mainnet": { - "WitnetParserLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", "WitnetRandomness": "0x1ebD93231a7fE551E1d6405404Df34909eff4c2C", "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" @@ -208,13 +208,13 @@ }, "meter": { "meter.testnet": { - "WitnetParserLib": "0x3a46EF336f619e69dcDCa36A6772596E0fD800B3", + "WitnetLib": "0x3a46EF336f619e69dcDCa36A6772596E0fD800B3", "WitnetPriceRouter": "0xBbDB82a16d7b66bb076879f766042b914F1C7572", "WitnetRandomness": "0x89de43D6295D960Af1F2029a66CE680c7f798fC1", "WitnetRequestBoard": "0xF99883aa51Fb76E37De6aC37854230d2337D2752" }, "meter.mainnet": { - "WitnetParserLib": "0x60507Ef497EC61d407cD6Fa1c65FE820620bfA88", + "WitnetLib": "0x60507Ef497EC61d407cD6Fa1c65FE820620bfA88", "WitnetPriceRouter": "0xA0Ea8C99159843afdAE9eD092E8eaec0368e8A20", "WitnetRandomness": "0xE189B1D26dAAB45cd344452f29Db8E93B5C7FaF1", "WitnetRequestBoard": "0x4e645446799776B9670D18E1ecBCad059987eaCa" @@ -222,13 +222,13 @@ }, "metis": { "metis.rinkeby": { - "WitnetParserLib": "0xAec43b0DED0148B8456B355Ec2dA3930b42bFA08", + "WitnetLib": "0xAec43b0DED0148B8456B355Ec2dA3930b42bFA08", "WitnetPriceRouter": "0x5134EAF08bcf8cE1922991150AAad1774e93751f", "WitnetRandomness": "0x7b0D67739b5B9480080817E5b921EbbA714236ca", "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" }, "metis.mainnet": { - "WitnetParserLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", "WitnetRandomness": "0xB280e3B785f615C000A8BeBb55C35eCD2376F2eb", "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" @@ -236,19 +236,19 @@ }, "moonbeam": { "moonbeam.moonbase": { - "WitnetParserLib": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", + "WitnetLib": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", "WitnetPriceRouter": "0x56834Ff8D4b27db647Da97CA3bd8540f7fA0e89D", "WitnetRandomness": "0x45111778a7db1356DaAB576cBe73681F0745182c", "WitnetRequestBoard": "0x02Cd4089679EAA9431a88170fd784e7dE78A2425" }, "moonbeam.moonriver": { - "WitnetParserLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", "WitnetPriceRouter": "0xE22f48DDdcb34BD34489fE224d7fFC1b0a361D87", "WitnetRandomness": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" }, "moonbeam.mainnet": { - "WitnetParserLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", "WitnetRandomness": "0x1ebD93231a7fE551E1d6405404Df34909eff4c2C", "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" @@ -256,13 +256,13 @@ }, "okxchain": { "okxchain.testnet": { - "WitnetParserLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", + "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", "WitnetPriceRouter": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", "WitnetRandomness": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" }, "okxchain.mainnet": { - "WitnetParserLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", "WitnetRandomness": "0x1ebD93231a7fE551E1d6405404Df34909eff4c2C", "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" @@ -270,13 +270,13 @@ }, "optimism": { "optimism.goerli": { - "WitnetParserLib": "0x02Cd4089679EAA9431a88170fd784e7dE78A2425", + "WitnetLib": "0x02Cd4089679EAA9431a88170fd784e7dE78A2425", "WitnetPriceRouter": "0xD9465D38f50f364b3263Cb219e58d4dB2D584530", "WitnetRandomness": "0x9E943Ab1FD0D35B3BaDe31AA78D60C485EA1a604", "WitnetRequestBoard": "0x0985FDe9f424fe4f5AC516F66bAf5591e18aCBEb" }, "optimism.mainnet": { - "WitnetParserLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", "WitnetRandomness": "0x1ebD93231a7fE551E1d6405404Df34909eff4c2C", "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" @@ -284,13 +284,13 @@ }, "polygon": { "polygon.goerli": { - "WitnetParserLib": "0x70767887abBF23291B24620dE6bcEBe8d8F1e6F3", + "WitnetLib": "0x70767887abBF23291B24620dE6bcEBe8d8F1e6F3", "WitnetPriceRouter": "0x6d5544ca5b35bf2e7a78ace4E7B8d191fe5C9FAb", "WitnetRandomness": "0x0178287DfECA2F13F342512282bc8B59e774aE43", "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" }, "polygon.mainnet": { - "WitnetParserLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", "WitnetPriceRouter": "0x3806311c7138ddF2bAF2C2093ff3633E5A73AbD4", "WitnetRandomness": "0xc8c0d4dB2D7801D6E2A863934597cFD31689f7D5", "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" @@ -298,7 +298,7 @@ }, "reef": { "reef.testnet": { - "WitnetParserLib": "0x5757040246996BFcDC890CD1CcdE6D414eAbFF74", + "WitnetLib": "0x5757040246996BFcDC890CD1CcdE6D414eAbFF74", "WitnetPriceRouter": "0xB600e92DbA7CA66895Aa353d9128514ba47e7896", "WitnetRandomness": "0x3f159F3bD5c27A936E0C897a4584Eb1647a62197", "WitnetRequestBoard": "0x77d64ec18b0a14fefe673e3aa194c816c2383232" @@ -320,13 +320,13 @@ }, "smartbch": { "smartbch.amber": { - "WitnetParserLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", + "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", "WitnetPriceRouter": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", "WitnetRandomness": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" }, "smartbch.mainnet": { - "WitnetParserLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", "WitnetRandomness": "0x1ebD93231a7fE551E1d6405404Df34909eff4c2C", "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" @@ -334,13 +334,13 @@ }, "syscoin": { "syscoin.testnet": { - "WitnetParserLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", + "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", "WitnetPriceRouter": "0x9E943Ab1FD0D35B3BaDe31AA78D60C485EA1a604", "WitnetRandomness": "0x56834Ff8D4b27db647Da97CA3bd8540f7fA0e89D", "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" }, "syscoin.mainnet": { - "WitnetParserLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", "WitnetPriceRouter": "0xE22f48DDdcb34BD34489fE224d7fFC1b0a361D87", "WitnetRandomness": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" diff --git a/migrations/witnet.settings.js b/migrations/witnet.settings.js index 17a539802..b8da0ddae 100644 --- a/migrations/witnet.settings.js +++ b/migrations/witnet.settings.js @@ -2,8 +2,7 @@ const packageJson = require("../package.json") module.exports = { artifacts: { default: { - WitnetDecoderLib: "WitnetDecoderLib", - WitnetParserLib: "WitnetParserLib", + WitnetLib: "WitnetLib", WitnetPriceRouter: "WitnetPriceRouter", WitnetProxy: "WitnetProxy", WitnetRandomness: "WitnetRandomness", diff --git a/package.json b/package.json index 13bc3e237..dfbc1ab10 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "flatten": "node ./scripts/flatten.js 2>&1", "flatten:witnet": "npm run clean && npm run flatten:witnet:libs && npm run flatten:witnet:proxy && npm run flatten:witnet:boards && npm run flatten:witnet:rng && npm run flatten:witnet:registry", "flatten:witnet:rng": "npm run flatten contracts/apps/WitnetRandomness.sol && npm run flatten contracts/requests/WitnetRequestRandomness.sol", - "flatten:witnet:libs": "npm run flatten contracts/libs/WitnetDecoderLib.sol && npm run flatten contracts/libs/WitnetParserLib.sol", + "flatten:witnet:libs": "npm run flatten contracts/libs/WitnetLib.sol", "flatten:witnet:proxy": "npm run flatten contracts/impls/WitnetProxy.sol", "flatten:witnet:registry": "npm run flatten contracts/apps/WitnetPriceRouter.sol", "flatten:witnet:boards": "npm run flatten:witnet:boards:trustable", @@ -41,8 +41,9 @@ ], "license": "MIT", "dependencies": { - "@openzeppelin/contracts": "^4.7.3", "ado-contracts": "1.0.0", + "@eth-optimism/solc": "0.7.6-alpha.1", + "@openzeppelin/contracts": "~4.8.0-rc.2", "lodash": "^4.17.21" }, "devDependencies": { diff --git a/scripts/utils.js b/scripts/utils.js index 70664c633..6b9053fbb 100644 --- a/scripts/utils.js +++ b/scripts/utils.js @@ -3,12 +3,22 @@ const readline = require("readline") const web3 = require("web3") module.exports = { + fromAscii, getRealmNetworkFromArgs, getRealmNetworkFromString, isNullAddress, prompt, } +function fromAscii (str) { + const arr1 = [] + for (let n = 0, l = str.length; n < l; n++) { + const hex = Number(str.charCodeAt(n)).toString(16) + arr1.push(hex) + } + return "0x" + arr1.join("") +} + function getRealmNetworkFromArgs () { let networkString = process.argv.includes("test") ? "test" : "development" // If a `--network` argument is provided, use that instead diff --git a/test/TestWitnetBuffer.sol b/test/TestWitnetBuffer.sol index 228a6e981..e8c5e3d54 100644 --- a/test/TestWitnetBuffer.sol +++ b/test/TestWitnetBuffer.sol @@ -9,13 +9,13 @@ import "../contracts/libs/WitnetBuffer.sol"; contract TestWitnetBuffer { - using WitnetBuffer for Witnet.Buffer; + using WitnetBuffer for WitnetBuffer.Buffer; event Log(string _topic, uint256 _value); function testRead31bytes() external { bytes memory data = hex"58207eadcf3ba9a9a860b4421ee18caa6dca4738fef266aa7b3668a2ff97304cfcab"; - Witnet.Buffer memory buf = Witnet.Buffer(data, 1); + WitnetBuffer.Buffer memory buf = WitnetBuffer.Buffer(data, 1); bytes memory actual = buf.read(31); Assert.equal(31, actual.length, "Read 31 bytes from a Buffer"); @@ -23,7 +23,7 @@ contract TestWitnetBuffer { function testRead32bytes() external { bytes memory data = hex"58207eadcf3ba9a9a860b4421ee18caa6dca4738fef266aa7b3668a2ff97304cfcab"; - Witnet.Buffer memory buf = Witnet.Buffer(data, 1); + WitnetBuffer.Buffer memory buf = WitnetBuffer.Buffer(data, 1); bytes memory actual = buf.read(32); Assert.equal(32, actual.length, "Read 32 bytes from a Buffer"); @@ -31,7 +31,7 @@ contract TestWitnetBuffer { function testRead33bytes() external { bytes memory data = hex"58207eadcf3ba9a9a860b4421ee18caa6dca4738fef266aa7b3668a2ff97304cfcabff"; - Witnet.Buffer memory buf = Witnet.Buffer(data, 1); + WitnetBuffer.Buffer memory buf = WitnetBuffer.Buffer(data, 1); bytes memory actual = buf.read(33); Assert.equal(33, actual.length, "Read 33 bytes from a Buffer"); @@ -40,7 +40,7 @@ contract TestWitnetBuffer { function testReadUint8() external { uint8 expected = 31; bytes memory data = abi.encodePacked(expected); - Witnet.Buffer memory buf = Witnet.Buffer(data, 0); + WitnetBuffer.Buffer memory buf = WitnetBuffer.Buffer(data, 0); uint8 actual = buf.readUint8(); Assert.equal(uint(actual), uint(expected), "Read Uint8 from a Buffer"); @@ -49,7 +49,7 @@ contract TestWitnetBuffer { function testReadUint16() external { uint16 expected = 31415; bytes memory data = abi.encodePacked(expected); - Witnet.Buffer memory buf = Witnet.Buffer(data, 0); + WitnetBuffer.Buffer memory buf = WitnetBuffer.Buffer(data, 0); uint16 actual = buf.readUint16(); Assert.equal(uint(actual), uint(expected), "Read Uint16 from a Buffer"); @@ -58,7 +58,7 @@ contract TestWitnetBuffer { function testReadUint32() external { uint32 expected = 3141592653; bytes memory data = abi.encodePacked(expected); - Witnet.Buffer memory buf = Witnet.Buffer(data, 0); + WitnetBuffer.Buffer memory buf = WitnetBuffer.Buffer(data, 0); uint32 actual = buf.readUint32(); Assert.equal(uint(actual), uint(expected), "Read Uint32 from a Buffer"); @@ -67,7 +67,7 @@ contract TestWitnetBuffer { function testReadUint64() external { uint64 expected = 3141592653589793238; bytes memory data = abi.encodePacked(expected); - Witnet.Buffer memory buf = Witnet.Buffer(data, 0); + WitnetBuffer.Buffer memory buf = WitnetBuffer.Buffer(data, 0); uint64 actual = buf.readUint64(); Assert.equal(uint(actual), uint(expected), "Read Uint64 from a Buffer"); @@ -76,7 +76,7 @@ contract TestWitnetBuffer { function testReadUint128() external { uint128 expected = 314159265358979323846264338327950288419; bytes memory data = abi.encodePacked(expected); - Witnet.Buffer memory buf = Witnet.Buffer(data, 0); + WitnetBuffer.Buffer memory buf = WitnetBuffer.Buffer(data, 0); uint128 actual = buf.readUint128(); Assert.equal(uint(actual), uint(expected), "Read Uint128 from a Buffer"); @@ -85,7 +85,7 @@ contract TestWitnetBuffer { function testReadUint256() external { uint256 expected = 31415926535897932384626433832795028841971693993751058209749445923078164062862; bytes memory data = abi.encodePacked(expected); - Witnet.Buffer memory buf = Witnet.Buffer(data, 0); + WitnetBuffer.Buffer memory buf = WitnetBuffer.Buffer(data, 0); uint256 actual = buf.readUint256(); Assert.equal(uint(actual), uint(expected), "Read Uint64 from a Buffer"); @@ -95,7 +95,7 @@ contract TestWitnetBuffer { uint8 small = 31; uint64 big = 3141592653589793238; bytes memory data = abi.encodePacked(small, big); - Witnet.Buffer memory buf = Witnet.Buffer(data, 0); + WitnetBuffer.Buffer memory buf = WitnetBuffer.Buffer(data, 0); buf.readUint8(); uint64 actualBig = buf.readUint64(); @@ -168,7 +168,7 @@ contract TestWitnetBuffer { bytes memory error; uint8 input = 0xAA; bytes memory data = abi.encodePacked(input); - Witnet.Buffer memory buf = Witnet.Buffer(data, 0); + WitnetBuffer.Buffer memory buf = WitnetBuffer.Buffer(data, 0); buf.next(); TestWitnetBuffer(address(throwProxy)).errorReadNext(buf); @@ -177,31 +177,31 @@ contract TestWitnetBuffer { } function errorReadAsUint16(bytes memory data) public { - Witnet.Buffer memory buf = Witnet.Buffer(data, 0); + WitnetBuffer.Buffer memory buf = WitnetBuffer.Buffer(data, 0); buf.readUint16(); } function errorReadAsUint32(bytes memory data) public { - Witnet.Buffer memory buf = Witnet.Buffer(data, 0); + WitnetBuffer.Buffer memory buf = WitnetBuffer.Buffer(data, 0); buf.readUint32(); } function errorReadAsUint64(bytes memory data) public { - Witnet.Buffer memory buf = Witnet.Buffer(data, 0); + WitnetBuffer.Buffer memory buf = WitnetBuffer.Buffer(data, 0); buf.readUint64(); } function errorReadAsUint128(bytes memory data) public { - Witnet.Buffer memory buf = Witnet.Buffer(data, 0); + WitnetBuffer.Buffer memory buf = WitnetBuffer.Buffer(data, 0); buf.readUint128(); } function errorReadAsUint256(bytes memory data) public { - Witnet.Buffer memory buf = Witnet.Buffer(data, 0); + WitnetBuffer.Buffer memory buf = WitnetBuffer.Buffer(data, 0); buf.readUint256(); } - function errorReadNext(Witnet.Buffer memory buf) public { + function errorReadNext(WitnetBuffer.Buffer memory buf) public { buf.next(); } diff --git a/test/TestWitnetDecoderLib.sol b/test/TestWitnetCBOR.sol similarity index 59% rename from test/TestWitnetDecoderLib.sol rename to test/TestWitnetCBOR.sol index 2c8b5530e..56178989a 100644 --- a/test/TestWitnetDecoderLib.sol +++ b/test/TestWitnetCBOR.sol @@ -4,33 +4,33 @@ pragma solidity >=0.7.0 <0.9.0; pragma experimental ABIEncoderV2; import "truffle/Assert.sol"; -import "../contracts/libs/WitnetDecoderLib.sol"; +import "../contracts/libs/WitnetCBOR.sol"; -contract TestWitnetDecoderLib { +contract TestWitnetCBOR { - using WitnetDecoderLib for Witnet.CBOR; + using WitnetCBOR for WitnetCBOR.CBOR; event Log(string _topic, uint256 _value); event Log(string _topic, bytes _value); function testBoolDecode() external { - bool decodedFalse = WitnetDecoderLib.valueFromBytes(hex"f4").decodeBool(); - bool decodedTrue = WitnetDecoderLib.valueFromBytes(hex"f5").decodeBool(); + bool decodedFalse = WitnetCBOR.valueFromBytes(hex"f4").decodeBool(); + bool decodedTrue = WitnetCBOR.valueFromBytes(hex"f5").decodeBool(); Assert.equal( decodedFalse, false, - "CBOR-encoded false value should be decoded into a Witnet.CBOR containing the correct bool false value" + "CBOR-encoded false value should be decoded into a WitnetCBOR.CBOR containing the correct bool false value" ); Assert.equal( decodedTrue, true, - "CBOR-encoded true value should be decoded into a Witnet.CBOR containing the correct bool true value" + "CBOR-encoded true value should be decoded into a WitnetCBOR.CBOR containing the correct bool true value" ); } function helperDecodeBoolRevert() public pure { - WitnetDecoderLib.valueFromBytes(hex"f6").decodeBool(); + WitnetCBOR.valueFromBytes(hex"f6").decodeBool(); } function testBoolDecodeRevert() external { @@ -41,57 +41,57 @@ contract TestWitnetDecoderLib { } function testUint64DecodeDiscriminant() external { - Witnet.CBOR memory decoded = WitnetDecoderLib.valueFromBytes(hex"1b0020000000000000"); - Assert.equal(uint(decoded.majorType), 0, "CBOR-encoded Uint64 value should be decoded into a Witnet.CBOR with major type 0"); + WitnetCBOR.CBOR memory decoded = WitnetCBOR.valueFromBytes(hex"1b0020000000000000"); + Assert.equal(uint(decoded.majorType), 0, "CBOR-encoded Uint64 value should be decoded into a WitnetCBOR.CBOR with major type 0"); } function testUint64DecodeValue() external { - uint64 decoded = WitnetDecoderLib.valueFromBytes(hex"1b0020000000000000").decodeUint64(); + uint64 decoded = WitnetCBOR.valueFromBytes(hex"1b0020000000000000").decodeUint64(); Assert.equal( uint(decoded), 9007199254740992, - "CBOR-encoded Uint64 value should be decoded into a Witnet.CBOR containing the correct Uint64 value" + "CBOR-encoded Uint64 value should be decoded into a WitnetCBOR.CBOR containing the correct Uint64 value" ); } function testInt128DecodeDiscriminant() external { - Witnet.CBOR memory decoded = WitnetDecoderLib.valueFromBytes(hex"3bfffffffffffffffe"); - Assert.equal(uint(decoded.majorType), 1, "CBOR-encoded Int128 value should be decoded into a Witnet.CBOR with major type 1"); + WitnetCBOR.CBOR memory decoded = WitnetCBOR.valueFromBytes(hex"3bfffffffffffffffe"); + Assert.equal(uint(decoded.majorType), 1, "CBOR-encoded Int128 value should be decoded into a WitnetCBOR.CBOR with major type 1"); } function testInt128DecodeValue() external { - int128 decoded = WitnetDecoderLib.valueFromBytes(hex"3bfffffffffffffffe").decodeInt128(); + int128 decoded = WitnetCBOR.valueFromBytes(hex"3bfffffffffffffffe").decodeInt128(); Assert.equal( int(decoded), -18446744073709551615, - "CBOR-encoded Int128 value should be decoded into a Witnet.CBOR containing the correct Uint64 value" + "CBOR-encoded Int128 value should be decoded into a WitnetCBOR.CBOR containing the correct Uint64 value" ); } function testInt128DecodeZeroValue() external { - int128 decoded = WitnetDecoderLib.valueFromBytes(hex"00").decodeInt128(); - Assert.equal(int(decoded), 0, "CBOR-encoded Int128 value should be decoded into a Witnet.CBOR containing the correct Uint64 value"); + int128 decoded = WitnetCBOR.valueFromBytes(hex"00").decodeInt128(); + Assert.equal(int(decoded), 0, "CBOR-encoded Int128 value should be decoded into a WitnetCBOR.CBOR containing the correct Uint64 value"); } function testBytes0DecodeDiscriminant() external { - Witnet.CBOR memory decoded = WitnetDecoderLib.valueFromBytes(hex"40"); - Assert.equal(uint(decoded.majorType), 2, "Empty CBOR-encoded Bytes value should be decoded into a Witnet.CBOR with major type 2"); + WitnetCBOR.CBOR memory decoded = WitnetCBOR.valueFromBytes(hex"40"); + Assert.equal(uint(decoded.majorType), 2, "Empty CBOR-encoded Bytes value should be decoded into a WitnetCBOR.CBOR with major type 2"); } function testBytes0DecodeValue() external { bytes memory encoded = hex"40"; - bytes memory decoded = WitnetDecoderLib.valueFromBytes(encoded).decodeBytes(); - Assert.equal(decoded.length, 0, "Empty CBOR-encoded Bytes value should be decoded into an empty Witnet.CBOR containing an empty bytes value"); + bytes memory decoded = WitnetCBOR.valueFromBytes(encoded).decodeBytes(); + Assert.equal(decoded.length, 0, "Empty CBOR-encoded Bytes value should be decoded into an empty WitnetCBOR.CBOR containing an empty bytes value"); } function testBytes4BDecodeDiscriminant() external { - Witnet.CBOR memory decoded = WitnetDecoderLib.valueFromBytes(hex"4401020304"); - Assert.equal(uint(decoded.majorType), 2, "CBOR-encoded Bytes value should be decoded into a Witnet.CBOR with major type 2"); + WitnetCBOR.CBOR memory decoded = WitnetCBOR.valueFromBytes(hex"4401020304"); + Assert.equal(uint(decoded.majorType), 2, "CBOR-encoded Bytes value should be decoded into a WitnetCBOR.CBOR with major type 2"); } function testBytes4DecodeValue() external { bytes memory encoded = hex"4401020304"; - bytes memory decoded = WitnetDecoderLib.valueFromBytes(encoded).decodeBytes(); + bytes memory decoded = WitnetCBOR.valueFromBytes(encoded).decodeBytes(); bytes memory expected = abi.encodePacked( uint8(1), uint8(2), @@ -102,155 +102,155 @@ contract TestWitnetDecoderLib { Assert.equal( decoded[0], expected[0], - "CBOR-encoded Bytes value should be decoded into a Witnet.CBOR containing the correct Bytes value (error at item 0)" + "CBOR-encoded Bytes value should be decoded into a WitnetCBOR.CBOR containing the correct Bytes value (error at item 0)" ); Assert.equal( decoded[1], expected[1], - "CBOR-encoded Bytes value should be decoded into a Witnet.CBOR containing the correct Bytes value (error at item 1)" + "CBOR-encoded Bytes value should be decoded into a WitnetCBOR.CBOR containing the correct Bytes value (error at item 1)" ); Assert.equal( decoded[2], expected[2], - "CBOR-encoded Bytes value should be decoded into a Witnet.CBOR containing the correct Bytes value (error at item 2)" + "CBOR-encoded Bytes value should be decoded into a WitnetCBOR.CBOR containing the correct Bytes value (error at item 2)" ); Assert.equal( decoded[3], expected[3], - "CBOR-encoded Bytes value should be decoded into a Witnet.CBOR containing the correct Bytes value (error at item 3)" + "CBOR-encoded Bytes value should be decoded into a WitnetCBOR.CBOR containing the correct Bytes value (error at item 3)" ); } function testBytes32DecodeValueFrom31bytes() external { bytes memory encoded = hex"581f01020304050607080910111213141516171819202122232425262728293031"; - bytes32 decoded = WitnetDecoderLib.decodeBytes32(WitnetDecoderLib.valueFromBytes(encoded)); + bytes32 decoded = WitnetCBOR.decodeBytes32(WitnetCBOR.valueFromBytes(encoded)); bytes32 expected = 0x0102030405060708091011121314151617181920212223242526272829303100; Assert.equal(decoded, expected, "CBOR-encoded 31-byte array should be decoded into a bytes32 with right padded zeros"); } function testBytes32DecodeValueFrom32bytes() external { bytes memory encoded = hex"58200102030405060708091011121314151617181920212223242526272829303132"; - bytes32 decoded = WitnetDecoderLib.decodeBytes32(WitnetDecoderLib.valueFromBytes(encoded)); + bytes32 decoded = WitnetCBOR.decodeBytes32(WitnetCBOR.valueFromBytes(encoded)); bytes32 expected = 0x0102030405060708091011121314151617181920212223242526272829303132; Assert.equal(decoded, expected, "CBOR-encoded 32-byte array should be decoded into a bytes32"); } function testBytes32DecodeValueFrom33bytes() external { bytes memory encoded = hex"5821010203040506070809101112131415161718192021222324252627282930313233"; - bytes32 decoded = WitnetDecoderLib.decodeBytes32(WitnetDecoderLib.valueFromBytes(encoded)); + bytes32 decoded = WitnetCBOR.decodeBytes32(WitnetCBOR.valueFromBytes(encoded)); bytes32 expected = 0x0102030405060708091011121314151617181920212223242526272829303132; Assert.equal(decoded, expected, "CBOR-encoded 33-byte array should be decoded left-aligned into a bytes32"); } function testStringDecodeDiscriminant() external { - Witnet.CBOR memory decoded = WitnetDecoderLib.valueFromBytes(hex"6449455446"); - Assert.equal(uint(decoded.majorType), 3, "CBOR-encoded String value should be decoded into a Witnet.CBOR with major type 3"); + WitnetCBOR.CBOR memory decoded = WitnetCBOR.valueFromBytes(hex"6449455446"); + Assert.equal(uint(decoded.majorType), 3, "CBOR-encoded String value should be decoded into a WitnetCBOR.CBOR with major type 3"); } function testStringDecodeValue() external { bytes memory encoded = hex"6449455446"; - string memory decoded = WitnetDecoderLib.valueFromBytes(encoded).decodeString(); + string memory decoded = WitnetCBOR.valueFromBytes(encoded).decodeString(); string memory expected = "IETF"; - Assert.equal(decoded, expected, "CBOR-encoded String value should be decoded into a Witnet.CBOR containing the correct String value"); + Assert.equal(decoded, expected, "CBOR-encoded String value should be decoded into a WitnetCBOR.CBOR containing the correct String value"); } function testFloatDecodeDiscriminant() external { - Witnet.CBOR memory decoded = WitnetDecoderLib.valueFromBytes(hex"f90001"); + WitnetCBOR.CBOR memory decoded = WitnetCBOR.valueFromBytes(hex"f90001"); Assert.equal(uint(decoded.majorType), 7, "CBOR-encoded Float value should be decoded into a CBOR with major type 7"); } function testFloatDecodeSmallestSubnormal() external { bytes memory encoded = hex"f90001"; - int32 decoded = WitnetDecoderLib.valueFromBytes(encoded).decodeFixed16(); + int32 decoded = WitnetCBOR.valueFromBytes(encoded).decodeFixed16(); int32 expected = 0; - Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a Witnet.CBOR containing the correct Float value"); + Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value"); } function testFloatDecodeLargestSubnormal() external { bytes memory encoded = hex"f903ff"; - int32 decoded = WitnetDecoderLib.valueFromBytes(encoded).decodeFixed16(); + int32 decoded = WitnetCBOR.valueFromBytes(encoded).decodeFixed16(); int32 expected = 0; - Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a Witnet.CBOR containing the correct Float value"); + Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value"); } function testFloatDecodeSmallestPositiveNormal() external { bytes memory encoded = hex"f90400"; - int32 decoded = WitnetDecoderLib.valueFromBytes(encoded).decodeFixed16(); + int32 decoded = WitnetCBOR.valueFromBytes(encoded).decodeFixed16(); int32 expected = 0; - Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a Witnet.CBOR containing the correct Float value"); + Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value"); } function testFloatDecodeLargestNormal() external { bytes memory encoded = hex"f97bff"; - int32 decoded = WitnetDecoderLib.valueFromBytes(encoded).decodeFixed16(); + int32 decoded = WitnetCBOR.valueFromBytes(encoded).decodeFixed16(); int32 expected = 655040000; - Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a Witnet.CBOR containing the correct Float value"); + Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value"); } function testFloatDecodeLargestLessThanOne() external { bytes memory encoded = hex"f93bff"; - int32 decoded = WitnetDecoderLib.valueFromBytes(encoded).decodeFixed16(); + int32 decoded = WitnetCBOR.valueFromBytes(encoded).decodeFixed16(); int32 expected = 9995; - Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a Witnet.CBOR containing the correct Float value"); + Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value"); } function testFloatDecodeOne() external { bytes memory encoded = hex"f93c00"; - int32 decoded = WitnetDecoderLib.valueFromBytes(encoded).decodeFixed16(); + int32 decoded = WitnetCBOR.valueFromBytes(encoded).decodeFixed16(); int32 expected = 10000; - Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a Witnet.CBOR containing the correct Float value"); + Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value"); } function testFloatDecodeSmallestGreaterThanOne() external { bytes memory encoded = hex"f93c01"; - int32 decoded = WitnetDecoderLib.valueFromBytes(encoded).decodeFixed16(); + int32 decoded = WitnetCBOR.valueFromBytes(encoded).decodeFixed16(); int32 expected = 10009; - Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a Witnet.CBOR containing the correct Float value"); + Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value"); } function testFloatDecodeOneThird() external { bytes memory encoded = hex"f93555"; - int32 decoded = WitnetDecoderLib.valueFromBytes(encoded).decodeFixed16(); + int32 decoded = WitnetCBOR.valueFromBytes(encoded).decodeFixed16(); int32 expected = 3332; - Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a Witnet.CBOR containing the correct Float value"); + Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value"); } function testFloatDecodeMinusTwo() external { bytes memory encoded = hex"f9c000"; - int32 decoded = WitnetDecoderLib.valueFromBytes(encoded).decodeFixed16(); + int32 decoded = WitnetCBOR.valueFromBytes(encoded).decodeFixed16(); int32 expected = -20000; - Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a Witnet.CBOR containing the correct Float value"); + Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value"); } function testFloatDecodeZero() external { bytes memory encoded = hex"f90000"; - int32 decoded = WitnetDecoderLib.valueFromBytes(encoded).decodeFixed16(); + int32 decoded = WitnetCBOR.valueFromBytes(encoded).decodeFixed16(); int32 expected = 0; - Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a Witnet.CBOR containing the correct Float value"); + Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value"); } function testFloatDecodeMinusZero() external { bytes memory encoded = hex"f98000"; - int32 decoded = WitnetDecoderLib.valueFromBytes(encoded).decodeFixed16(); + int32 decoded = WitnetCBOR.valueFromBytes(encoded).decodeFixed16(); int32 expected = 0; - Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a Witnet.CBOR containing the correct Float value"); + Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value"); } function testUint64ArrayDecode() external { bytes memory encoded = hex"840102031a002fefd8"; - uint64[] memory decoded = WitnetDecoderLib.valueFromBytes(encoded).decodeUint64Array(); + uint64[] memory decoded = WitnetCBOR.valueFromBytes(encoded).decodeUint64Array(); uint64[4] memory expected = [ uint64(1), uint64(2), @@ -261,28 +261,28 @@ contract TestWitnetDecoderLib { Assert.equal( uint(decoded[0]), uint(expected[0]), - "CBOR-encoded Array of Uint64 values should be decoded into a Witnet.CBOR containing the correct Uint64 values (error at item 0)" + "CBOR-encoded Array of Uint64 values should be decoded into a WitnetCBOR.CBOR containing the correct Uint64 values (error at item 0)" ); Assert.equal( uint(decoded[1]), uint(expected[1]), - "CBOR-encoded Array of Uint64 values should be decoded into a Witnet.CBOR containing the correct Uint64 values (error at item 1)" + "CBOR-encoded Array of Uint64 values should be decoded into a WitnetCBOR.CBOR containing the correct Uint64 values (error at item 1)" ); Assert.equal( uint(decoded[2]), uint(expected[2]), - "CBOR-encoded Array of Uint64 values should be decoded into a Witnet.CBOR containing the correct Uint64 values (error at item 2)" + "CBOR-encoded Array of Uint64 values should be decoded into a WitnetCBOR.CBOR containing the correct Uint64 values (error at item 2)" ); Assert.equal( uint(decoded[3]), uint(expected[3]), - "CBOR-encoded Array of Uint64 values should be decoded into a Witnet.CBOR containing the correct Uint64 values (error at item 3)" + "CBOR-encoded Array of Uint64 values should be decoded into a WitnetCBOR.CBOR containing the correct Uint64 values (error at item 3)" ); } function testInt128ArrayDecode() external { bytes memory encoded = hex"840121033a002fefd7"; - int128[] memory decoded = WitnetDecoderLib.valueFromBytes(encoded).decodeInt128Array(); + int128[] memory decoded = WitnetCBOR.valueFromBytes(encoded).decodeInt128Array(); int128[4] memory expected = [ int128(1), int128(-2), @@ -293,28 +293,28 @@ contract TestWitnetDecoderLib { Assert.equal( decoded[0], expected[0], - "CBOR-encoded Array of Int128 values should be decoded into a Witnet.CBOR containing the correct Int128 values (error at item 0)" + "CBOR-encoded Array of Int128 values should be decoded into a WitnetCBOR.CBOR containing the correct Int128 values (error at item 0)" ); Assert.equal( decoded[1], expected[1], - "CBOR-encoded Array of Int128 values should be decoded into a Witnet.CBOR containing the correct Int128 values (error at item 1)" + "CBOR-encoded Array of Int128 values should be decoded into a WitnetCBOR.CBOR containing the correct Int128 values (error at item 1)" ); Assert.equal( decoded[2], expected[2], - "CBOR-encoded Array of Int128 values should be decoded into a Witnet.CBOR containing the correct Int128 values (error at item 2)" + "CBOR-encoded Array of Int128 values should be decoded into a WitnetCBOR.CBOR containing the correct Int128 values (error at item 2)" ); Assert.equal( decoded[3], expected[3], - "CBOR-encoded Array of Int128 values should be decoded into a Witnet.CBOR containing the correct Int128 values (error at item 3)" + "CBOR-encoded Array of Int128 values should be decoded into a WitnetCBOR.CBOR containing the correct Int128 values (error at item 3)" ); } function testFixed16ArrayDecode() external { bytes memory encoded = hex"84f93c80f9c080f94290f9C249"; - int32[] memory decoded = WitnetDecoderLib.valueFromBytes(encoded).decodeFixed16Array(); + int32[] memory decoded = WitnetCBOR.valueFromBytes(encoded).decodeFixed16Array(); int32[4] memory expected = [ int32(11250), int32(-22500), @@ -325,28 +325,28 @@ contract TestWitnetDecoderLib { Assert.equal( decoded[0], expected[0], - "CBOR-encoded Array of Fixed16 values should be decoded into a Witnet.CBOR containing the correct Int128 values (error at item 0)" + "CBOR-encoded Array of Fixed16 values should be decoded into a WitnetCBOR.CBOR containing the correct Int128 values (error at item 0)" ); Assert.equal( decoded[1], expected[1], - "CBOR-encoded Array of Fixed16 values should be decoded into a Witnet.CBOR containing the correct Int128 values (error at item 1)" + "CBOR-encoded Array of Fixed16 values should be decoded into a WitnetCBOR.CBOR containing the correct Int128 values (error at item 1)" ); Assert.equal( decoded[2], expected[2], - "CBOR-encoded Array of Fixed16 values should be decoded into a Witnet.CBOR containing the correct Int128 values (error at item 2)" + "CBOR-encoded Array of Fixed16 values should be decoded into a WitnetCBOR.CBOR containing the correct Int128 values (error at item 2)" ); Assert.equal( decoded[3], expected[3], - "CBOR-encoded Array of Fixed16 values should be decoded into a Witnet.CBOR containing the correct Int128 values (error at item 3)" + "CBOR-encoded Array of Fixed16 values should be decoded into a WitnetCBOR.CBOR containing the correct Int128 values (error at item 3)" ); } function testStringArrayDecode() external { bytes memory encoded = hex"846548656c6c6f6d646563656e7472616c697a656465776f726c646121"; - string[] memory decoded = WitnetDecoderLib.valueFromBytes(encoded).decodeStringArray(); + string[] memory decoded = WitnetCBOR.valueFromBytes(encoded).decodeStringArray(); string[4] memory expected = [ "Hello", "decentralized", @@ -357,22 +357,22 @@ contract TestWitnetDecoderLib { Assert.equal( decoded[0], expected[0], - "CBOR-encoded Array of String values should be decoded into a Witnet.CBOR containing the correct String values (error at item 0)" + "CBOR-encoded Array of String values should be decoded into a WitnetCBOR.CBOR containing the correct String values (error at item 0)" ); Assert.equal( decoded[1], expected[1], - "CBOR-encoded Array of String values should be decodrm -rf noed into a Witnet.CBOR containing the correct String values (error at item 1)" + "CBOR-encoded Array of String values should be decodrm -rf noed into a WitnetCBOR.CBOR containing the correct String values (error at item 1)" ); Assert.equal( decoded[2], expected[2], - "CBOR-encoded Array of String values should be decoded into a Witnet.CBOR containing the correct String values (error at item 2)" + "CBOR-encoded Array of String values should be decoded into a WitnetCBOR.CBOR containing the correct String values (error at item 2)" ); Assert.equal( decoded[3], expected[3], - "CBOR-encoded Array of String values should be decoded into a Witnet.CBOR containing the correct String values (error at item 3)" + "CBOR-encoded Array of String values should be decoded into a WitnetCBOR.CBOR containing the correct String values (error at item 3)" ); } } diff --git a/test/TestWitnetParserLib.sol b/test/TestWitnetLib.sol similarity index 61% rename from test/TestWitnetParserLib.sol rename to test/TestWitnetLib.sol index 027081f9c..a0fb58134 100644 --- a/test/TestWitnetParserLib.sol +++ b/test/TestWitnetLib.sol @@ -4,36 +4,36 @@ pragma solidity >=0.7.0 <0.9.0; pragma experimental ABIEncoderV2; import "truffle/Assert.sol"; -import "../contracts/libs/WitnetParserLib.sol"; +import "../contracts/libs/WitnetLib.sol"; -contract TestWitnetParserLib { +contract TestWitnetLib { - using WitnetParserLib for Witnet.Result; + using WitnetLib for Witnet.Result; - // Test the `WitnetParserLib.stageNames` pure method, which gives strings with the names for the different Witnet request + // Test the `WitnetLib.stageNames` pure method, which gives strings with the names for the different Witnet request // stages function testStageNames() external { - Assert.equal(WitnetParserLib.stageName(0), "retrieval", "Stage name for stage #1 should be \"retrieval\""); - Assert.equal(WitnetParserLib.stageName(1), "aggregation", "Stage name for stage #1 should be \"aggregation\""); - Assert.equal(WitnetParserLib.stageName(2), "tally", "Stage name for stage #1 should be \"tally\""); + Assert.equal(WitnetLib.stageName(0), "retrieval", "Stage name for stage #1 should be \"retrieval\""); + Assert.equal(WitnetLib.stageName(1), "aggregation", "Stage name for stage #1 should be \"aggregation\""); + Assert.equal(WitnetLib.stageName(2), "tally", "Stage name for stage #1 should be \"tally\""); } // Test decoding of `RadonError` error codes function testErrorCodes1() external { - Witnet.ErrorCodes errorCodeEmpty = WitnetParserLib.resultFromCborBytes(hex"D82780").asErrorCode(); - Witnet.ErrorCodes errorCode0x00 = WitnetParserLib.resultFromCborBytes(hex"D8278100").asErrorCode(); - Witnet.ErrorCodes errorCode0x01 = WitnetParserLib.resultFromCborBytes(hex"D8278101").asErrorCode(); - Witnet.ErrorCodes errorCode0x02 = WitnetParserLib.resultFromCborBytes(hex"D8278102").asErrorCode(); - Witnet.ErrorCodes errorCode0x03 = WitnetParserLib.resultFromCborBytes(hex"D8278103").asErrorCode(); - Witnet.ErrorCodes errorCode0x10 = WitnetParserLib.resultFromCborBytes(hex"D8278110").asErrorCode(); - Witnet.ErrorCodes errorCode0x11 = WitnetParserLib.resultFromCborBytes(hex"D8278111").asErrorCode(); - Witnet.ErrorCodes errorCode0x20 = WitnetParserLib.resultFromCborBytes(hex"D827811820").asErrorCode(); - Witnet.ErrorCodes errorCode0x30 = WitnetParserLib.resultFromCborBytes(hex"D827811830").asErrorCode(); - Witnet.ErrorCodes errorCode0x31 = WitnetParserLib.resultFromCborBytes(hex"D827811831").asErrorCode(); - Witnet.ErrorCodes errorCode0x40 = WitnetParserLib.resultFromCborBytes(hex"D827811840").asErrorCode(); - Witnet.ErrorCodes errorCode0x41 = WitnetParserLib.resultFromCborBytes(hex"D827811841").asErrorCode(); - Witnet.ErrorCodes errorCode0x42 = WitnetParserLib.resultFromCborBytes(hex"D827811842").asErrorCode(); + Witnet.ErrorCodes errorCodeEmpty = WitnetLib.resultFromCborBytes(hex"D82780").asErrorCode(); + Witnet.ErrorCodes errorCode0x00 = WitnetLib.resultFromCborBytes(hex"D8278100").asErrorCode(); + Witnet.ErrorCodes errorCode0x01 = WitnetLib.resultFromCborBytes(hex"D8278101").asErrorCode(); + Witnet.ErrorCodes errorCode0x02 = WitnetLib.resultFromCborBytes(hex"D8278102").asErrorCode(); + Witnet.ErrorCodes errorCode0x03 = WitnetLib.resultFromCborBytes(hex"D8278103").asErrorCode(); + Witnet.ErrorCodes errorCode0x10 = WitnetLib.resultFromCborBytes(hex"D8278110").asErrorCode(); + Witnet.ErrorCodes errorCode0x11 = WitnetLib.resultFromCborBytes(hex"D8278111").asErrorCode(); + Witnet.ErrorCodes errorCode0x20 = WitnetLib.resultFromCborBytes(hex"D827811820").asErrorCode(); + Witnet.ErrorCodes errorCode0x30 = WitnetLib.resultFromCborBytes(hex"D827811830").asErrorCode(); + Witnet.ErrorCodes errorCode0x31 = WitnetLib.resultFromCborBytes(hex"D827811831").asErrorCode(); + Witnet.ErrorCodes errorCode0x40 = WitnetLib.resultFromCborBytes(hex"D827811840").asErrorCode(); + Witnet.ErrorCodes errorCode0x41 = WitnetLib.resultFromCborBytes(hex"D827811841").asErrorCode(); + Witnet.ErrorCodes errorCode0x42 = WitnetLib.resultFromCborBytes(hex"D827811842").asErrorCode(); Assert.equal( uint(errorCodeEmpty), uint(Witnet.ErrorCodes.Unknown), @@ -102,17 +102,17 @@ contract TestWitnetParserLib { } function testErrorCodes2() external { - Witnet.ErrorCodes errorCode0x50 = WitnetParserLib.resultFromCborBytes(hex"D827811850").asErrorCode(); - Witnet.ErrorCodes errorCode0x51 = WitnetParserLib.resultFromCborBytes(hex"D827811851").asErrorCode(); - Witnet.ErrorCodes errorCode0x52 = WitnetParserLib.resultFromCborBytes(hex"D827811852").asErrorCode(); - Witnet.ErrorCodes errorCode0x53 = WitnetParserLib.resultFromCborBytes(hex"D827811853").asErrorCode(); - Witnet.ErrorCodes errorCode0x60 = WitnetParserLib.resultFromCborBytes(hex"D827811860").asErrorCode(); - Witnet.ErrorCodes errorCode0x70 = WitnetParserLib.resultFromCborBytes(hex"D827811870").asErrorCode(); - Witnet.ErrorCodes errorCode0x71 = WitnetParserLib.resultFromCborBytes(hex"D827811871").asErrorCode(); - Witnet.ErrorCodes errorCode0xE0 = WitnetParserLib.resultFromCborBytes(hex"D8278118E0").asErrorCode(); - Witnet.ErrorCodes errorCode0xE1 = WitnetParserLib.resultFromCborBytes(hex"D8278118E1").asErrorCode(); - Witnet.ErrorCodes errorCode0xE2 = WitnetParserLib.resultFromCborBytes(hex"D8278118E2").asErrorCode(); - Witnet.ErrorCodes errorCode0xFF = WitnetParserLib.resultFromCborBytes(hex"D8278118FF").asErrorCode(); + Witnet.ErrorCodes errorCode0x50 = WitnetLib.resultFromCborBytes(hex"D827811850").asErrorCode(); + Witnet.ErrorCodes errorCode0x51 = WitnetLib.resultFromCborBytes(hex"D827811851").asErrorCode(); + Witnet.ErrorCodes errorCode0x52 = WitnetLib.resultFromCborBytes(hex"D827811852").asErrorCode(); + Witnet.ErrorCodes errorCode0x53 = WitnetLib.resultFromCborBytes(hex"D827811853").asErrorCode(); + Witnet.ErrorCodes errorCode0x60 = WitnetLib.resultFromCborBytes(hex"D827811860").asErrorCode(); + Witnet.ErrorCodes errorCode0x70 = WitnetLib.resultFromCborBytes(hex"D827811870").asErrorCode(); + Witnet.ErrorCodes errorCode0x71 = WitnetLib.resultFromCborBytes(hex"D827811871").asErrorCode(); + Witnet.ErrorCodes errorCode0xE0 = WitnetLib.resultFromCborBytes(hex"D8278118E0").asErrorCode(); + Witnet.ErrorCodes errorCode0xE1 = WitnetLib.resultFromCborBytes(hex"D8278118E1").asErrorCode(); + Witnet.ErrorCodes errorCode0xE2 = WitnetLib.resultFromCborBytes(hex"D8278118E2").asErrorCode(); + Witnet.ErrorCodes errorCode0xFF = WitnetLib.resultFromCborBytes(hex"D8278118FF").asErrorCode(); Assert.equal( uint(errorCode0x50), uint(Witnet.ErrorCodes.NoReveals), @@ -172,20 +172,20 @@ contract TestWitnetParserLib { // Test decoding of `RadonError` error messages function testErrorMessages() external { - (, string memory errorMessageEmpty) = WitnetParserLib.resultFromCborBytes(hex"D82780").asErrorMessage(); - (, string memory errorMessage0x00) = WitnetParserLib.resultFromCborBytes(hex"D8278100").asErrorMessage(); - (, string memory errorMessage0x01) = WitnetParserLib.resultFromCborBytes(hex"D827820102").asErrorMessage(); - (, string memory errorMessage0x02) = WitnetParserLib.resultFromCborBytes(hex"D827820203").asErrorMessage(); - (, string memory errorMessage0x03) = WitnetParserLib.resultFromCborBytes(hex"D827820304").asErrorMessage(); - (, string memory errorMessage0x10) = WitnetParserLib.resultFromCborBytes(hex"D827821005").asErrorMessage(); - (, string memory errorMessage0x11) = WitnetParserLib.resultFromCborBytes(hex"D8278411000708").asErrorMessage(); - (, string memory errorMessage0x20) = WitnetParserLib.resultFromCborBytes(hex"D8278518200108090A").asErrorMessage(); - (, string memory errorMessage0x30) = WitnetParserLib.resultFromCborBytes(hex"D82783183008190141").asErrorMessage(); - (, string memory errorMessage0x31) = WitnetParserLib.resultFromCborBytes(hex"D82782183109").asErrorMessage(); - (, string memory errorMessage0x40) = WitnetParserLib.resultFromCborBytes(hex"D82785184002090A0B").asErrorMessage(); - (, string memory errorMessage0x41) = WitnetParserLib.resultFromCborBytes(hex"D827851841000A0B0C").asErrorMessage(); - (, string memory errorMessage0x42) = WitnetParserLib.resultFromCborBytes(hex"D827851842010B0C0D").asErrorMessage(); - (, string memory errorMessage0xFF) = WitnetParserLib.resultFromCborBytes(hex"D8278118FF").asErrorMessage(); + (, string memory errorMessageEmpty) = WitnetLib.resultFromCborBytes(hex"D82780").asErrorMessage(); + (, string memory errorMessage0x00) = WitnetLib.resultFromCborBytes(hex"D8278100").asErrorMessage(); + (, string memory errorMessage0x01) = WitnetLib.resultFromCborBytes(hex"D827820102").asErrorMessage(); + (, string memory errorMessage0x02) = WitnetLib.resultFromCborBytes(hex"D827820203").asErrorMessage(); + (, string memory errorMessage0x03) = WitnetLib.resultFromCborBytes(hex"D827820304").asErrorMessage(); + (, string memory errorMessage0x10) = WitnetLib.resultFromCborBytes(hex"D827821005").asErrorMessage(); + (, string memory errorMessage0x11) = WitnetLib.resultFromCborBytes(hex"D8278411000708").asErrorMessage(); + (, string memory errorMessage0x20) = WitnetLib.resultFromCborBytes(hex"D8278518200108090A").asErrorMessage(); + (, string memory errorMessage0x30) = WitnetLib.resultFromCborBytes(hex"D82783183008190141").asErrorMessage(); + (, string memory errorMessage0x31) = WitnetLib.resultFromCborBytes(hex"D82782183109").asErrorMessage(); + (, string memory errorMessage0x40) = WitnetLib.resultFromCborBytes(hex"D82785184002090A0B").asErrorMessage(); + (, string memory errorMessage0x41) = WitnetLib.resultFromCborBytes(hex"D827851841000A0B0C").asErrorMessage(); + (, string memory errorMessage0x42) = WitnetLib.resultFromCborBytes(hex"D827851842010B0C0D").asErrorMessage(); + (, string memory errorMessage0xFF) = WitnetLib.resultFromCborBytes(hex"D8278118FF").asErrorMessage(); Assert.equal( errorMessageEmpty, "Unknown error (no error code)", @@ -259,9 +259,9 @@ contract TestWitnetParserLib { } function testBridgeErrorMessages() external { - (, string memory errorMessage0xE0) = WitnetParserLib.resultFromCborBytes(hex"D8278118E0").asErrorMessage(); - (, string memory errorMessage0xE1) = WitnetParserLib.resultFromCborBytes(hex"D8278118E1").asErrorMessage(); - (, string memory errorMessage0xE2) = WitnetParserLib.resultFromCborBytes(hex"D8278118E2").asErrorMessage(); + (, string memory errorMessage0xE0) = WitnetLib.resultFromCborBytes(hex"D8278118E0").asErrorMessage(); + (, string memory errorMessage0xE1) = WitnetLib.resultFromCborBytes(hex"D8278118E1").asErrorMessage(); + (, string memory errorMessage0xE2) = WitnetLib.resultFromCborBytes(hex"D8278118E2").asErrorMessage(); Assert.equal( errorMessage0xE0, diff --git a/test/helpers/WitnetRequestBoardTestHelper.sol b/test/helpers/WitnetRequestBoardTestHelper.sol index 29301f712..94061fc42 100644 --- a/test/helpers/WitnetRequestBoardTestHelper.sol +++ b/test/helpers/WitnetRequestBoardTestHelper.sol @@ -3,7 +3,7 @@ pragma solidity >=0.7.0 <0.9.0; pragma experimental ABIEncoderV2; -import "../../contracts/impls/trustable/WitnetRequestBoardTrustableDefault.sol"; +import "../../contracts/impls/boards/trustable/WitnetRequestBoardTrustableDefault.sol"; /** * @title Witnet Requests Board Version 1 diff --git a/test/using_witnet.test.js b/test/using_witnet.test.js index c2442c176..5ad6106cd 100644 --- a/test/using_witnet.test.js +++ b/test/using_witnet.test.js @@ -4,7 +4,7 @@ const { expectRevert } = require("@openzeppelin/test-helpers") const WRB = artifacts.require(settings.artifacts.default.WitnetRequestBoard) const WRBProxy = artifacts.require(settings.artifacts.default.WitnetProxy) -const WitnetParser = artifacts.require(settings.artifacts.default.WitnetParserLib) +const WitnetLib = artifacts.require(settings.artifacts.default.WitnetLib) const UsingWitnetTestHelper = artifacts.require("UsingWitnetTestHelper") const WitnetRequest = artifacts.require("WitnetRequestTestHelper") @@ -26,7 +26,7 @@ contract("UsingWitnet", accounts => { const reporterAccount = accounts[1] before(async () => { - witnet = await WitnetParser.deployed() + witnet = await WitnetLib.deployed() if (!proxy) { // create one and only proxy contract: proxy = await WRBProxy.new({ from: ownerAccount }) @@ -46,7 +46,7 @@ contract("UsingWitnet", accounts => { // ...from owner account. { from: ownerAccount } ) - await UsingWitnetTestHelper.link(WitnetParser, witnet.address) + await UsingWitnetTestHelper.link(WitnetLib, witnet.address) clientContract = await UsingWitnetTestHelper.new(proxy.address) lastAccount1Balance = await web3.eth.getBalance(accounts[1]) }) @@ -179,7 +179,7 @@ contract("UsingWitnet", accounts => { let witnet, clientContract, wrb, proxy, request, requestId, result before(async () => { - witnet = await WitnetParser.deployed() + witnet = await WitnetLib.deployed() if (!proxy) { // create one and only proxy contract: proxy = await WRBProxy.new() @@ -197,7 +197,7 @@ contract("UsingWitnet", accounts => { { from: ownerAccount } ) } - await UsingWitnetTestHelper.link(WitnetParser, witnet.address) + await UsingWitnetTestHelper.link(WitnetLib, witnet.address) clientContract = await UsingWitnetTestHelper.new(wrb.address) }) diff --git a/test/wrb_proxy.test.js b/test/wrb_proxy.test.js index 7b57113d6..c13f0abaa 100644 --- a/test/wrb_proxy.test.js +++ b/test/wrb_proxy.test.js @@ -3,7 +3,7 @@ const settings = require("../migrations/witnet.settings") const { assert } = require("chai") const truffleAssert = require("truffle-assertions") -const WitnetParser = artifacts.require(settings.artifacts.default.WitnetParserLib) +const WitnetLib = artifacts.require(settings.artifacts.default.WitnetLib) const WitnetRequest = artifacts.require("WitnetRequestTestHelper") const WitnetRequestBoard = artifacts.require("WitnetRequestBoardTestHelper") @@ -24,8 +24,8 @@ contract("Witnet Requests Board Proxy", accounts => { let wrb before(async () => { - witnet = await WitnetParser.deployed() - await WitnetRequestBoard.link(WitnetParser, witnet.address) + witnet = await WitnetLib.deployed() + await WitnetRequestBoard.link(WitnetLib, witnet.address) wrbInstance1 = await WitnetRequestBoard.new([contractOwner], true) wrbInstance2 = await WitnetRequestBoard.new([contractOwner], true) wrbInstance3 = await WitnetRequestBoard.new([contractOwner], false) From b84d2a1100e39637890b2180ddddbb242b95cab3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Tue, 8 Nov 2022 13:26:54 +0100 Subject: [PATCH 002/119] chore: fmt! --- contracts/impls/WitnetUpgradableBase.sol | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/impls/WitnetUpgradableBase.sol b/contracts/impls/WitnetUpgradableBase.sol index d11515345..b722bb880 100644 --- a/contracts/impls/WitnetUpgradableBase.sol +++ b/contracts/impls/WitnetUpgradableBase.sol @@ -32,10 +32,10 @@ abstract contract WitnetUpgradableBase proxiableUUID = keccak256(bytes(_proxiableUUID)); } - receive() payable external virtual; + receive() external payable virtual; /// @dev Reverts if proxy delegatecalls to unexistent method. - fallback() payable external { + fallback() external payable { revert("WitnetUpgradableBase: not implemented"); } diff --git a/package.json b/package.json index dfbc1ab10..1b3858459 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "eslint-plugin-promise": "^6.1.1", "eth-gas-reporter": "0.2.25", "js-sha256": "0.9.0", - "solhint": "3.3.4", + "solhint": "3.3.7", "solidity-coverage": "0.7.16", "truffle": "5.6.2", "truffle-assertions": "0.9.2" From 69698381dc4bb9d92fead727b08221d9ca74ea18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Tue, 8 Nov 2022 19:03:32 +0100 Subject: [PATCH 003/119] test: fix Destructible tests --- README.md | 55 ++++++++++++++++++++++++++--------------------- test/wrb.test.js | 2 +- truffle-config.js | 2 +- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index a01f08039..6baeb4572 100644 --- a/README.md +++ b/README.md @@ -257,55 +257,62 @@ Please, have a look at the [`witnet/truffle-box`](https://github.com/witnet/truf ```bash ·------------------------------------------------------------------|---------------------------|-------------|----------------------------· -| Solc version: 0.8.11+commit.d7f03943 · Optimizer enabled: true · Runs: 200 · Block limit: 6718946 gas │ +| Solc version: 0.8.17+commit.8df45f5f · Optimizer enabled: true · Runs: 200 · Block limit: 6718946 gas │ ···································································|···························|·············|····························· | Methods │ ·······································|···························|·············|·············|·············|··············|·············· | Contract · Method · Min · Max · Avg · # calls · usd (avg) │ ·······································|···························|·············|·············|·············|··············|·············· -| WitnetPriceRouter · transferOwnership · - · - · 31826 · 1 · - │ +| WitnetPriceRouter · transferOwnership · - · - · 31550 · 1 · - │ ·······································|···························|·············|·············|·············|··············|·············· -| WitnetProxy · upgradeTo · - · - · 120348 · 1 · - │ +| WitnetProxy · upgradeTo · - · - · 129653 · 1 · - │ ·······································|···························|·············|·············|·············|··············|·············· -| WitnetRandomness · clone · - · - · 245198 · 3 · - │ +| WitnetRandomness · clone · - · - · 247765 · 7 · - │ ·······································|···························|·············|·············|·············|··············|·············· -| WitnetRequestBoardTrustableDefault · deleteQuery · - · - · 32760 · 5 · - │ +| WitnetRandomness · upgradeRandomizeFee · - · - · 28446 · 1 · - │ ·······································|···························|·············|·············|·············|··············|·············· -| WitnetRequestBoardTrustableDefault · destruct · - · - · 13593 · 2 · - │ +| WitnetRequestBoardTrustableBoba · deleteQuery · - · - · 59706 · 3 · - │ ·······································|···························|·············|·············|·············|··············|·············· -| WitnetRequestBoardTrustableDefault · initialize · - · - · 75083 · 32 · - │ +| WitnetRequestBoardTrustableBoba · postRequest · 196513 · 213613 · 205840 · 11 · - │ ·······································|···························|·············|·············|·············|··············|·············· -| WitnetRequestBoardTrustableDefault · postRequest · 144097 · 159097 · 158063 · 29 · - │ +| WitnetRequestBoardTrustableBoba · reportResult · 135354 · 135366 · 135361 · 5 · - │ ·······································|···························|·············|·············|·············|··············|·············· -| WitnetRequestBoardTrustableDefault · reportResult · - · - · 79316 · 14 · - │ +| WitnetRequestBoardTrustableDefault · deleteQuery · - · - · 51200 · 6 · - │ ·······································|···························|·············|·············|·············|··············|·············· -| WitnetRequestBoardTrustableDefault · upgradeReward · 31322 · 36505 · 34777 · 6 · - │ +| WitnetRequestBoardTrustableDefault · destruct · - · - · 28541 · 2 · - │ ·······································|···························|·············|·············|·············|··············|·············· -| WitnetRequestRandomness · clone · - · - · 244432 · 8 · - │ +| WitnetRequestBoardTrustableDefault · initialize · - · - · 83767 · 36 · - │ ·······································|···························|·············|·············|·············|··············|·············· -| WitnetRequestRandomness · renounceOwnership · - · - · 15530 · 2 · - │ +| WitnetRequestBoardTrustableDefault · postRequest · 163063 · 180163 · 175626 · 49 · - │ ·······································|···························|·············|·············|·············|··············|·············· -| WitnetRequestRandomness · setWitnessingCollateral · 72225 · 72994 · 72610 · 4 · - │ +| WitnetRequestBoardTrustableDefault · reportResult · - · - · 130017 · 17 · - │ ·······································|···························|·············|·············|·············|··············|·············· -| WitnetRequestRandomness · setWitnessingFees · 64087 · 74380 · 69234 · 4 · - │ +| WitnetRequestBoardTrustableDefault · reportResultBatch · 53532 · 328625 · 188967 · 8 · - │ ·······································|···························|·············|·············|·············|··············|·············· -| WitnetRequestRandomness · setWitnessingQuorum · 63945 · 65838 · 64892 · 4 · - │ +| WitnetRequestBoardTrustableDefault · upgradeReward · - · - · 38828 · 6 · - │ ·······································|···························|·············|·············|·············|··············|·············· -| WitnetRequestRandomness · transferOwnership · - · - · 31057 · 2 · - │ +| WitnetRequestRandomness · clone · - · - · 245099 · 9 · - │ +·······································|···························|·············|·············|·············|··············|·············· +| WitnetRequestRandomness · renounceOwnership · - · - · 23554 · 2 · - │ +·······································|···························|·············|·············|·············|··············|·············· +| WitnetRequestRandomness · setWitnessingCollateral · 69708 · 72377 · 71043 · 10 · - │ +·······································|···························|·············|·············|·············|··············|·············· +| WitnetRequestRandomness · setWitnessingFees · 64370 · 73741 · 69481 · 11 · - │ +·······································|···························|·············|·············|·············|··············|·············· +| WitnetRequestRandomness · setWitnessingQuorum · 64228 · 67999 · 66114 · 12 · - │ +·······································|···························|·············|·············|·············|··············|·············· +| WitnetRequestRandomness · transferOwnership · - · - · 28881 · 3 · - │ ·······································|···························|·············|·············|·············|··············|·············· | Deployments · · % of limit · │ ···································································|·············|·············|·············|··············|·············· -| WitnetCBOR · - · - · 2036059 · 30.3 % · - │ -···································································|·············|·············|·············|··············|·············· -| WitnetLib · - · - · 2627933 · 39.1 % · - │ +| WitnetLib · - · - · 3541780 · 52.7 % · - │ ···································································|·············|·············|·············|··············|·············· -| WitnetProxy · - · - · 574794 · 8.6 % · - │ +| WitnetProxy · - · - · 571986 · 8.5 % · - │ ···································································|·············|·············|·············|··············|·············· -| WitnetRequestBoardTrustableDefault · - · - · 3745708 · 55.7 % · - │ +| WitnetRequestBoardTrustableDefault · - · - · 4265164 · 63.5 % · - │ ···································································|·············|·············|·············|··············|·············· -| WitnetRequestRandomness · - · - · 1655828 · 24.6 % · - │ -·------------------------------------------------------------------|-------------|-------------|-------------|--------------|-------------· -``` +| WitnetRequestRandomness · - · - · 1643020 · 24.5 % · - │ +·------------------------------------------------------------------|-------------|-------------|-------------|--------------|-------------·``` ## **License** diff --git a/test/wrb.test.js b/test/wrb.test.js index d6a6cab4e..b6c41b0f3 100644 --- a/test/wrb.test.js +++ b/test/wrb.test.js @@ -737,7 +737,7 @@ contract("WitnetRequestBoard", ([ it("fails if trying to destruct from non owner address", async () => { await expectRevert( this.WitnetRequestBoard.destruct({ from: other }), - "only owner" + "not the owner" ) }) it("instance gets actually destructed", async () => { diff --git a/truffle-config.js b/truffle-config.js index 28f0500af..a928b8c33 100644 --- a/truffle-config.js +++ b/truffle-config.js @@ -31,7 +31,7 @@ module.exports = { excludeContracts: ["Migrations"], src: "contracts", }, - timeout: 100000, + timeout: 300000, useColors: true, }, } From 3d32780a82a197932c4bdb054874ddcdb9e09f7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 9 Nov 2022 18:05:48 +0100 Subject: [PATCH 004/119] chore: bump package version to 2.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1b3858459..aa876fd07 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "witnet-solidity-bridge", - "version": "0.6.1", + "version": "2.0.0", "description": "Witnet Solidity Bridge contracts for EVM-compatible chains", "main": "", "scripts": { From e162b242957e7b71ccea678e14a99704e9aa2a07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 9 Nov 2022 18:07:04 +0100 Subject: [PATCH 005/119] feat: first approach to libs/WitnetV2.sol --- contracts/libs/WitnetV2.sol | 99 +++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 contracts/libs/WitnetV2.sol diff --git a/contracts/libs/WitnetV2.sol b/contracts/libs/WitnetV2.sol new file mode 100644 index 000000000..da37415bd --- /dev/null +++ b/contracts/libs/WitnetV2.sol @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.0 <0.9.0; + +import "./Witnet.sol"; + +library WitnetV2 { + + error IndexOutOfBounds(uint256 index, uint256 range); + error InsufficientBalance(uint256 weiBalance, uint256 weiExpected); + error InsufficientFee(uint256 weiProvided, uint256 weiExpected); + error Unauthorized(address violator); + + + function toEpoch(uint _timestamp) internal pure returns (uint) { + return 1 + (_timestamp - 11111) / 15; + } + + function toTimestamp(uint _epoch) internal pure returns (uint) { + return 111111+ _epoch * 15; + } + + struct Beacon { + uint256 escrow; + uint256 evmBlock; + uint256 gasprice; + address relayer; + address slasher; + uint256 superblockIndex; + uint256 superblockRoot; + } + + enum BeaconStatus { + Idle + } + + struct Block { + bytes32 blockHash; + bytes32 drTxsRoot; + bytes32 drTallyTxsRoot; + } + + enum BlockStatus { + Idle + } + + struct DrSla { + uint64 witnessReward; + uint16 numwitnesses; + uint64 commitRevelFee; + uint32 minConsensusPercentage; + uint64 collateral; + } + + struct DrPost { + uint256 block; + DrPostStatus status; + DrPostRequest request; + DrPostResponse response; + } + + enum DrPostStatus { + Void, + Deleted, + Expired, + Posted, + Disputed, + Reported, + Finalized, + Accepted, + Rejected + } + + /// Data kept in EVM-storage for every Request posted to the Witnet Request Board. + struct DrPostRequest { + uint256 epoch; + address requester; + address reporter; + bytes32 radHash; + bytes32 slaHash; + uint256 weiReward; + } + + /// Data kept in EVM-storage containing Witnet-provided response metadata and result. + struct DrPostResponse { + address disputer; + address reporter; + uint256 escrowed; + uint256 drCommitTxEpoch; + uint256 drTallyTxEpoch; + bytes32 drTallyTxHash; + bytes drTallyResultBytes; + } + + enum Types { + Integer + } + +} \ No newline at end of file From f6bf459deaac00bfbf56e2e8b7dff3c61e648eaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 9 Nov 2022 18:08:03 +0100 Subject: [PATCH 006/119] feat: first approach to interfaces/V2 --- contracts/interfaces/V2/IWitnetBlocks.sol | 33 ++++++ contracts/interfaces/V2/IWitnetBytecodes.sol | 33 ++++++ contracts/interfaces/V2/IWitnetDecoder.sol | 107 ++++++++++++++++++ contracts/interfaces/V2/IWitnetEncoder.sol | 12 ++ contracts/interfaces/V2/IWitnetReporting1.sol | 40 +++++++ .../interfaces/V2/IWitnetReporting1Admin.sol | 13 +++ contracts/interfaces/V2/IWitnetRequests.sol | 67 +++++++++++ .../interfaces/V2/IWitnetRequestsAdmin.sol | 17 +++ contracts/interfaces/V2/IWitnetTraps.sol | 30 +++++ 9 files changed, 352 insertions(+) create mode 100644 contracts/interfaces/V2/IWitnetBlocks.sol create mode 100644 contracts/interfaces/V2/IWitnetBytecodes.sol create mode 100644 contracts/interfaces/V2/IWitnetDecoder.sol create mode 100644 contracts/interfaces/V2/IWitnetEncoder.sol create mode 100644 contracts/interfaces/V2/IWitnetReporting1.sol create mode 100644 contracts/interfaces/V2/IWitnetReporting1Admin.sol create mode 100644 contracts/interfaces/V2/IWitnetRequests.sol create mode 100644 contracts/interfaces/V2/IWitnetRequestsAdmin.sol create mode 100644 contracts/interfaces/V2/IWitnetTraps.sol diff --git a/contracts/interfaces/V2/IWitnetBlocks.sol b/contracts/interfaces/V2/IWitnetBlocks.sol new file mode 100644 index 000000000..07c4d75b7 --- /dev/null +++ b/contracts/interfaces/V2/IWitnetBlocks.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.0 <0.9.0; + +import "./IWitnetRequests.sol"; + +interface IWitnetBlocks { + + event Hooked(address indexed from); + event Rollup(address indexed from, uint256 index, uint256 prevIndex); + event Slashed(address indexed from, uint256 index, uint256 prevIndex); + + function getBlockDrTxsRoot(uint256 _witnetEpoch) external view returns (bytes32 _blockHash, bytes32 _txsRoot); + function getBlockDrTallyTxsRoot(uint256 _witnetEpoch) external view returns (bytes32 _blockHash, bytes32 _txsRoot); + function getBlockStatus(uint256 _witnetEpoch) external view returns (WitnetV2.BlockStatus); + + function getLastBeacon() external view returns (WitnetV2.Beacon memory); + function getLastBeaconIndex() external view returns (uint256); + function getLastBeaconStatus() external view returns (WitnetV2.BeaconStatus); + + function getNextBeaconEvmBlock() external view returns (uint256); + function getNextBeaconIndex() external view returns (uint256); + + function rollupNext() external payable; + function rollupForward() external payable; + + function setupForward() external payable; + + function verifyNext() external; + function verifyForward() external; + + function hook(IWitnetRequests) external; +} \ No newline at end of file diff --git a/contracts/interfaces/V2/IWitnetBytecodes.sol b/contracts/interfaces/V2/IWitnetBytecodes.sol new file mode 100644 index 000000000..44e4bc1dc --- /dev/null +++ b/contracts/interfaces/V2/IWitnetBytecodes.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.0 <0.9.0; + +import "../../libs/WitnetV2.sol"; + +interface IWitnetBytecodes { + + event DrQueryHash(bytes32 drQueryHash); + event DrRadonHash(bytes32 drRadonHash); + event DrRadonTemplateHash(bytes32 drRadonTemplateHash, WitnetV2.Types[] types); + event DrSlaHash(bytes32 drSlaHash); + + function bytecodeOf(bytes32 _drRadonHash) external view returns (bytes memory); + function bytecodeOf(bytes32 _drRadonHash, bytes32 _drSlaHash) external view returns (bytes memory); + function bytecodeOf(bytes32 _drRadonHash, bytes[] calldata _drArgs) external view returns (bytes memory); + function bytecodeOf(bytes32 _drRadonHash, bytes32 _drSlaHash, bytes[] calldata _drArgs) external view returns (bytes memory); + + function hashOf(bytes32 _drRadonHash, bytes32 _drSlaHash) external pure returns (bytes32); + function hashOf(bytes32 _drRadonHash, bytes32 _drSlaHash, bytes[] calldata _drArgs) external view returns (bytes32); + + function lookupDrSla(bytes32 _drSlaHash) external view returns (WitnetV2.DrSla memory); + function lookupDrSlaReward(bytes32 _drSlaHash) external view returns (uint256); + function lookupDrSources(bytes32 _drRadHash) external view returns (string[] memory); + function lookupDrInputTypes(bytes32 _drRadonHash) external view returns (WitnetV2.Types[] memory); + function lookupDrResultSize(bytes32 _drRadonHash) external view returns (uint256); + function lookupDrResultType(bytes32 _drRadonHash) external view returns (WitnetV2.Types); + + function verifyDrRadonBytes(bytes calldata _drRadonBytes) external returns (bytes32 _drRadonHash); + function verifyDrRadonTemplateBytes(bytes calldata _drRadonBytes, WitnetV2.Types[] calldata _types) external returns (bytes32 _drRadonHash); + function verifyDrSla(WitnetV2.DrSla calldata _drSla) external returns (bytes32 _drSlaHash); + +} \ No newline at end of file diff --git a/contracts/interfaces/V2/IWitnetDecoder.sol b/contracts/interfaces/V2/IWitnetDecoder.sol new file mode 100644 index 000000000..a051c498a --- /dev/null +++ b/contracts/interfaces/V2/IWitnetDecoder.sol @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.0 <0.9.0; + +import "../../libs/WitnetV2.sol"; + +/// @title The Witnet interface for decoding Witnet-provided request to Data Requests. +/// This interface exposes functions to check for the success/failure of +/// a Witnet-provided result, as well as to parse and convert result into +/// Solidity types suitable to the application level. +/// @author The Witnet Foundation. +interface IWitnetDecoder { + /// Decode raw CBOR bytes into a Witnet.Result instance. + /// @param _cborBytes Raw bytes representing a CBOR-encoded value. + /// @return A `Witnet.Result` instance. + function toWitnetResult(bytes memory _cborBytes) external pure returns (Witnet.Result memory); + + /// Tell if a Witnet.Result contains a successful result, or not. + /// @param _result An instance of Witnet.Result. + /// @return `true` if successful, `false` if errored. + function succeeded(Witnet.Result memory _result) external pure returns (bool); + + /// Decode a boolean value from a Witnet.Result as an `bool` value. + /// @param _result An instance of Witnet.Result. + /// @return The `bool` decoded from the Witnet.Result. + function toBool(Witnet.Result memory _result) external pure returns (bool); + + /// Decode a bytes value from a Witnet.Result as a `bytes` value. + /// @param _result An instance of Witnet.Result. + /// @return The `bytes` decoded from the Witnet.Result. + function toBytes(Witnet.Result memory _result) external pure returns (bytes memory); + + /// Decode a bytes value from a Witnet.Result as a `bytes32` value. + /// @param _result An instance of Witnet.Result. + /// @return The `bytes32` decoded from the Witnet.Result. + function toBytes32(Witnet.Result memory _result) external pure returns (bytes32); + + /// Decode a fixed16 (half-precision) numeric value from a Witnet.Result as an `int32` value. + /// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values. + /// by 5 decimal orders so as to get a fixed precision of 5 decimal positions, which should be OK for most `fixed16`. + /// use cases. In other words, the output of this method is 10,000 times the actual value, encoded into an `int32`. + /// @param _result An instance of Witnet.Result. + /// @return The `int128` decoded from the Witnet.Result. + function toInt32(Witnet.Result memory _result) external pure returns (int32); + + /// Decode an array of fixed16 values from a Witnet.Result as an `int128[]` value. + /// @param _result An instance of Witnet.Result. + /// @return The `int128[]` decoded from the Witnet.Result. + function toInt32Array(Witnet.Result memory _result) external pure returns (int32[] memory); + + /// Decode a integer numeric value from a Witnet.Result as an `int128` value. + /// @param _result An instance of Witnet.Result. + /// @return The `int128` decoded from the Witnet.Result. + function toInt128(Witnet.Result memory _result) external pure returns (int128); + + /// Decode an array of integer numeric values from a Witnet.Result as an `int128[]` value. + /// @param _result An instance of Witnet.Result. + /// @return The `int128[]` decoded from the Witnet.Result. + function toInt128Array(Witnet.Result memory _result) external pure returns (int128[] memory); + + /// Decode a string value from a Witnet.Result as a `string` value. + /// @param _result An instance of Witnet.Result. + /// @return The `string` decoded from the Witnet.Result. + function toString(Witnet.Result memory _result) external pure returns (string memory); + + /// Decode an array of string values from a Witnet.Result as a `string[]` value. + /// @param _result An instance of Witnet.Result. + /// @return The `string[]` decoded from the Witnet.Result. + function toStringArray(Witnet.Result memory _result) external pure returns (string[] memory); + + /// Decode a natural numeric value from a Witnet.Result as a `uint64` value. + /// @param _result An instance of Witnet.Result. + /// @return The `uint64` decoded from the Witnet.Result. + function toUint64(Witnet.Result memory _result) external pure returns(uint64); + + /// Decode an array of natural numeric values from a Witnet.Result as a `uint64[]` value. + /// @param _result An instance of Witnet.Result. + /// @return The `uint64[]` decoded from the Witnet.Result. + function toUint64Array(Witnet.Result memory _result) external pure returns (uint64[] memory); + + /// Decode an error code from a Witnet.Result as a member of `WitnetV2.ErrorCodes`. + /// @param _result An instance of `Witnet.Result`. + /// @return The `CBORValue.Error memory` decoded from the Witnet.Result. + function getErrorCode(Witnet.Result memory _result) external pure returns (Witnet.ErrorCodes); + + /// Generate a suitable error message for a member of `WitnetV2.ErrorCodes` and its corresponding arguments. + /// @dev WARN: Note that client contracts should wrap this function into a try-catch foreseing potential errors generated in this function + /// @param _result An instance of `Witnet.Result`. + /// @return A tuple containing the `CBORValue.Error memory` decoded from the `Witnet.Result`, plus a loggable error message. + function getErrorMessage(Witnet.Result memory _result) external pure returns (Witnet.ErrorCodes, string memory); + + /// Decode a raw error from a `Witnet.Result` as a `uint64[]`. + /// @param _result An instance of `Witnet.Result`. + /// @return The `uint64[]` raw error as decoded from the `Witnet.Result`. + function getRawError(Witnet.Result memory _result) external pure returns(uint64[] memory); + + function isArray(Witnet.Result memory _result) external pure returns (bool); + function getArrayLength(Witnet.Result memory _result) external pure returns (bool); + function getTypeAndSize(Witnet.Result memory _result) external pure returns (WitnetV2.Types, uint); + function getAddressAt(Witnet.Result memory _result, uint _indexes) external pure returns (address); + function getBoolAt(Witnet.Result memory _result, uint _indexes) external pure returns (bool); + function getBytesAt(Witnet.Result memory _result, uint _indexes) external pure returns (bytes memory); + function getInt256At(Witnet.Result memory _result, uint _indexes) external pure returns (int256); + function getStringAt(Witnet.Result memory _result, uint _indexes) external pure returns (string memory); + function getUint256At(Witnet.Result memory _result, uint _indexes) external pure returns (uint256); + function toAddress(Witnet.Result memory _result) external pure returns (address); +} diff --git a/contracts/interfaces/V2/IWitnetEncoder.sol b/contracts/interfaces/V2/IWitnetEncoder.sol new file mode 100644 index 000000000..5037d5f0c --- /dev/null +++ b/contracts/interfaces/V2/IWitnetEncoder.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.0 <0.9.0; + +interface IWitnetEncoder { + function encode(address) external pure returns (bytes memory); + function encode(bool) external pure returns (bytes memory); + function encode(bytes calldata) external pure returns (bytes memory); + function encode(int256) external pure returns (bytes memory); + function encode(uint256) external pure returns (bytes memory); + function encode(string calldata) external pure returns (bytes memory); +} diff --git a/contracts/interfaces/V2/IWitnetReporting1.sol b/contracts/interfaces/V2/IWitnetReporting1.sol new file mode 100644 index 000000000..a85c311cf --- /dev/null +++ b/contracts/interfaces/V2/IWitnetReporting1.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.7.0 <0.9.0; + +import "../../libs/WitnetV2.sol"; + +/// @title Witnet Request Board emitting events interface. +/// @author The Witnet Foundation. +interface IWitnetReporting1 { + + error AlreadySignedUp(address reporter); + + event DrPostAccepted(address indexed from, bytes32 drHash); + event DrPostRejected(address indexed from, bytes32 drHash, Witnet.ErrorCodes reason); + + event SignedUp(address indexed reporter, uint256 weiValue, uint256 totalReporters); + event SigningOut(address indexed reporter, uint256 weiValue, uint256 totalReporters); + event Slashed(address indexed reporter, uint256 weiValue, uint256 totalReporters); + + struct SignUpConfig { + uint256 weiRejectionFee; + uint256 weiSignUpFee; + uint256 acceptanceBlocks; + uint256 banningBlocks; + uint256 exitBlocks; + } + event SignUpConfigSet(address indexed from, SignUpConfig config); + + function getReportingAddressByIndex(uint256) external view returns (address); + function getReportingAddresses() external view returns (address[] memory); + function getReportingSignUpConfig() external view returns (SignUpConfig memory); + function isSignedUpReporter(address) external view returns (bool); + function totalSignedUpReporters() external view returns (uint256); + + function signUp() external payable returns (uint256 _index); + function signOut() external; + + function acceptDrPost(bytes32) external; + function rejectDrPost(bytes32, Witnet.ErrorCodes) external payable; +} \ No newline at end of file diff --git a/contracts/interfaces/V2/IWitnetReporting1Admin.sol b/contracts/interfaces/V2/IWitnetReporting1Admin.sol new file mode 100644 index 000000000..4999b4b72 --- /dev/null +++ b/contracts/interfaces/V2/IWitnetReporting1Admin.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.7.0 <0.9.0; + +import "./IWitnetReporting1.sol"; + +/// @title Witnet Request Board emitting events interface. +/// @author The Witnet Foundation. +interface IWitnetReporting1Admin { + + function setSignUpConfig(IWitnetReporting1.SignUpConfig calldata) external; + +} \ No newline at end of file diff --git a/contracts/interfaces/V2/IWitnetRequests.sol b/contracts/interfaces/V2/IWitnetRequests.sol new file mode 100644 index 000000000..f7910838e --- /dev/null +++ b/contracts/interfaces/V2/IWitnetRequests.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.0 <0.9.0; + +import "../../libs/WitnetV2.sol"; + +/// @title Witnet Request Board emitting events interface. +/// @author The Witnet Foundation. +interface IWitnetRequests { + + struct Stats { + uint256 totalDisputes; + uint256 totalPosts; + uint256 totalReports; + uint256 totalUpgrades; + } + + error DrPostBadDisputer(bytes32 drHash, address disputer); + error DrPostBadEpochs(bytes32 drHash, uint256 drPostEpoch, uint256 drCommitTxEpoch, uint256 drTallyTxEpoch); + error DrPostBadMood(bytes32 drHash, WitnetV2.DrPostStatus currentStatus); + error DrPostLowReward(bytes32 drHash, uint256 minBaseFee, uint256 weiValue); + error DrPostNotInStatus(bytes32 drHash, WitnetV2.DrPostStatus currentStatus, WitnetV2.DrPostStatus requiredStatus); + error DrPostOnlyRequester(bytes32 drHash, address requester); + error DrPostOnlyReporter(bytes32 drHash, address reporter); + + event DrPost(WitnetV2.DrPostRequest request); + event DrPostDeleted (address indexed from, bytes32 drHash); + event DrPostDisputed(address indexed from, bytes32 drHash); + event DrPostReported(address indexed from, bytes32 drHash); + event DrPostUpgraded(address indexed from, bytes32 drHash, uint256 weiReward); + event DrPostVerified(address indexed from, bytes32 drHash); + + function estimateBaseFee(bytes32 _drRadHash, uint256 _gasPrice, bytes32 _drSlaHash, uint256 _witPrice) external view returns (uint256); + function estimateReportFee(bytes32 _drRadHash, uint256 _gasPrice) external view returns (uint256); + + function getDrPost(bytes32 _drHash) external view returns (WitnetV2.DrPost memory); + function getDrPostEpoch(bytes32 _drHash) external view returns (uint256); + function getDrPostResponse(bytes32 _drHash) external view returns (WitnetV2.DrPostResponse memory); + function getDrPostStatus(bytes32 _drHash) external view returns (WitnetV2.DrPostStatus); + function readDrPostResultBytes(bytes32 _drHash) external view returns (bytes memory); + function serviceStats() external view returns (Stats memory); + function serviceTag() external view returns (bytes4); + + function postDr(bytes32 _drRadHash, bytes32 _drSlaHash, uint256 _witPrice) external payable returns (bytes32 _drHash); + + function deleteDrPost(bytes32 _drHash) external; + function deleteDrPostRequest(bytes32 _drHash) external; + function disputeDrPost(bytes32 _drHash) external payable; + function reportDrPost( + bytes32 _drHash, + uint256 _drCommitTxEpoch, + uint256 _drTallyTxEpoch, + bytes32 _drTallyTxHash, + bytes calldata _drTallyResultBytes + ) external payable; + function upgradeDrPostReward(bytes32 _drHash) external payable; + function verifyDrPost( + bytes32 _drHash, + uint256 _drCommitTxEpoch, + uint256 _drTallyTxEpoch, + uint256 _drTallyTxIndex, + bytes32 _blockDrTallyTxsRoot, + bytes32[] calldata _blockDrTallyTxHashes, + bytes calldata _drTallyTxBytes + ) external payable; + +} \ No newline at end of file diff --git a/contracts/interfaces/V2/IWitnetRequestsAdmin.sol b/contracts/interfaces/V2/IWitnetRequestsAdmin.sol new file mode 100644 index 000000000..0ff538b83 --- /dev/null +++ b/contracts/interfaces/V2/IWitnetRequestsAdmin.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.4 <0.9.0; + +/// @title Witnet Request Board emitting events interface. +/// @author The Witnet Foundation. +interface IWitnetRequestsAdmin { + + event SetBlocks(address indexed from, address contractAddr); + event SetBytecodes(address indexed from, address contractAddr); + event SetDecoder(address indexed from, address contractAddr); + + function setBlocks(address) external; + function setBytecodes(address) external; + function setDecoder(address) external; + +} \ No newline at end of file diff --git a/contracts/interfaces/V2/IWitnetTraps.sol b/contracts/interfaces/V2/IWitnetTraps.sol new file mode 100644 index 000000000..67042475c --- /dev/null +++ b/contracts/interfaces/V2/IWitnetTraps.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.0 <0.9.0; + +import "../../libs/WitnetV2.sol"; + +/// @title Witnet Request Board emitting events interface. +/// @author The Witnet Foundation. +interface IWitnetTraps { + + event Trap(address indexed from, bytes32 _trapHash); + event TrapIn(address indexed from, bytes32 _trapHash, uint256 _amount); + event TrapOut(address indexed from, bytes32 _trapHash); + + function getTrapBalanceOf(address from, bytes32 _trapHash) external view returns (uint256); + // function getTrapData(bytes32 _trapHash) external view returns (WitnetV2.TrapData memory); + // function getTrapStatus(bytes32 _trapHash) external view returns (WitnetV2.TrapStatus); + + function trap(bytes32 _drQueryHash, uint256 _pushInterval, uint256 _pushReward) external returns (bytes32 _trapHash); + function push(bytes32 _drQueryHash, bytes32 _drHash/*, **/) external payable; + function verifyPush(bytes32 _drQueryHash, bytes32 _drHash/*, **/) external payable; + + function trapIn(bytes32 _trapHash) external payable; + function trapOut(bytes32 _trapHash) external; + + function totalTraps() external view returns (uint256); + function totalPushes() external view returns (uint256); + function totalDisputes() external view returns (uint256); + +} \ No newline at end of file From 9a5f930a21d9be8f22486f54856d4b143d4753a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 9 Nov 2022 18:08:46 +0100 Subject: [PATCH 007/119] chore: ammedments to common WitnetUpgradableBase --- contracts/impls/WitnetUpgradableBase.sol | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/contracts/impls/WitnetUpgradableBase.sol b/contracts/impls/WitnetUpgradableBase.sol index b722bb880..f5559d3ac 100644 --- a/contracts/impls/WitnetUpgradableBase.sol +++ b/contracts/impls/WitnetUpgradableBase.sol @@ -21,6 +21,11 @@ abstract contract WitnetUpgradableBase { bytes32 internal immutable _WITNET_UPGRADABLE_VERSION; + error AlreadyInitialized(address implementation); + error NotCompliant(bytes4 interfaceId); + error NotUpgradable(address self); + error OnlyOwner(address owner); + constructor( bool _upgradable, bytes32 _versionTag, From 5a4fa006b7d8800196dde8d19347cfcce69e4cfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 9 Nov 2022 18:10:21 +0100 Subject: [PATCH 008/119] feat: first approach to WRBv2 --- contracts/WitnetRequestBoardV2.sol | 25 + contracts/data/WitnetRequestBoardV2Data.sol | 185 ++++++ .../WitnetRequestBoardTrustlessBase.sol | 587 ++++++++++++++++++ 3 files changed, 797 insertions(+) create mode 100644 contracts/WitnetRequestBoardV2.sol create mode 100644 contracts/data/WitnetRequestBoardV2Data.sol create mode 100644 contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol diff --git a/contracts/WitnetRequestBoardV2.sol b/contracts/WitnetRequestBoardV2.sol new file mode 100644 index 000000000..f8df0246a --- /dev/null +++ b/contracts/WitnetRequestBoardV2.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.0 <0.9.0; + +import "./interfaces/V2/IWitnetBlocks.sol"; +import "./interfaces/V2/IWitnetBytecodes.sol"; +import "./interfaces/V2/IWitnetDecoder.sol"; + +// import "./interfaces/V2/IWitnetReporting.sol"; +import "./interfaces/V2/IWitnetRequests.sol"; +//import "./interfaces/V2/IWitnetTraps.sol"; + +/// @title Witnet Request Board V2 functionality base contract. +/// @author The Witnet Foundation. +abstract contract WitnetRequestBoardV2 is + IWitnetRequests + //, IWitnetReporting + //, IWitnetTraps +{ + function blocks() virtual external view returns (IWitnetBlocks); + + function bytecodes() virtual external view returns (IWitnetBytecodes); + + function decoder() virtual external view returns (IWitnetDecoder); +} diff --git a/contracts/data/WitnetRequestBoardV2Data.sol b/contracts/data/WitnetRequestBoardV2Data.sol new file mode 100644 index 000000000..f28296b8c --- /dev/null +++ b/contracts/data/WitnetRequestBoardV2Data.sol @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.0 <0.9.0; + +import "@openzeppelin/contracts/utils/Strings.sol"; +import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; + +import "../interfaces/V2/IWitnetBlocks.sol"; +import "../interfaces/V2/IWitnetBytecodes.sol"; +import "../interfaces/V2/IWitnetDecoder.sol"; +import "../interfaces/V2/IWitnetRequests.sol"; +import "../interfaces/V2/IWitnetRequestsAdmin.sol"; +import "../patterns/Payable.sol"; + +/// @title Witnet Request Board base data model. +/// @author The Witnet Foundation. +abstract contract WitnetRequestBoardV2Data + is + ERC165, + Payable, + IWitnetRequestsAdmin +{ + using Strings for address; + using Strings for uint256; + + bytes32 internal constant _WITNET_REQUEST_BOARD_DATA_SLOTHASH = + /* keccak256("io.witnet.boards.data.v2") */ + 0xfeed002ff8a708dcba69bac2a8e829fd61fee551b9e9fc0317707d989cb0fe53; + + struct Board { + address base; + address owner; + address pendingOwner; + + IWitnetBlocks blocks; + IWitnetBytecodes bytecodes; + IWitnetDecoder decoder; + + IWitnetRequests.Stats serviceStats; + bytes4 serviceTag; + + mapping (bytes32 => WitnetV2.DrPost) posts; + } + + constructor() { + __board().owner = msg.sender; + } + + /// Asserts the given query is currently in the given status. + modifier drPostInStatus(bytes32 _drHash, WitnetV2.DrPostStatus _requiredStatus) { + WitnetV2.DrPostStatus _currentStatus = _getDrPostStatus(_drHash); + if (_currentStatus != _requiredStatus) { + revert IWitnetRequests.DrPostNotInStatus( + _drHash, + _currentStatus, + _requiredStatus + ); + } + _; + } + + /// Asserts the given query was previously posted and that it was not yet deleted. + modifier drPostNotDeleted(bytes32 _drHash) { + WitnetV2.DrPostStatus _currentStatus = __drPost(_drHash).status; + if (uint(_currentStatus) <= uint(WitnetV2.DrPostStatus.Deleted)) { + revert IWitnetRequests.DrPostBadMood(_drHash, _currentStatus); + } + _; + } + + + // ================================================================================================================ + // --- Internal functions ----------------------------------------------------------------------------------------- + + /// Returns storage pointer to contents of 'Board' struct. + function __board() + internal pure + returns (Board storage _ptr) + { + assembly { + _ptr.slot := _WITNET_REQUEST_BOARD_DATA_SLOTHASH + } + } + + function _canDrPostBeDeletedFrom(bytes32 _drHash, address _from) + internal view + virtual + returns (bool _itCanBeDeleted) + { + WitnetV2.DrPostStatus _currentStatus = _getDrPostStatus(_drHash); + if (_from == __drPostRequest(_drHash).requester) { + _itCanBeDeleted = ( + _currentStatus == WitnetV2.DrPostStatus.Finalized + || _currentStatus == WitnetV2.DrPostStatus.Expired + ); + } + } + + function __deleteDrPost(bytes32 _drHash) + internal + virtual + { + WitnetV2.DrPostRequest storage __request = __drPostRequest(_drHash); + uint _value = __request.weiReward; + address _to = __request.requester; + delete __board().posts[_drHash]; + if (address(this).balance < _value) { + revert WitnetV2.InsufficientBalance(address(this).balance, _value); + } + _safeTransferTo(payable(_to), _value); + } + + function __deleteDrPostRequest(bytes32 _drHash) + internal + virtual + { + delete __drPost(_drHash).request; + } + + function _getDrPostBlock(bytes32 _drHash) + internal view + virtual + returns (uint256) + { + return __drPost(_drHash).block; + } + + /// Gets current status of given query. + function _getDrPostStatus(bytes32 _drHash) + internal view + virtual + returns (WitnetV2.DrPostStatus _temporaryStatus) + { + uint256 _drPostBlock = _getDrPostBlock(_drHash); + _temporaryStatus = __drPost(_drHash).status; + if ( + _temporaryStatus == WitnetV2.DrPostStatus.Reported + || _temporaryStatus == WitnetV2.DrPostStatus.Disputed + || _temporaryStatus == WitnetV2.DrPostStatus.Accepted + ) { + if (block.number > _drPostBlock + 256 /* TODO: __drPostExpirationBlocks */) { + _temporaryStatus = WitnetV2.DrPostStatus.Expired; + } + } + } + + // /// Gets from of a given query. + function __drPost(bytes32 _drHash) + internal view + returns (WitnetV2.DrPost storage) + { + return __board().posts[_drHash]; + } + + /// Gets the WitnetV2.DrPostRequest part of a given post. + function __drPostRequest(bytes32 _drHash) + internal view + returns (WitnetV2.DrPostRequest storage) + { + return __board().posts[_drHash].request; + } + + /// Gets the Witnet.Result part of a given post. + function __drPostResponse(bytes32 _drHash) + internal view + returns (WitnetV2.DrPostResponse storage) + { + return __board().posts[_drHash].response; + } + + function __setServiceTag() + internal + virtual + returns (bytes4 _serviceTag) + { + _serviceTag = bytes4(keccak256(abi.encodePacked( + "evm::", + block.chainid.toString(), + "::", + address(this).toHexString() + ))); + __board().serviceTag = _serviceTag; + } + +} \ No newline at end of file diff --git a/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol b/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol new file mode 100644 index 000000000..a96efde52 --- /dev/null +++ b/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol @@ -0,0 +1,587 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.4 <0.9.0; + +import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; + +import "../../WitnetUpgradableBase.sol"; +import "../../../WitnetRequestBoardV2.sol"; +import "../../../data/WitnetRequestBoardV2Data.sol"; + +/// @title Witnet Request Board "trustless" base implementation contract. +/// @notice Contract to bridge requests to Witnet Decentralized Oracle Network. +/// @dev This contract enables posting requests that Witnet bridges will insert into the Witnet network. +/// The result of the requests will be posted back to this contract by the bridge nodes too. +/// @author The Witnet Foundation +abstract contract WitnetRequestBoardTrustlessBase + is + WitnetUpgradableBase, + WitnetRequestBoardV2, + WitnetRequestBoardV2Data +{ + using ERC165Checker for address; + using WitnetV2 for bytes; + using WitnetV2 for uint256; + + modifier onlyDrPostReporter(bytes32 _drHash) { + address _expectedReporter = __drPostRequest(_drHash).reporter; + if ( + _msgSender() != _expectedReporter + && _expectedReporter != address(0) + ) { + revert IWitnetRequests.DrPostOnlyReporter( + _drHash, + _expectedReporter + ); + } + _; + } + + modifier onlyDrPostRequester(bytes32 _drHash) { + WitnetV2.DrPostRequest storage __post = __drPostRequest(_drHash); + address _requester = __drPostRequest(_drHash).requester; + if (_msgSender() != _requester) { + revert IWitnetRequests.DrPostOnlyRequester(_drHash, _requester); + } + _; + } + + constructor( + bool _upgradable, + bytes32 _versionTag + ) + WitnetUpgradableBase( + _upgradable, + _versionTag, + "io.witnet.proxiable.boards.v2" + ) + {} + + // ================================================================================================================ + // --- Overrides IERC165 interface -------------------------------------------------------------------------------- + + /// @dev See {IERC165-supportsInterface}. + function supportsInterface(bytes4 _interfaceId) + public view + virtual override(WitnetUpgradableBase, ERC165) + returns (bool) + { + return _interfaceId == type(WitnetRequestBoardV2).interfaceId + || _interfaceId == type(IWitnetRequestsAdmin).interfaceId + || super.supportsInterface(_interfaceId); + } + + + // ================================================================================================================ + // --- Internal virtual methods ----------------------------------------------------------------------------------- + + function _cancelDeadline(uint256 _postEpoch) internal view virtual returns (uint256); + function _reportDeadlineEpoch(uint256 _postEpoch) internal view virtual returns (uint256); + function _selectReporter(bytes32 _drHash) internal virtual view returns (address); + + + // ================================================================================================================ + // --- Overrides 'Ownable2Step' ----------------------------------------------------------------------------------- + + /// Returns the address of the pending owner. + function pendingOwner() + public view + virtual override + returns (address) + { + return __board().pendingOwner; + } + + /// Returns the address of the current owner. + function owner() + public view + virtual override + returns (address) + { + return __board().owner; + } + + /// Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one. + /// @dev Can only be called by the current owner. + function transferOwnership(address _newOwner) + public + virtual override + onlyOwner + { + __board().pendingOwner = _newOwner; + emit OwnershipTransferStarted(owner(), _newOwner); + } + + /// @dev Transfers ownership of the contract to a new account (`_newOwner`) and deletes any pending owner. + /// @dev Internal function without access restriction. + function _transferOwnership(address _newOwner) + internal + virtual override + { + delete __board().pendingOwner; + address _oldOwner = owner(); + if (_newOwner != _oldOwner) { + __board().owner = _newOwner; + emit OwnershipTransferred(_oldOwner, _newOwner); + } + } + + + // ================================================================================================================ + // --- Overrides 'Payable' ---------------------------------------------------------------------------------------- + + /// Gets current transaction price. + function _getGasPrice() + internal view + virtual override + returns (uint256) + { + return tx.gasprice; + } + + /// Gets current payment value. + function _getMsgValue() + internal view + virtual override + returns (uint256) + { + return msg.value; + } + + /// Transfers ETHs to given address. + /// @param _to Recipient address. + /// @param _amount Amount of ETHs to transfer. + function _safeTransferTo(address payable _to, uint256 _amount) + internal + virtual override + nonReentrant + { + payable(_to).transfer(_amount); + } + + + // ================================================================================================================ + // --- Overrides 'Upgradable' ------------------------------------------------------------------------------------- + + /// Initialize storage-context when invoked as delegatecall. + /// @dev Must fail when trying to initialize same instance more than once. + function initialize(bytes memory _initData) + public + virtual override + { + address _owner = __board().owner; + if (_owner == address(0)) { + // set owner if none set yet + _owner = msg.sender; + __board().owner = _owner; + } else { + // only owner can initialize: + if (msg.sender != _owner) revert WitnetUpgradableBase.OnlyOwner(_owner); + } + + if (__board().serviceTag == bytes4(0)) { + __setServiceTag(); + } + + if (__board().base != address(0)) { + // current implementation cannot be initialized more than once: + if(__board().base == base()) revert WitnetUpgradableBase.AlreadyInitialized(base()); + } + __board().base = base(); + + emit Upgraded(msg.sender, base(), codehash(), version()); + + // Parse optional input addresses array: + address[] memory _refs = abi.decode(_initData, (address[])); + if (_refs.length > 0 && _refs[0] != address(0)) setBlocks(_refs[0]); + if (_refs.length > 1 && _refs[1] != address(0)) setBytecodes(_refs[1]); + if (_refs.length > 2 && _refs[2] != address(0)) setDecoder(_refs[2]); + + // All complying references must be provided: + if (address(__board().blocks) == address(0)) { + revert WitnetUpgradableBase.NotCompliant(type(IWitnetBlocks).interfaceId); + } else if (address(__board().bytecodes) == address(0)) { + revert WitnetUpgradableBase.NotCompliant(type(IWitnetBytecodes).interfaceId); + } else if (address(__board().decoder) == address(0)) { + revert WitnetUpgradableBase.NotCompliant(type(IWitnetDecoder).interfaceId); + } + + // Set deliveryTag if not done yet: + if (__board().serviceTag == bytes4(0)) { + __setServiceTag(); + } + } + + /// Tells whether provided address could eventually upgrade the contract. + function isUpgradableFrom(address _from) external view override returns (bool) { + address _owner = __board().owner; + return ( + // false if the WRB is intrinsically not upgradable, or `_from` is no owner + isUpgradable() + && _owner == _from + ); + } + + + // ================================================================================================================ + // --- Base implementation of 'IWitnetRequestsAdmin' -------------------------------------------------------------- + + function setBlocks(address _contractAddr) + public + virtual override + onlyOwner + { + if (isUpgradable()) { + if (!_contractAddr.supportsInterface(type(IWitnetBlocks).interfaceId)) { + revert WitnetUpgradableBase.NotCompliant(type(IWitnetBlocks).interfaceId); + } + __board().blocks = IWitnetBlocks(_contractAddr); + emit SetBlocks(_msgSender(), _contractAddr); + } else { + revert WitnetUpgradableBase.NotUpgradable(address(this)); + } + } + + function setBytecodes(address _contractAddr) + public + virtual override + onlyOwner + { + if (!_contractAddr.supportsInterface(type(IWitnetBytecodes).interfaceId)) { + revert WitnetUpgradableBase.NotCompliant(type(IWitnetBytecodes).interfaceId); + } + __board().bytecodes = IWitnetBytecodes(_contractAddr); + emit SetBytecodes(_msgSender(), _contractAddr); + } + + function setDecoder(address _contractAddr) + public + virtual override + onlyOwner + { + if (!_contractAddr.supportsInterface(type(IWitnetDecoder).interfaceId)) { + revert WitnetUpgradableBase.NotCompliant(type(IWitnetDecoder).interfaceId); + } + __board().decoder = IWitnetDecoder(_contractAddr); + emit SetDecoder(_msgSender(), _contractAddr); + } + + + // ================================================================================================================ + // --- Base implementation of read-only methods in 'IWitnetRequests' ---------------------------------------------- + + function estimateBaseFee( + bytes32 _drRadHash, + uint256 _gasPrice, + bytes32 _drSlaHash, + uint256 _witPrice + ) + public view + override + returns (uint256) + { + return ( + estimateReportFee(_drRadHash, _gasPrice) + + __board().bytecodes.lookupDrSlaReward(_drSlaHash) * _witPrice + ); + } + + function estimateReportFee(bytes32 _drRadHash, uint256 _gasPrice) + public view + virtual + returns (uint256); + + function getDrPost(bytes32 _drHash) + public view + virtual override + returns (WitnetV2.DrPost memory) + { + return __board().posts[_drHash]; + } + + function getDrPostEpoch(bytes32 _drHash) + public view + virtual override + returns (uint256) + { + return __drPost(_drHash).request.epoch; + } + + function getDrPostResponse(bytes32 _drHash) + public view + virtual override + returns (WitnetV2.DrPostResponse memory) + { + return __drPostResponse(_drHash); + } + + function getDrPostStatus(bytes32 _drHash) + public view + virtual override + returns (WitnetV2.DrPostStatus) + { + return _getDrPostStatus(_drHash); + } + + function readDrPostResultBytes(bytes32 _drHash) + public view + virtual override + drPostNotDeleted(_drHash) + returns (bytes memory) + { + return __drPostResponse(_drHash).drTallyResultBytes; + } + + function serviceStats() + public view + virtual override + returns (IWitnetRequests.Stats memory) + { + return __board().serviceStats; + } + + function serviceTag() + public view + virtual override + returns (bytes4) + { + return __board().serviceTag; + } + + + // ================================================================================================================ + // --- Base implementation of state-modifying methods in 'IWitnetRequests' ---------------------------------------- + + function deleteDrPost(bytes32 _drHash) + external + virtual override + { + if (!_canDrPostBeDeletedFrom(_drHash, _msgSender())){ + revert WitnetV2.Unauthorized(_msgSender()); + } + __deleteDrPost(_drHash); + emit DrPostDeleted(_msgSender(), _drHash); + } + + function deleteDrPostRequest(bytes32 _drHash) + public + virtual override + { + if (!_canDrPostBeDeletedFrom(_drHash, _msgSender())){ + revert WitnetV2.Unauthorized(_msgSender()); + } + __deleteDrPostRequest(_drHash); + } + + function disputeDrPost(bytes32 _drHash) + external payable + virtual override + // stakes(_disputeStake(), _disputeDeadlineBlock()) + { + // TODO + // WitnetV2.DrPostStatus _currentStatus = _getDrPostStatus(_drHash); + // WitnetV2.DrPostResponse storage __response = __drPostResponse(_drHash); + // if ( + // _currentStatus == WitnetV2.DrPostStatus.Posted + // || _currentStatus = WitnetV2.DrPostStatus.Reported + // ) { + // if (__response.reporter != address(0)) { + // if (_msgSender() == __response.reporter) { + // revert IWitnetRequests.DrPostBadDisputer( + // _drHash, + // _msgSender() + // ); + // } + // } + // // TODO: check escrow value + // __response = WitnetV2.DrPostResponse({ + // disputer: _msgSender(), + // reporter: __response.reporter, + // escrowed: _getMsgValue(), + // drCommitTxEpoch: 0, + // drTallyTxEpoch: 0, + // drTallyTxHash: bytes32(0), + // cborBytes: bytes("") // Witnet.Precompiled.NoWitnetResponse + // }); + // emit DrPostDisputed(_msgSender(), _drHash); + // } + // else { + // revert IWitnetRequests.DrPostBadMood( + // _drHash, + // _currentStatus + // ); + // } + // __board.serviceStats.totalDisputes ++; + } + + function postDr( + bytes32 _drRadHash, + bytes32 _drSlaHash, + uint256 _weiWitPrice + ) + external payable + returns (bytes32 _drHash) + { + // TODO + // // Calculate current epoch in Witnet terms: + // uint256 _currentEpoch = block.timestamp.toEpoch(); + + // // Calculate data request delivery tag: + // bytes8 _drDeliveryTag = bytes8(keccak256(abi.encode( + // _msgSender(), + // _drRadHash, + // _drSlaHash, + // _currentEpoch, + // ++ __board().serviceStats.totalPosts + // ))); + // _drDeliveryTag |= bytes8(serviceTag()); + + // // Calculate data request post hash: + // _drHash = WitnetV2.hash(abi.encodePacked( + // _drRadHash, + // _drSlaHash, + // _drDeliveryTag + // )); + + // // Check minimum base fee is covered: + // uint256 _minBaseFee = estimateBaseFee( + // _drRadHash, + // _getGasPrice(), + // _drSlaHash, + // _weiWitPrice + // ); + // if (_getMsgValue() < _minBaseFee) { + // revert IWitnetRequests.DrPostLowReward(_drHash, _minBaseFee, _getMsgValue()); + // } + + // // Save DrPost in storage: + // __drPostRequest(_drHash) = WitnetV2.DrPostRequest({ + // epoch: _currentEpoch, + // from: _msgSender(), + // to: _selectReporter(), + // radHash: _drRadHash, + // slaHash: _drSlaHash, + // weiReward: _getMsgValue() + // }); + // emit DrPost(__drPostRequest(_drHash)); + } + + function reportDrPost( + bytes32 _drHash, + uint256 _drCommitTxEpoch, + uint256 _drTallyTxEpoch, + bytes32 _drTallyTxHash, + bytes calldata _drTallyResultBytes + ) + external payable + virtual override + drPostInStatus(_drHash, WitnetV2.DrPostStatus.Posted) + // stakes(_disputeStake(), _disputeDeadlineBlock()) + { + // TODO + // address _disputer = address(0); + // uint256 _currentEpoch = block.timestamp.toEpoch(); + // uint256 _drPostEpoch = _getDrPostEpoch(_drHash); + // if (_currentEpoch <= _reportDeadlineEpoch(_drHash)) { + // if (_msgSender() != __drPostRequest(_drHash).to) { + // revert DrPostOnlyReporter(__drPostRequest(_drHash).to); + // } + // } else { + // _disputer = _msgSender(); + // } + // if ( + // _drCommitTxEpoch <= _drPostEpoch + // || _drTallyTxEpoch <= _drCommitTxEpoch + // ) { + // revert DrPostBadEpochs( + // _drHash, + // _drPostEpoch, + // _drCommitTxEpoch, + // _drTallyTxEpoch + // ); + // } + // __drPostResponse(_drHash) = WitnetV2.DrPostResponse({ + // disputer: _disputer, + // reporter: _disputer == address(0) ? _msgSender() : address(0), + // escrowed: _getMsgValue(), + // drCommitTxEpoch: _drCommitTxEpoch, + // drTallyTxEpoch: _drTallyTxEpoch, + // drTallyTxHash: _drTallyTxHash, + // drTallyResultBytes: _drTallyResultBytes + // }); + // __board().serviceStats.totalReports ++; + } + + function verifyDrPost( + bytes32 _drHash, + uint256 _drCommitTxEpoch, + uint256 _drTallyTxEpoch, + uint256 _drTallyTxIndex, + bytes32 _blockDrTallyTxsRoot, + bytes32[] calldata _blockDrTallyTxHashes, + bytes calldata _drTallyTxBytes + ) + external payable + virtual override + { + // TODO + // WitnetV2.DrPostStatus _currentStatus = _getDrPostStatus(_drHash); + // WitnetV2.DrPostResponse storage __response = __drPostResponse(_drHash); + // address _bannedSender = _currentStatus == WitnetV2.DrPostStatus.Reported + // ? __response.reporter + // : _currentStatus == WitnetV2.DrPostStatus.Disputed + // ? __response.disputer + // : address(0) + // ; + // if (_bannedSender == address(0)) { + // revert IWitnetRequests.DrPostBadMood( + // _drHash, + // _currentStatus + // ); + // } else if (_msgSender() == _bannedSender) { + // revert IWitnetRequests.DrPostBadDisputer( + // _drHash, + // _msgSender() + // ); + // } else { + // bytes memory _drTallyTxResult; + // // TODO: _drTallyTxResult = _verifyDrPost(...); + // __response = WitnetV2.DrPostResponse({ + // disputer: _currentStatus == WitnetV2.DrPostStatus.Reported ? _msgSender() : __response.disputer, + // reporter: _currentStatus == WitnetV2.DrPostStatus.Disputed ? _msgSender() : __response.reporter, + // escrowed: __response.escrowed, + // drCommitTxEpoch: _drCommitTxEpoch, + // drTallyTxEpoch: _drTallyTxEpoch, + // drTallyTxHash: _blockDrTallyTxHashes[_drTallyTxIndex], + // drTallyTxResult: _drTallyTxResult + // }); + // emit DrPostVerified( + // _msgSender(), + // _drHash + // ); + // if (_currentStatus == WitnetV2.DrPostStatus.Reported) { + // __board().serviceStats.totalDisputes ++; + // } else { + // __board().serviceStats.totalReports ++; + // } + // } + // // TODO: __contextSlash(_bannedSender); + } + + function upgradeDrPostReward(bytes32 _drHash) + public payable + virtual override + drPostInStatus(_drHash, WitnetV2.DrPostStatus.Posted) + { + if (_getMsgValue() > 0) { + __drPostRequest(_drHash).weiReward += _getMsgValue(); + emit DrPostUpgraded( + _msgSender(), + _drHash, + __drPostRequest(_drHash).weiReward + ); + __board().serviceStats.totalUpgrades ++; + } + } + +} \ No newline at end of file From f1cc5cbf3604b5dbc37126df678a182e0931c5c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 9 Nov 2022 18:11:52 +0100 Subject: [PATCH 009/119] feat: first approach to evm-interactive SoFIR --- contracts/data/WitnetReporting1Data.sol | 102 +++++++ .../WitnetRequestBoardTrustlessReporting1.sol | 270 ++++++++++++++++++ 2 files changed, 372 insertions(+) create mode 100644 contracts/data/WitnetReporting1Data.sol create mode 100644 contracts/impls/boards/trustless/WitnetRequestBoardTrustlessReporting1.sol diff --git a/contracts/data/WitnetReporting1Data.sol b/contracts/data/WitnetReporting1Data.sol new file mode 100644 index 000000000..45b384fd0 --- /dev/null +++ b/contracts/data/WitnetReporting1Data.sol @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.0 <0.9.0; + +import "../interfaces/V2/IWitnetReporting1.sol"; +import "../interfaces/V2/IWitnetReporting1Admin.sol"; + +/// @title Witnet Request Board base data model. +/// @author The Witnet Foundation. +abstract contract WitnetReporting1Data + is + IWitnetReporting1, + IWitnetReporting1Admin +{ + + bytes32 internal constant _WITNET_REPORTING_1_DATA_SLOTHASH = + /* keccak256("io.witnet.boards.data.v2.reporting.1") */ + 0x32ecea6ea7fbc6d7e8c8041c5ecf898bf8d40bd92da1207bebee19461a94c7bd; + + struct Escrow { + uint256 index; + uint256 weiSignUpFee; + uint256 lastSignUpBlock; + uint256 lastSignOutBlock; + uint256 lastSlashBlock; + } + + struct Reporting { + uint256 totalReporters; + IWitnetReporting1.SignUpConfig settings; + address[] reporters; + mapping (address => Escrow) escrows; + } + + // --- Internal view functions + + + + // ================================================================================================================ + // --- Internal state-modifying functions ------------------------------------------------------------------------- + + function __deleteReporterAddressByIndex(uint _index) + internal + returns (uint256 _totalReporters) + { + _totalReporters = __reporting().totalReporters; + if (_index >= _totalReporters) { + revert WitnetV2.IndexOutOfBounds(_index, _totalReporters); + } + else if (_totalReporters > 1 && _index < _totalReporters - 1) { + address _latestReporterAddress = __reporting().reporters[_totalReporters - 1]; + Escrow storage __latestReporterEscrow = __reporting().escrows[_latestReporterAddress]; + __latestReporterEscrow.index = _index; + __reporting().reporters[_index] = _latestReporterAddress; + } + __reporting().reporters.pop(); + __reporting().totalReporters = -- _totalReporters; + } + + function __pushReporterAddress(address _reporterAddr) + internal + returns (uint256 _totalReporters) + { + __reporting().reporters.push(_reporterAddr); + return ++ __reporting().totalReporters; + } + + /// @dev Returns storage pointer to contents of 'Board' struct. + function __reporting() + internal pure + returns (Reporting storage _ptr) + { + assembly { + _ptr.slot := _WITNET_REPORTING_1_DATA_SLOTHASH + } + } + + /// @dev Slash given reporter, after checking slashing conditions for sender are met. + function __slashSignedUpReporter(address _reporter) + internal + virtual + returns (uint256 _weiValue) + { + WitnetReporting1Data.Escrow storage __escrow = __reporting().escrows[_reporter]; + if (__escrow.weiSignUpFee > 0) { + if ( + __escrow.lastSignOutBlock < __escrow.lastSignUpBlock + || block.number < __escrow.lastSignUpBlock + __reporting().settings.banningBlocks + ) { + _weiValue = __escrow.weiSignUpFee; + __escrow.weiSignUpFee = 0; + __escrow.lastSlashBlock = block.number; + emit Slashed( + _reporter, + _weiValue, + __deleteReporterAddressByIndex(__escrow.index) + ); + } + } + } + +} \ No newline at end of file diff --git a/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessReporting1.sol b/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessReporting1.sol new file mode 100644 index 000000000..d8e5d3c5b --- /dev/null +++ b/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessReporting1.sol @@ -0,0 +1,270 @@ +// SPDX-License-Identifier: MIT +// solhint-disable var-name-mixedcase + +pragma solidity >=0.8.0 <0.9.0; + +import "./WitnetRequestBoardTrustlessBase.sol"; +import "../../../data/WitnetReporting1Data.sol"; + +/// @title Witnet Request Board "trustable" implementation contract. +/// @notice Contract to bridge requests to Witnet Decentralized Oracle Network. +/// @dev This contract enables posting requests that Witnet bridges will insert into the Witnet network. +/// The result of the requests will be posted back to this contract by the bridge nodes too. +/// @author The Witnet Foundation +abstract contract WitnetRequestBoardTrustlessReporting1 + is + WitnetRequestBoardTrustlessBase, + WitnetReporting1Data +{ + modifier onlySignedUpReporters { + if (!isSignedUpReporter(_msgSender())) { + revert WitnetV2.Unauthorized(_msgSender()); + } + _; + } + + modifier onlyExpectedReporterFor(bytes32 _drHash) { + if (_msgSender() != __drPostRequest(_drHash).reporter) { + revert WitnetV2.Unauthorized(_msgSender()); + } + _; + } + + constructor( + bool _upgradable, + bytes32 _versionTag + ) + WitnetRequestBoardTrustlessBase(_upgradable, _versionTag) + {} + + // ================================================================================================================ + // --- Override WitnetRequestBoardV2Data -------------------------------------------------------------------------- + + function _canDrPostBeDeletedFrom(bytes32 _drHash, address _from) + internal view + virtual override + returns (bool) + { + WitnetV2.DrPostStatus _temporaryStatus = __drPost(_drHash).status; + return (_temporaryStatus == WitnetV2.DrPostStatus.Rejected + ? true + : super._canDrPostBeDeletedFrom(_drHash, _from) + ); + } + + function _getDrPostStatus(bytes32 _drHash) + internal view + virtual override + returns (WitnetV2.DrPostStatus _temporaryStatus) + { + _temporaryStatus = __drPost(_drHash).status; + if (_temporaryStatus == WitnetV2.DrPostStatus.Accepted) { + if (block.number > _getDrPostBlock(_drHash) + __reporting().settings.acceptanceBlocks) { + return WitnetV2.DrPostStatus.Expired; + } + } + return super._getDrPostStatus(_drHash); + } + + // ================================================================================================================ + // --- Overrides IERC165 interface -------------------------------------------------------------------------------- + + /// @dev See {IERC165-supportsInterface}. + function supportsInterface(bytes4 _interfaceId) + public view + virtual override + returns (bool) + { + return _interfaceId == type(IWitnetReporting1).interfaceId + || _interfaceId == type(IWitnetReporting1Admin).interfaceId + || super.supportsInterface(_interfaceId); + } + +function deleteDrPost(bytes32 _drHash) + external + virtual override + { + if (!_canDrPostBeDeletedFrom(_drHash, _msgSender())){ + revert WitnetV2.Unauthorized(_msgSender()); + } + uint _value; + if (__drPost(_drHash).status == WitnetV2.DrPostStatus.Posted) { + address _reporter = __drPostRequest(_drHash).reporter; + _value = __slashSignedUpReporter(_reporter); + if (_value < address(this).balance) { + revert WitnetV2.InsufficientBalance(address(this).balance, _value); + } + } + __deleteDrPost(_drHash); + if (_value > 0) { + _safeTransferTo(payable(_msgSender()), _value); + } + emit DrPostDeleted(_msgSender(), _drHash); + } + + // ================================================================================================================ + // --- IWitnetReporting1 implementation --------------------------------------------------------------------------- + + function getReportingAddressByIndex(uint _reporterIndex) + external view + override + returns (address) + { + if (_reporterIndex >= __reporting().totalReporters) { + revert WitnetV2.IndexOutOfBounds(_reporterIndex, __reporting().totalReporters); + } + return __reporting().reporters[_reporterIndex]; + } + + function getReportingAddresses() + external view + override + returns (address[] memory _addrs) + { + _addrs = new address[](__reporting().totalReporters); + address[] storage __addrs = __reporting().reporters; + for (uint _ix = 0; _ix < _addrs.length; ) { + _addrs[_ix] = __addrs[_ix]; + unchecked { + _ix ++; + } + } + } + + function getReportingSignUpConfig() + external view + override + returns (SignUpConfig memory) + { + return __reporting().settings; + } + + function isSignedUpReporter(address _reporter) + public view + virtual override + returns (bool) + { + WitnetReporting1Data.Escrow storage __escrow = __reporting().escrows[_reporter]; + return ( + __escrow.weiSignUpFee > 0 + && __escrow.lastSignOutBlock > __escrow.lastSignUpBlock + ); + } + + function totalSignedUpReporters() + external view + override + returns (uint256) + { + return __reporting().totalReporters; + } + + function signUp() + external payable + override + nonReentrant + returns (uint256 _index) + { + IWitnetReporting1.SignUpConfig storage __settings = __reporting().settings; + WitnetReporting1Data.Escrow storage __escrow = __reporting().escrows[_msgSender()]; + uint _fee = __settings.weiSignUpFee; + uint _value = _getMsgValue(); + // Check that it's not already signed up: + if (__escrow.weiSignUpFee > 0) { + revert IWitnetReporting1.AlreadySignedUp(_msgSender()); + } + // Check that it's not banned, or if so, enough blocks have elapsed since then: + if (__escrow.lastSlashBlock > 0) { + if ( + __settings.banningBlocks == 0 + || block.number < __escrow.lastSlashBlock + __settings.banningBlocks + ) { + revert WitnetV2.Unauthorized(_msgSender()); + } + } + // Check that enough sign-up fee is being provided: + if (_value < _fee) { + revert WitnetV2.InsufficientFee(_value, _fee); + } + // Update storage: + _index = __reporting().totalReporters; + __escrow.index = _index; + __escrow.weiSignUpFee = _fee; + __escrow.lastSignUpBlock = block.number; + emit SignedUp( + _msgSender(), + _fee, + __pushReporterAddress(_msgSender()) + ); + // Transfer unused funds back: + if (_value > _fee) { + _safeTransferTo(payable(_msgSender()), _value - _fee); + } + } + + function signOut() + external + override + onlySignedUpReporters + { + WitnetReporting1Data.Escrow storage __escrow = __reporting().escrows[_msgSender()]; + // Update storage: + __escrow.lastSignOutBlock = block.number; + emit SigningOut( + _msgSender(), + __escrow.weiSignUpFee, + __deleteReporterAddressByIndex(__escrow.index) + ); + } + + function acceptDrPost(bytes32 _drHash) + external + override + drPostInStatus(_drHash, WitnetV2.DrPostStatus.Posted) + onlySignedUpReporters + onlyExpectedReporterFor(_drHash) + { + if (_msgSender() != __drPostRequest(_drHash).requester) { + revert WitnetV2.Unauthorized(_msgSender()); + } + __drPost(_drHash).status = WitnetV2.DrPostStatus.Accepted; + emit DrPostAccepted(_msgSender(), _drHash); + } + + function rejectDrPost(bytes32 _drHash, Witnet.ErrorCodes _reason) + external payable + override + drPostInStatus(_drHash, WitnetV2.DrPostStatus.Posted) + onlyExpectedReporterFor(_drHash) + nonReentrant + { + uint _value = _getMsgValue(); + uint _fee = __reporting().settings.weiRejectionFee; + // Check enough value is provided as to pay for rejection fee, if any + if (_value < _fee) { + revert WitnetV2.InsufficientFee(_value, _fee); + } + // Transfer back income funds exceeding rejection fee, if any + if (_value > _fee) { + _safeTransferTo( + payable(_msgSender()), + _value - _fee + ); + } + // Transfer reporter as much deposited drPost reward as possible: + WitnetV2.DrPost storage __post = __drPost(_drHash); + _value = __post.request.weiReward; + if (_value > address(this).balance) { + _value = address(this).balance; + } + __post.request.weiReward -= _value; + __post.status = WitnetV2.DrPostStatus.Rejected; + _safeTransferTo( + payable(__post.request.requester), + _value + _fee + ); + emit DrPostRejected(_msgSender(), _drHash, _reason); + } + + +} \ No newline at end of file From 9c19dfaa5c67b603d1a41b562288e54fcf09029b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 25 Nov 2022 10:53:10 +0100 Subject: [PATCH 010/119] feat(libs): refactor and add generic support to arrays --- contracts/libs/Witnet.sol | 8 +- contracts/libs/WitnetCBOR.sol | 695 ++++++++++++++++++++++------------ contracts/libs/WitnetLib.sol | 582 +++++++++++++++++++--------- 3 files changed, 865 insertions(+), 420 deletions(-) diff --git a/contracts/libs/Witnet.sol b/contracts/libs/Witnet.sol index 0c3b02764..b0f02a971 100644 --- a/contracts/libs/Witnet.sol +++ b/contracts/libs/Witnet.sol @@ -8,6 +8,9 @@ import "../interfaces/IWitnetRequest.sol"; library Witnet { + /// =============================================================================================================== + /// --- Witnet internal methods ----------------------------------------------------------------------------------- + /// @notice Witnet function that computes the hash of a CBOR-encoded Data Request. /// @param _bytecode CBOR-encoded RADON. function hash(bytes memory _bytecode) internal pure returns (bytes32) { @@ -52,7 +55,9 @@ library Witnet { WitnetCBOR.CBOR value; // Resulting value, in CBOR-serialized bytes. } - /// Witnet error codes table. + /// =============================================================================================================== + /// --- Witnet error codes table ---------------------------------------------------------------------------------- + enum ErrorCodes { // 0x00: Unknown error. Something went really bad! Unknown, @@ -355,4 +360,5 @@ library Witnet { /// 0xFF: Some tally error is not intercepted but should UnhandledIntercept } + } diff --git a/contracts/libs/WitnetCBOR.sol b/contracts/libs/WitnetCBOR.sol index 8492cb328..de4ab04f5 100644 --- a/contracts/libs/WitnetCBOR.sol +++ b/contracts/libs/WitnetCBOR.sol @@ -10,7 +10,6 @@ import "./WitnetBuffer.sol"; /// @dev Most of the logic has been borrowed from Patrick Gansterer’s cbor.js library: https://github.com/paroga/cbor-js /// @author The Witnet Foundation. /// -/// TODO: add support for Array (majorType = 4) /// TODO: add support for Map (majorType = 5) /// TODO: add support for Float32 (majorType = 7, additionalInformation = 26) /// TODO: add support for Float64 (majorType = 7, additionalInformation = 27) @@ -19,6 +18,40 @@ library WitnetCBOR { using WitnetBuffer for WitnetBuffer.Buffer; + enum MajorTypes { + /* 0 */ Uint, + /* 1 */ Int, + /* 2 */ Bytes, + /* 3 */ String, + /* 4 */ Array, + /* 5 */ Map, + /* 6 */ Tag, + /* 7 */ Primitive + } + + error EmptyArray(); + error InvalidLengthEncoding(uint length); + error UnexpectedMajorType(uint read, uint expected); + error UnsupportedPrimitive(uint primitive); + error UnsupportedMajorType(uint unexpected); + + modifier isMajorType( + WitnetCBOR.CBOR memory _cbor, + MajorTypes _expected + ) { + if (_cbor.majorType != uint(_expected)) { + revert UnexpectedMajorType(_cbor.majorType, uint(_expected)); + } + _; + } + + modifier notEmpty(WitnetBuffer.Buffer memory _buf) { + if (_buf.data.length == 0) { + revert WitnetBuffer.EmptyBuffer(); + } + _; + } + /// Data struct following the RFC-7049 standard: Concise Binary Object Representation. struct CBOR { WitnetBuffer.Buffer buffer; @@ -32,296 +65,480 @@ library WitnetCBOR { uint32 constant internal _UINT32_MAX = type(uint32).max; uint64 constant internal _UINT64_MAX = type(uint64).max; - /// @notice Decode a `CBOR` structure into a native `bool` value. - /// @param _cborValue An instance of `CBOR`. - /// @return The value represented by the input, as a `bool` value. - function decodeBool(CBOR memory _cborValue) internal pure returns(bool) { - _cborValue.len = readLength(_cborValue.buffer, _cborValue.additionalInformation); - require(_cborValue.majorType == 7, "WitnetCBOR: Tried to read a `bool` value from a `CBOR` with majorType != 7"); - if (_cborValue.len == 20) { - return false; - } else if (_cborValue.len == 21) { - return true; - } else { - revert("WitnetCBOR: Tried to read `bool` from a `CBOR` with len different than 20 or 21"); - } + /// @notice Decode a CBOR structure from raw bytes. + /// @dev This is the main factory for CBOR instances, which can be later decoded into native EVM types. + /// @param _cborBytes Raw bytes representing a CBOR-encoded value. + /// @return A `CBOR` instance containing a partially decoded value. + function valueFromBytes(bytes memory _cborBytes) + internal pure + returns (CBOR memory) + { + WitnetBuffer.Buffer memory buffer = WitnetBuffer.Buffer(_cborBytes, 0); + return _valueFromBuffer(buffer); } - /// @notice Decode a `CBOR` structure into a native `bytes` value. - /// @param _cborValue An instance of `CBOR`. - /// @return The value represented by the input, as a `bytes` value. - function decodeBytes(CBOR memory _cborValue) internal pure returns(bytes memory) { - _cborValue.len = readLength(_cborValue.buffer, _cborValue.additionalInformation); - if (_cborValue.len == _UINT32_MAX) { - bytes memory bytesData; - - // These checks look repetitive but the equivalent loop would be more expensive. - uint32 itemLength = uint32(readIndefiniteStringLength(_cborValue.buffer, _cborValue.majorType)); - if (itemLength < _UINT32_MAX) { - bytesData = abi.encodePacked(bytesData, _cborValue.buffer.read(itemLength)); - itemLength = uint32(readIndefiniteStringLength(_cborValue.buffer, _cborValue.majorType)); - if (itemLength < _UINT32_MAX) { - bytesData = abi.encodePacked(bytesData, _cborValue.buffer.read(itemLength)); - } + /// @notice Decode a CBOR structure from raw bytes. + /// @dev This is an alternate factory for CBOR instances, which can be later decoded into native EVM types. + /// @param _buffer A Buffer structure representing a CBOR-encoded value. + /// @return A `CBOR` instance containing a partially decoded value. + function _valueFromBuffer(WitnetBuffer.Buffer memory _buffer) + private pure + notEmpty(_buffer) + returns (CBOR memory) + { + uint8 _initialByte; + uint8 _majorType = 255; + uint8 _additionalInformation; + uint64 _tag = _UINT64_MAX; + + bool _isTagged = true; + while (_isTagged) { + // Extract basic CBOR properties from input bytes + _initialByte = _buffer.readUint8(); + _majorType = _initialByte >> 5; + _additionalInformation = _initialByte & 0x1f; + // Early CBOR tag parsing. + if (_majorType == 6) { + _tag = _readLength(_buffer, _additionalInformation); + } else { + _isTagged = false; } - return bytesData; - } else { - return _cborValue.buffer.read(uint32(_cborValue.len)); } - } - - /// @notice Decode a `CBOR` structure into a native `bytes32` value. - /// @param _cborValue An instance of `CBOR`. - /// @return _bytes32 The value represented by the input, as a `bytes32` value. - function decodeBytes32(CBOR memory _cborValue) internal pure returns(bytes32 _bytes32) { - bytes memory _bb = decodeBytes(_cborValue); - uint _len = _bb.length > 32 ? 32 : _bb.length; - for (uint _i = 0; _i < _len; _i ++) { - _bytes32 |= bytes32(_bb[_i] & 0xff) >> (_i * 8); + if (_majorType > 7) { + revert UnsupportedMajorType(_majorType); } + return CBOR( + _buffer, + _initialByte, + _majorType, + _additionalInformation, + 0, + _tag + ); } - /// @notice Decode a `CBOR` structure into a `fixed16` value. - /// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values - /// by 5 decimal orders so as to get a fixed precision of 5 decimal positions, which should be OK for most `fixed16` - /// use cases. In other words, the output of this method is 10,000 times the actual value, encoded into an `int32`. - /// @param _cborValue An instance of `CBOR`. - /// @return The value represented by the input, as an `int128` value. - function decodeFixed16(CBOR memory _cborValue) internal pure returns(int32) { - require(_cborValue.majorType == 7, "WitnetCBOR: Tried to read a `fixed` value from a `WT.CBOR` with majorType != 7"); - require(_cborValue.additionalInformation == 25, "WitnetCBOR: Tried to read `fixed16` from a `WT.CBOR` with additionalInformation != 25"); - return _cborValue.buffer.readFloat16(); + /// Read the length of a CBOR indifinite-length item (arrays, maps, byte strings and text) from a buffer, consuming + /// as many bytes as specified by the first byte. + function _readIndefiniteStringLength( + WitnetBuffer.Buffer memory _buffer, + uint8 _majorType + ) + private pure + returns (uint64 _length) + { + uint8 _initialByte = _buffer.readUint8(); + if (_initialByte == 0xff) { + return _UINT64_MAX; + } + _length = _readLength( + _buffer, + _initialByte & 0x1f + ); + if (_length >= _UINT64_MAX) { + revert InvalidLengthEncoding(_length); + } else if (_majorType != (_initialByte >> 5)) { + revert UnexpectedMajorType((_initialByte >> 5), _majorType); + } } - /// @notice Decode a `CBOR` structure into a native `int128[]` value whose inner values follow the same convention. - /// as explained in `decodeFixed16`. - /// @param _cborValue An instance of `CBOR`. - /// @return The value represented by the input, as an `int128[]` value. - function decodeFixed16Array(CBOR memory _cborValue) internal pure returns(int32[] memory) { - require(_cborValue.majorType == 4, "WitnetCBOR: Tried to read `int128[]` from a `CBOR` with majorType != 4"); - - uint64 length = readLength(_cborValue.buffer, _cborValue.additionalInformation); - require(length < _UINT64_MAX, "WitnetCBOR: Indefinite-length CBOR arrays are not supported"); - - int32[] memory array = new int32[](length); - for (uint64 i = 0; i < length; i++) { - CBOR memory item = valueFromBuffer(_cborValue.buffer); - array[i] = decodeFixed16(item); + /// Reads the length of the next CBOR item from a buffer, consuming a different number of bytes depending on the + /// value of the `additionalInformation` argument. + function _readLength( + WitnetBuffer.Buffer memory _buffer, + uint8 _additionalInformation + ) + private pure + returns (uint64) + { + if (_additionalInformation < 24) { + return _additionalInformation; } - - return array; + if (_additionalInformation == 24) { + return _buffer.readUint8(); + } + if (_additionalInformation == 25) { + return _buffer.readUint16(); + } + if (_additionalInformation == 26) { + return _buffer.readUint32(); + } + if (_additionalInformation == 27) { + return _buffer.readUint64(); + } + if (_additionalInformation == 31) { + return _UINT64_MAX; + } + revert InvalidLengthEncoding(_additionalInformation); } - /// @notice Decode a `CBOR` structure into a native `int128` value. - /// @param _cborValue An instance of `CBOR`. - /// @return The value represented by the input, as an `int128` value. - function decodeInt128(CBOR memory _cborValue) internal pure returns(int128) { - if (_cborValue.majorType == 1) { - uint64 length = readLength(_cborValue.buffer, _cborValue.additionalInformation); - return int128(-1) - int128(uint128(length)); - } else if (_cborValue.majorType == 0) { - // Any `uint64` can be safely casted to `int128`, so this method supports majorType 1 as well so as to have offer - // a uniform API for positive and negative numbers - return int128(uint128(decodeUint64(_cborValue))); + function _seekNext(WitnetCBOR.CBOR memory _cbor) + private pure + returns (WitnetCBOR.CBOR memory) + { + if (_cbor.majorType == 0 || _cbor.majorType == 1) { + return _skipInt(_cbor); + } else if (_cbor.majorType == 2) { + return _skipBytes(_cbor); + } else if (_cbor.majorType == 3) { + return _skipText(_cbor); + } else if (_cbor.majorType == 4) { + return _skipArray(_cbor); + } else if (_cbor.majorType == 7) { + return _skipPrimitive(_cbor); + } else { + revert UnsupportedMajorType(_cbor.majorType); } - revert("WitnetCBOR: Tried to read `int128` from a `CBOR` with majorType not 0 or 1"); } - /// @notice Decode a `CBOR` structure into a native `int128[]` value. - /// @param _cborValue An instance of `CBOR`. - /// @return The value represented by the input, as an `int128[]` value. - function decodeInt128Array(CBOR memory _cborValue) internal pure returns(int128[] memory) { - require(_cborValue.majorType == 4, "WitnetCBOR: Tried to read `int128[]` from a `CBOR` with majorType != 4"); - - uint64 length = readLength(_cborValue.buffer, _cborValue.additionalInformation); - require(length < _UINT64_MAX, "WitnetCBOR: Indefinite-length CBOR arrays are not supported"); - - int128[] memory array = new int128[](length); - for (uint64 i = 0; i < length; i++) { - CBOR memory item = valueFromBuffer(_cborValue.buffer); - array[i] = decodeInt128(item); + function _skipArray(CBOR memory _cbor) + private pure + isMajorType(_cbor, MajorTypes.Array) + returns (CBOR memory) + { + CBOR[] memory _items = readArray(_cbor); + if (_items.length > 0) { + return _seekNext(_items[_items.length - 1]); + } else { + revert EmptyArray(); } + } - return array; + function _skipBytes(CBOR memory _cbor) + internal pure + isMajorType(_cbor, MajorTypes.Bytes) + returns (CBOR memory) + { + _cbor.len = _readLength(_cbor.buffer, _cbor.additionalInformation); + if (_cbor.len < _UINT32_MAX) { + _cbor.buffer.seek(_cbor.len); + return _valueFromBuffer(_cbor.buffer); + } + // TODO: support skipping indefitine length bytes array + revert InvalidLengthEncoding(_cbor.len); } - /// @notice Decode a `CBOR` structure into a native `string` value. - /// @param _cborValue An instance of `CBOR`. - /// @return The value represented by the input, as a `string` value. - function decodeString(CBOR memory _cborValue) internal pure returns(string memory) { - _cborValue.len = readLength(_cborValue.buffer, _cborValue.additionalInformation); - if (_cborValue.len == _UINT64_MAX) { - bytes memory textData; - bool done; - while (!done) { - uint64 itemLength = readIndefiniteStringLength(_cborValue.buffer, _cborValue.majorType); - if (itemLength < _UINT64_MAX) { - textData = abi.encodePacked(textData, readText(_cborValue.buffer, itemLength / 4)); + function _skipInt(CBOR memory _cbor) + private pure + returns (CBOR memory) + { + if (_cbor.majorType == 0 || _cbor.majorType == 1) { + uint _offset = 1; + if (_cbor.additionalInformation >= 24) { + if (_cbor.additionalInformation <= 27) { + _offset += 1 << (_cbor.additionalInformation - 24); } else { - done = true; + revert InvalidLengthEncoding(_cbor.additionalInformation); } - } - return string(textData); + } + _cbor.buffer.seek(_offset); + return _valueFromBuffer(_cbor.buffer); } else { - return string(readText(_cborValue.buffer, _cborValue.len)); + revert UnexpectedMajorType(_cbor.majorType, 1); } } - /// @notice Decode a `CBOR` structure into a native `string[]` value. - /// @param _cborValue An instance of `CBOR`. - /// @return The value represented by the input, as an `string[]` value. - function decodeStringArray(CBOR memory _cborValue) internal pure returns(string[] memory) { - require(_cborValue.majorType == 4, "WitnetCBOR: Tried to read `string[]` from a `CBOR` with majorType != 4"); - - uint64 length = readLength(_cborValue.buffer, _cborValue.additionalInformation); - require(length < _UINT64_MAX, "WitnetCBOR: Indefinite-length CBOR arrays are not supported"); - - string[] memory array = new string[](length); - for (uint64 i = 0; i < length; i++) { - CBOR memory item = valueFromBuffer(_cborValue.buffer); - array[i] = decodeString(item); + function _skipPrimitive(CBOR memory _cbor) + private pure + isMajorType(_cbor, MajorTypes.Primitive) + returns (WitnetCBOR.CBOR memory) + { + if (_cbor.additionalInformation == 25) { + _cbor.buffer.seek(2); + + } else if ( + _cbor.additionalInformation != 20 + && _cbor.additionalInformation != 21 + ) { + revert UnsupportedPrimitive(_cbor.additionalInformation); } - - return array; + return _valueFromBuffer(_cbor.buffer); } - /// @notice Decode a `CBOR` structure into a native `uint64` value. - /// @param _cborValue An instance of `CBOR`. - /// @return The value represented by the input, as an `uint64` value. - function decodeUint64(CBOR memory _cborValue) internal pure returns(uint64) { - require(_cborValue.majorType == 0, "WitnetCBOR: Tried to read `uint64` from a `CBOR` with majorType != 0"); - return readLength(_cborValue.buffer, _cborValue.additionalInformation); + function _skipText(CBOR memory _cbor) + internal pure + isMajorType(_cbor, MajorTypes.String) + returns (CBOR memory) + { + _cbor.len = _readLength(_cbor.buffer, _cbor.additionalInformation); + if (_cbor.len < _UINT64_MAX) { + _cbor.buffer.seek(_cbor.len); + return _valueFromBuffer(_cbor.buffer); + } + // TODO: support skipping indefitine length text array + revert InvalidLengthEncoding(_cbor.len); } - /// @notice Decode a `CBOR` structure into a native `uint64[]` value. - /// @param _cborValue An instance of `CBOR`. - /// @return The value represented by the input, as an `uint64[]` value. - function decodeUint64Array(CBOR memory _cborValue) internal pure returns(uint64[] memory) { - require(_cborValue.majorType == 4, "WitnetCBOR: Tried to read `uint64[]` from a `CBOR` with majorType != 4"); - - uint64 length = readLength(_cborValue.buffer, _cborValue.additionalInformation); - require(length < _UINT64_MAX, "WitnetCBOR: Indefinite-length CBOR arrays are not supported"); - - uint64[] memory array = new uint64[](length); - for (uint64 i = 0; i < length; i++) { - CBOR memory item = valueFromBuffer(_cborValue.buffer); - array[i] = decodeUint64(item); + function readArray(CBOR memory _cbor) + internal pure + isMajorType(_cbor, MajorTypes.Array) + returns (CBOR[] memory _items) + { + uint64 _length = _readLength(_cbor.buffer, _cbor.additionalInformation); + _items = new CBOR[](_length); + for (uint _ix = 0; _ix < _length; _ix ++) { + _items[_ix] = _valueFromBuffer(_cbor.buffer); + _cbor = _seekNext(_items[_ix]); } - - return array; } - /// @notice Decode a CBOR structure from raw bytes. - /// @dev This is the main factory for CBOR instances, which can be later decoded into native EVM types. - /// @param _cborBytes Raw bytes representing a CBOR-encoded value. - /// @return A `CBOR` instance containing a partially decoded value. - function valueFromBytes(bytes memory _cborBytes) internal pure returns(CBOR memory) { - WitnetBuffer.Buffer memory buffer = WitnetBuffer.Buffer(_cborBytes, 0); - - return valueFromBuffer(buffer); + function _replaceWildcards(CBOR memory _cbor, string[] memory _args) + private pure + isMajorType(_cbor, MajorTypes.String) + returns (CBOR memory) + { + CBOR memory _copy = _valueFromBuffer(_cbor.buffer); + _copy.buffer.replaceWildcards( + _readLength(_copy.buffer, _copy.additionalInformation), + _args + ); + return _cbor; } - /// @notice Decode a CBOR structure from raw bytes. - /// @dev This is an alternate factory for CBOR instances, which can be later decoded into native EVM types. - /// @param _buffer A Buffer structure representing a CBOR-encoded value. - /// @return A `CBOR` instance containing a partially decoded value. - function valueFromBuffer(WitnetBuffer.Buffer memory _buffer) internal pure returns(CBOR memory) { - require(_buffer.data.length > 0, "WitnetCBOR: Found empty buffer when parsing CBOR value"); - - uint8 initialByte; - uint8 majorType = 255; - uint8 additionalInformation; - uint64 tag = _UINT64_MAX; - - bool isTagged = true; - while (isTagged) { - // Extract basic CBOR properties from input bytes - initialByte = _buffer.readUint8(); - majorType = initialByte >> 5; - additionalInformation = initialByte & 0x1f; - - // Early CBOR tag parsing. - if (majorType == 6) { - tag = readLength(_buffer, additionalInformation); - } else { - isTagged = false; + function replaceWildcards(CBOR[] memory _items, string[] memory _args) + internal pure + { + for (uint _ix = 0; _ix < _items.length; _ix ++) { + if (_items[_ix].majorType == 4) { + replaceWildcards(readArray(_items[_ix]), _args); + } else if (_items[_ix].majorType == 3) { + _replaceWildcards(_items[_ix], _args); } } - - require(majorType <= 7, "WitnetCBOR: Invalid CBOR major type"); - - return CBOR( - _buffer, - initialByte, - majorType, - additionalInformation, - 0, - tag); } - /// Reads the length of the next CBOR item from a buffer, consuming a different number of bytes depending on the - /// value of the `additionalInformation` argument. - function readLength(WitnetBuffer.Buffer memory _buffer, uint8 additionalInformation) private pure returns(uint64) { - if (additionalInformation < 24) { - return additionalInformation; + /// @notice Read a `CBOR` structure into a native `bool` value. + /// @param _cbor An instance of `CBOR`. + /// @return The value represented by the input, as a `bool` value. + function readBool(CBOR memory _cbor) + internal pure + isMajorType(_cbor, MajorTypes.Primitive) + returns (bool) + { + // uint64 _primitive = _readLength(_cbor.buffer, _cbor.additionalInformation); + // if (_primitive == 20) { + // return false; + // } else if (_primitive == 21) { + // return true; + if (_cbor.additionalInformation == 20) { + return false; + } else if (_cbor.additionalInformation == 21) { + return true; + } else { + revert UnsupportedPrimitive(_cbor.additionalInformation); } - if (additionalInformation == 24) { - return _buffer.readUint8(); + } + + /// @notice Decode a `CBOR` structure into a native `bytes` value. + /// @param _cbor An instance of `CBOR`. + /// @return _output The value represented by the input, as a `bytes` value. + function readBytes(CBOR memory _cbor) + internal pure + isMajorType(_cbor, MajorTypes.Bytes) + returns (bytes memory _output) + { + _cbor.len = _readLength( + _cbor.buffer, + _cbor.additionalInformation + ); + if (_cbor.len == _UINT32_MAX) { + // These checks look repetitive but the equivalent loop would be more expensive. + uint32 _length = uint32(_readIndefiniteStringLength( + _cbor.buffer, + _cbor.majorType + )); + if (_length < _UINT32_MAX) { + _output = abi.encodePacked(_cbor.buffer.read(_length)); + _length = uint32(_readIndefiniteStringLength( + _cbor.buffer, + _cbor.majorType + )); + if (_length < _UINT32_MAX) { + _output = abi.encodePacked( + _output, + _cbor.buffer.read(_length) + ); + } + } + } else { + return _cbor.buffer.read(uint32(_cbor.len)); } - if (additionalInformation == 25) { - return _buffer.readUint16(); + } + + /// @notice Decode a `CBOR` structure into a `fixed16` value. + /// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values + /// by 5 decimal orders so as to get a fixed precision of 5 decimal positions, which should be OK for most `fixed16` + /// use cases. In other words, the output of this method is 10,000 times the actual value, encoded into an `int32`. + /// @param _cbor An instance of `CBOR`. + /// @return The value represented by the input, as an `int128` value. + function readFloat16(CBOR memory _cbor) + internal pure + isMajorType(_cbor, MajorTypes.Primitive) + returns (int32) + { + if (_cbor.additionalInformation == 25) { + return _cbor.buffer.readFloat16(); + } else { + revert UnsupportedPrimitive(_cbor.additionalInformation); } - if (additionalInformation == 26) { - return _buffer.readUint32(); + } + + /// @notice Decode a `CBOR` structure into a native `int128[]` value whose inner values follow the same convention + /// @notice as explained in `decodeFixed16`. + /// @param _cbor An instance of `CBOR`. + function readFloat16Array(CBOR memory _cbor) + internal pure + isMajorType(_cbor, MajorTypes.Array) + returns (int32[] memory _values) + { + uint64 _length = _readLength(_cbor.buffer, _cbor.additionalInformation); + if (_length < _UINT64_MAX) { + _values = new int32[](_length); + for (uint64 _i = 0; _i < _length; ) { + CBOR memory _item = _valueFromBuffer(_cbor.buffer); + _values[_i] = readFloat16(_item); + unchecked { + _i ++; + } + } + } else { + revert InvalidLengthEncoding(_length); } - if (additionalInformation == 27) { - return _buffer.readUint64(); + } + + /// @notice Decode a `CBOR` structure into a native `int128` value. + /// @param _cbor An instance of `CBOR`. + /// @return The value represented by the input, as an `int128` value. + function readInt(CBOR memory _cbor) + internal pure + returns (int) + { + if (_cbor.majorType == 1) { + uint64 _value = _readLength( + _cbor.buffer, + _cbor.additionalInformation + ); + return int(-1) - int(uint(_value)); + } else if (_cbor.majorType == 0) { + // Any `uint64` can be safely casted to `int128`, so this method supports majorType 1 as well so as to have offer + // a uniform API for positive and negative numbers + return int(readUint(_cbor)); } - if (additionalInformation == 31) { - return _UINT64_MAX; + else { + revert UnexpectedMajorType(_cbor.majorType, 1); } - revert("WitnetCBOR: Invalid length encoding (non-existent additionalInformation value)"); } - /// Read the length of a CBOR indifinite-length item (arrays, maps, byte strings and text) from a buffer, consuming - /// as many bytes as specified by the first byte. - function readIndefiniteStringLength(WitnetBuffer.Buffer memory _buffer, uint8 majorType) private pure returns(uint64) { - uint8 initialByte = _buffer.readUint8(); - if (initialByte == 0xff) { - return _UINT64_MAX; + /// @notice Decode a `CBOR` structure into a native `int[]` value. + /// @param _cbor instance of `CBOR`. + /// @return _array The value represented by the input, as an `int[]` value. + function readIntArray(CBOR memory _cbor) + internal pure + isMajorType(_cbor, MajorTypes.Array) + returns (int[] memory _array) + { + uint64 _length = _readLength(_cbor.buffer, _cbor.additionalInformation); + if (_length < _UINT64_MAX) { + _array = new int[](_length); + for (uint _i = 0; _i < _length; ) { + CBOR memory _item = _valueFromBuffer(_cbor.buffer); + _array[_i] = readInt(_item); + unchecked { + _i ++; + } + } + } else { + revert InvalidLengthEncoding(_length); } - uint64 length = readLength(_buffer, initialByte & 0x1f); - require(length < _UINT64_MAX && (initialByte >> 5) == majorType, "WitnetCBOR: Invalid indefinite length"); - return length; } - /// Read a text string of a given length from a buffer. Returns a `bytes memory` value for the sake of genericness, - /// but it can be easily casted into a string with `string(result)`. - // solium-disable-next-line security/no-assign-params - function readText(WitnetBuffer.Buffer memory _buffer, uint64 _length) private pure returns(bytes memory) { - bytes memory result; - for (uint64 index = 0; index < _length; index++) { - uint8 value = _buffer.readUint8(); - if (value & 0x80 != 0) { - if (value < 0xe0) { - value = (value & 0x1f) << 6 | - (_buffer.readUint8() & 0x3f); - _length -= 1; - } else if (value < 0xf0) { - value = (value & 0x0f) << 12 | - (_buffer.readUint8() & 0x3f) << 6 | - (_buffer.readUint8() & 0x3f); - _length -= 2; + /// @notice Decode a `CBOR` structure into a native `string` value. + /// @param _cbor An instance of `CBOR`. + /// @return _text The value represented by the input, as a `string` value. + function readString(CBOR memory _cbor) + internal pure + isMajorType(_cbor, MajorTypes.String) + returns (string memory _text) + { + _cbor.len = _readLength(_cbor.buffer, _cbor.additionalInformation); + if (_cbor.len == _UINT64_MAX) { + bool _done; + while (!_done) { + uint64 _length = _readIndefiniteStringLength( + _cbor.buffer, + _cbor.majorType + ); + if (_length < _UINT64_MAX) { + _text = string(abi.encodePacked( + _text, + _cbor.buffer.readText(_length / 4) + )); } else { - value = (value & 0x0f) << 18 | - (_buffer.readUint8() & 0x3f) << 12 | - (_buffer.readUint8() & 0x3f) << 6 | - (_buffer.readUint8() & 0x3f); - _length -= 3; + _done = true; + } + } + } else { + return string(_cbor.buffer.readText(_cbor.len)); + } + } + + /// @notice Decode a `CBOR` structure into a native `string[]` value. + /// @param _cbor An instance of `CBOR`. + /// @return _strings The value represented by the input, as an `string[]` value. + function readStringArray(CBOR memory _cbor) + internal pure + isMajorType(_cbor, MajorTypes.Array) + returns (string[] memory _strings) + { + uint _length = _readLength(_cbor.buffer, _cbor.additionalInformation); + if (_length < _UINT64_MAX) { + _strings = new string[](_length); + for (uint _i = 0; _i < _length; ) { + CBOR memory _item = _valueFromBuffer(_cbor.buffer); + _strings[_i] = readString(_item); + unchecked { + _i ++; } } - result = abi.encodePacked(result, value); + } else { + revert InvalidLengthEncoding(_length); } - return result; } -} + + /// @notice Decode a `CBOR` structure into a native `uint64` value. + /// @param _cbor An instance of `CBOR`. + /// @return The value represented by the input, as an `uint64` value. + function readUint(CBOR memory _cbor) + internal pure + isMajorType(_cbor, MajorTypes.Uint) + returns (uint) + { + return _readLength( + _cbor.buffer, + _cbor.additionalInformation + ); + } + + /// @notice Decode a `CBOR` structure into a native `uint64[]` value. + /// @param _cbor An instance of `CBOR`. + /// @return _values The value represented by the input, as an `uint64[]` value. + function readUintArray(CBOR memory _cbor) + internal pure + isMajorType(_cbor, MajorTypes.Array) + returns (uint[] memory _values) + { + uint64 _length = _readLength(_cbor.buffer, _cbor.additionalInformation); + if (_length < _UINT64_MAX) { + _values = new uint[](_length); + for (uint _ix = 0; _ix < _length; ) { + CBOR memory _item = _valueFromBuffer(_cbor.buffer); + _values[_ix] = readUint(_item); + unchecked { + _ix ++; + } + } + } else { + revert InvalidLengthEncoding(_length); + } + } + +} \ No newline at end of file diff --git a/contracts/libs/WitnetLib.sol b/contracts/libs/WitnetLib.sol index d3920739a..9723a62cc 100644 --- a/contracts/libs/WitnetLib.sol +++ b/contracts/libs/WitnetLib.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.0 <0.9.0; -import "./Witnet.sol"; +import "./WitnetV2.sol"; /// @title A library for decoding Witnet request results /// @notice The library exposes functions to check the Witnet request success. @@ -10,25 +10,142 @@ import "./Witnet.sol"; /// @author The Witnet Foundation. library WitnetLib { - using WitnetCBOR for bytes; using WitnetCBOR for WitnetCBOR.CBOR; + using WitnetCBOR for WitnetCBOR.CBOR[]; + using WitnetLib for bytes; - /// @notice Decode raw CBOR bytes into a Witnet.Result instance. - /// @param _cborBytes Raw bytes representing a CBOR-encoded value. - /// @return A `Witnet.Result` instance. - function resultFromCborBytes(bytes calldata _cborBytes) - external pure - returns (Witnet.Result memory) + /// =============================================================================================================== + /// --- WitnetLib internal methods -------------------------------------------------------------------------------- + + function size(WitnetV2.RadonDataTypes _type) internal pure returns (uint) { + if (_type == WitnetV2.RadonDataTypes.Integer + || _type == WitnetV2.RadonDataTypes.Float + ) { + return 9; + } else if (_type == WitnetV2.RadonDataTypes.Bool) { + return 1; + } else { + // undetermined + return 0; + } + } + + function toAddress(bytes memory _value) internal pure returns (address) { + return address(toBytes20(_value)); + } + + function toBytes4(bytes memory _value) internal pure returns (bytes4) { + return bytes4(toFixedBytes(_value, 4)); + } + + function toBytes20(bytes memory _value) internal pure returns (bytes20) { + return bytes20(toFixedBytes(_value, 20)); + } + + function toBytes32(bytes memory _value) internal pure returns (bytes32) { + return toFixedBytes(_value, 32); + } + + function toFixedBytes(bytes memory _value, uint8 _numBytes) + internal pure + returns (bytes32 _bytes32) { - WitnetCBOR.CBOR memory cborValue = _cborBytes.valueFromBytes(); - return resultFromCborValue(cborValue); + assert(_numBytes <= 32); + unchecked { + uint _len = _value.length > _numBytes ? _numBytes : _value.length; + for (uint _i = 0; _i < _len; _i ++) { + _bytes32 |= bytes32(_value[_i] & 0xff) >> (_i * 8); + } + } + } + + /// @notice Convert a `uint64` into a 2 characters long `string` representing its two less significant hexadecimal values. + /// @param _u A `uint64` value. + /// @return The `string` representing its hexadecimal value. + function toHexString(uint8 _u) + internal pure + returns (string memory) + { + bytes memory b2 = new bytes(2); + uint8 d0 = uint8(_u / 16) + 48; + uint8 d1 = uint8(_u % 16) + 48; + if (d0 > 57) + d0 += 7; + if (d1 > 57) + d1 += 7; + b2[0] = bytes1(d0); + b2[1] = bytes1(d1); + return string(b2); + } + + /// @notice Convert a `uint64` into a 1, 2 or 3 characters long `string` representing its. + /// three less significant decimal values. + /// @param _u A `uint64` value. + /// @return The `string` representing its decimal value. + function toString(uint8 _u) + internal pure + returns (string memory) + { + if (_u < 10) { + bytes memory b1 = new bytes(1); + b1[0] = bytes1(uint8(_u) + 48); + return string(b1); + } else if (_u < 100) { + bytes memory b2 = new bytes(2); + b2[0] = bytes1(uint8(_u / 10) + 48); + b2[1] = bytes1(uint8(_u % 10) + 48); + return string(b2); + } else { + bytes memory b3 = new bytes(3); + b3[0] = bytes1(uint8(_u / 100) + 48); + b3[1] = bytes1(uint8(_u % 100 / 10) + 48); + b3[2] = bytes1(uint8(_u % 10) + 48); + return string(b3); + } + } + + /// @notice Returns true if Witnet.Result contains an error. + /// @param _result An instance of Witnet.Result. + /// @return `true` if errored, `false` if successful. + function failed(Witnet.Result memory _result) + internal pure + returns (bool) + { + return !_result.success; + } + + /// @notice Returns true if Witnet.Result contains valid result. + /// @param _result An instance of Witnet.Result. + /// @return `true` if errored, `false` if successful. + function succeeded(Witnet.Result memory _result) + internal pure + returns (bool) + { + return _result.success; + } + + /// =============================================================================================================== + /// --- WitnetLib private methods --------------------------------------------------------------------------------- + + /// @notice Decode an errored `Witnet.Result` as a `uint[]`. + /// @param _result An instance of `Witnet.Result`. + /// @return The `uint[]` error parameters as decoded from the `Witnet.Result`. + function _errorsFromResult(Witnet.Result memory _result) + private pure + returns(uint[] memory) + { + require( + failed(_result), + "WitnetLib: no actual error" + ); + return _result.value.readUintArray(); } /// @notice Decode a CBOR value into a Witnet.Result instance. /// @param _cborValue An instance of `Witnet.Value`. /// @return A `Witnet.Result` instance. - function resultFromCborValue(WitnetCBOR.CBOR memory _cborValue) - public pure + function _resultFromCborValue(WitnetCBOR.CBOR memory _cborValue) + private pure returns (Witnet.Result memory) { // Witnet uses CBOR tag 39 to represent RADON error code identifiers. @@ -37,209 +154,279 @@ library WitnetLib { return Witnet.Result(success, _cborValue); } - /// @notice Tell if a Witnet.Result is successful. - /// @param _result An instance of Witnet.Result. - /// @return `true` if successful, `false` if errored. - function isOk(Witnet.Result memory _result) - external pure - returns (bool) + /// @notice Convert a stage index number into the name of the matching Witnet request stage. + /// @param _stageIndex A `uint64` identifying the index of one of the Witnet request stages. + /// @return The name of the matching stage. + function _stageName(uint64 _stageIndex) + private pure + returns (string memory) { - return _result.success; + if (_stageIndex == 0) { + return "retrieval"; + } else if (_stageIndex == 1) { + return "aggregation"; + } else if (_stageIndex == 2) { + return "tally"; + } else { + return "unknown"; + } } - /// @notice Tell if a Witnet.Result is errored. + + /// =============================================================================================================== + /// --- WitnetLib public methods (if used library will have to linked to calling contracts) ----------------------- + + /// ----------------------------- public decoding methods --------------------------------------------------------- + + function asAddress(Witnet.Result memory _result) + public pure + returns (address) + { + require( + _result.success, + "WitnetLib: tried to read `address` from errored result." + ); + if (_result.value.majorType == uint8(WitnetCBOR.MajorTypes.Bytes)) { + return _result.value.readBytes().toAddress(); + } else { + revert("WitnetLib: reading address from string not yet supported."); + } + } + + /// @notice Decode a boolean value from a Witnet.Result as an `bool` value. /// @param _result An instance of Witnet.Result. - /// @return `true` if errored, `false` if successful. - function isError(Witnet.Result memory _result) - external pure - returns (bool) + /// @return The `bool` decoded from the Witnet.Result. + function asBool(Witnet.Result memory _result) + public pure + returns (bool) { - return !_result.success; + require( + _result.success, + "WitnetLib: tried to read `bool` value from errored result." + ); + return _result.value.readBool(); } /// @notice Decode a bytes value from a Witnet.Result as a `bytes` value. /// @param _result An instance of Witnet.Result. /// @return The `bytes` decoded from the Witnet.Result. function asBytes(Witnet.Result memory _result) - external pure + public pure returns(bytes memory) { - require(_result.success, "WitnetLib: Tried to read bytes value from errored Witnet.Result"); - return _result.value.decodeBytes(); + require( + _result.success, + "WitnetLib: Tried to read bytes value from errored Witnet.Result" + ); + return _result.value.readBytes(); + } + + function asBytes4(Witnet.Result memory _result) + public pure + returns (bytes4) + { + return asBytes(_result).toBytes4(); } /// @notice Decode a bytes value from a Witnet.Result as a `bytes32` value. /// @param _result An instance of Witnet.Result. /// @return The `bytes32` decoded from the Witnet.Result. function asBytes32(Witnet.Result memory _result) - external pure - returns(bytes32) + public pure + returns (bytes32) { - require(_result.success, "WitnetLib: tried to read bytes32 value from errored Witnet.Result"); - return _result.value.decodeBytes32(); + return asBytes(_result).toBytes32(); } /// @notice Decode an error code from a Witnet.Result as a member of `Witnet.ErrorCodes`. /// @param _result An instance of `Witnet.Result`. - /// @return The `CBORValue.Error memory` decoded from the Witnet.Result. function asErrorCode(Witnet.Result memory _result) - external pure + public pure returns (Witnet.ErrorCodes) { - uint64[] memory error = asRawError(_result); - if (error.length == 0) { + uint[] memory _errors = _errorsFromResult(_result); + if (_errors.length == 0) { return Witnet.ErrorCodes.Unknown; + } else { + return Witnet.ErrorCodes(_errors[0]); } - return _supportedErrorOrElseUnknown(error[0]); } /// @notice Generate a suitable error message for a member of `Witnet.ErrorCodes` and its corresponding arguments. /// @dev WARN: Note that client contracts should wrap this function into a try-catch foreseing potential errors generated in this function /// @param _result An instance of `Witnet.Result`. - /// @return A tuple containing the `CBORValue.Error memory` decoded from the `Witnet.Result`, plus a loggable error message. + /// @return _errorCode Decoded error code. + /// @return _errorString Decoded error message. function asErrorMessage(Witnet.Result memory _result) - public pure - returns (Witnet.ErrorCodes, string memory) + public pure + returns ( + Witnet.ErrorCodes _errorCode, + string memory _errorString + ) { - uint64[] memory error = asRawError(_result); - if (error.length == 0) { - return (Witnet.ErrorCodes.Unknown, "Unknown error (no error code)"); + uint[] memory _errors = _errorsFromResult(_result); + if (_errors.length == 0) { + return ( + Witnet.ErrorCodes.Unknown, + "Unknown error: no error code." + ); } - Witnet.ErrorCodes errorCode = _supportedErrorOrElseUnknown(error[0]); - bytes memory errorMessage; - - if (errorCode == Witnet.ErrorCodes.SourceScriptNotCBOR && error.length >= 2) { - errorMessage = abi.encodePacked( + else { + _errorCode = Witnet.ErrorCodes(_errors[0]); + } + if ( + _errorCode == Witnet.ErrorCodes.SourceScriptNotCBOR + && _errors.length >= 2 + ) { + _errorString = string(abi.encodePacked( "Source script #", - _utoa(error[1]), + toString(uint8(_errors[1])), " was not a valid CBOR value" - ); - } else if (errorCode == Witnet.ErrorCodes.SourceScriptNotArray && error.length >= 2) { - errorMessage = abi.encodePacked( + )); + } else if ( + _errorCode == Witnet.ErrorCodes.SourceScriptNotArray + && _errors.length >= 2 + ) { + _errorString = string(abi.encodePacked( "The CBOR value in script #", - _utoa(error[1]), + toString(uint8(_errors[1])), " was not an Array of calls" - ); - } else if (errorCode == Witnet.ErrorCodes.SourceScriptNotRADON && error.length >= 2) { - errorMessage = abi.encodePacked( + )); + } else if ( + _errorCode == Witnet.ErrorCodes.SourceScriptNotRADON + && _errors.length >= 2 + ) { + _errorString = string(abi.encodePacked( "The CBOR value in script #", - _utoa(error[1]), + toString(uint8(_errors[1])), " was not a valid Data Request" - ); - } else if (errorCode == Witnet.ErrorCodes.RequestTooManySources && error.length >= 2) { - errorMessage = abi.encodePacked( + )); + } else if ( + _errorCode == Witnet.ErrorCodes.RequestTooManySources + && _errors.length >= 2 + ) { + _errorString = string(abi.encodePacked( "The request contained too many sources (", - _utoa(error[1]), + toString(uint8(_errors[1])), ")" - ); - } else if (errorCode == Witnet.ErrorCodes.ScriptTooManyCalls && error.length >= 4) { - errorMessage = abi.encodePacked( + )); + } else if ( + _errorCode == Witnet.ErrorCodes.ScriptTooManyCalls + && _errors.length >= 4 + ) { + _errorString = string(abi.encodePacked( "Script #", - _utoa(error[2]), + toString(uint8(_errors[2])), " from the ", - stageName(error[1]), + _stageName(uint8(_errors[1])), " stage contained too many calls (", - _utoa(error[3]), + toString(uint8(_errors[3])), ")" - ); - } else if (errorCode == Witnet.ErrorCodes.UnsupportedOperator && error.length >= 5) { - errorMessage = abi.encodePacked( + )); + } else if ( + _errorCode == Witnet.ErrorCodes.UnsupportedOperator + && _errors.length >= 5 + ) { + _errorString = string(abi.encodePacked( "Operator code 0x", - _utohex(error[4]), + toHexString(uint8(_errors[4])), " found at call #", - _utoa(error[3]), + toString(uint8(_errors[3])), " in script #", - _utoa(error[2]), + toString(uint8(_errors[2])), " from ", - stageName(error[1]), + _stageName(uint8(_errors[1])), " stage is not supported" - ); - } else if (errorCode == Witnet.ErrorCodes.HTTP && error.length >= 3) { - errorMessage = abi.encodePacked( + )); + } else if ( + _errorCode == Witnet.ErrorCodes.HTTP + && _errors.length >= 3 + ) { + _errorString = string(abi.encodePacked( "Source #", - _utoa(error[1]), + toString(uint8(_errors[1])), " could not be retrieved. Failed with HTTP error code: ", - _utoa(error[2] / 100), - _utoa(error[2] % 100 / 10), - _utoa(error[2] % 10) - ); - } else if (errorCode == Witnet.ErrorCodes.RetrievalTimeout && error.length >= 2) { - errorMessage = abi.encodePacked( + toString(uint8(_errors[2] / 100)), + toString(uint8(_errors[2] % 100 / 10)), + toString(uint8(_errors[2] % 10)) + )); + } else if ( + _errorCode == Witnet.ErrorCodes.RetrievalTimeout + && _errors.length >= 2 + ) { + _errorString = string(abi.encodePacked( "Source #", - _utoa(error[1]), + toString(uint8(_errors[1])), " could not be retrieved because of a timeout" - ); - } else if (errorCode == Witnet.ErrorCodes.Underflow && error.length >= 5) { - errorMessage = abi.encodePacked( + )); + } else if ( + _errorCode == Witnet.ErrorCodes.Underflow + && _errors.length >= 5 + ) { + _errorString = string(abi.encodePacked( "Underflow at operator code 0x", - _utohex(error[4]), + toHexString(uint8(_errors[4])), " found at call #", - _utoa(error[3]), + toString(uint8(_errors[3])), " in script #", - _utoa(error[2]), + toString(uint8(_errors[2])), " from ", - stageName(error[1]), + _stageName(uint8(_errors[1])), " stage" - ); - } else if (errorCode == Witnet.ErrorCodes.Overflow && error.length >= 5) { - errorMessage = abi.encodePacked( + )); + } else if ( + _errorCode == Witnet.ErrorCodes.Overflow + && _errors.length >= 5 + ) { + _errorString = string(abi.encodePacked( "Overflow at operator code 0x", - _utohex(error[4]), + toHexString(uint8(_errors[4])), " found at call #", - _utoa(error[3]), + toString(uint8(_errors[3])), " in script #", - _utoa(error[2]), + toString(uint8(_errors[2])), " from ", - stageName(error[1]), + _stageName(uint8(_errors[1])), " stage" - ); - } else if (errorCode == Witnet.ErrorCodes.DivisionByZero && error.length >= 5) { - errorMessage = abi.encodePacked( + )); + } else if ( + _errorCode == Witnet.ErrorCodes.DivisionByZero + && _errors.length >= 5 + ) { + _errorString = string(abi.encodePacked( "Division by zero at operator code 0x", - _utohex(error[4]), + toHexString(uint8(_errors[4])), " found at call #", - _utoa(error[3]), + toString(uint8(_errors[3])), " in script #", - _utoa(error[2]), + toString(uint8(_errors[2])), " from ", - stageName(error[1]), + _stageName(uint8(_errors[1])), " stage" - ); - } else if (errorCode == Witnet.ErrorCodes.BridgeMalformedRequest) { - errorMessage = "The structure of the request is invalid and it cannot be parsed"; - } else if (errorCode == Witnet.ErrorCodes.BridgePoorIncentives) { - errorMessage = "The request has been rejected by the bridge node due to poor incentives"; - } else if (errorCode == Witnet.ErrorCodes.BridgeOversizedResult) { - errorMessage = "The request result length exceeds a bridge contract defined limit"; + )); + } else if ( + _errorCode == Witnet.ErrorCodes.BridgeMalformedRequest + ) { + _errorString = "The structure of the request is invalid and it cannot be parsed"; + } else if ( + _errorCode == Witnet.ErrorCodes.BridgePoorIncentives + ) { + _errorString = "The request has been rejected by the bridge node due to poor incentives"; + } else if ( + _errorCode == Witnet.ErrorCodes.BridgeOversizedResult + ) { + _errorString = "The request result length exceeds a bridge contract defined limit"; } else { - errorMessage = abi.encodePacked("Unknown error (0x", _utohex(error[0]), ")"); + _errorString = string(abi.encodePacked( + "Unknown error (0x", + toHexString(uint8(_errors[0])), + ")" + )); } - return (errorCode, string(errorMessage)); - } - - /// @notice Decode a raw error from a `Witnet.Result` as a `uint64[]`. - /// @param _result An instance of `Witnet.Result`. - /// @return The `uint64[]` raw error as decoded from the `Witnet.Result`. - function asRawError(Witnet.Result memory _result) - public pure - returns(uint64[] memory) - { - require( - !_result.success, - "WitnetLib: Tried to read error code from successful Witnet.Result" + return ( + _errorCode, + _errorString ); - return _result.value.decodeUint64Array(); - } - - /// @notice Decode a boolean value from a Witnet.Result as an `bool` value. - /// @param _result An instance of Witnet.Result. - /// @return The `bool` decoded from the Witnet.Result. - function asBool(Witnet.Result memory _result) - external pure - returns (bool) - { - require(_result.success, "WitnetLib: Tried to read `bool` value from errored Witnet.Result"); - return _result.value.decodeBool(); } /// @notice Decode a fixed16 (half-precision) numeric value from a Witnet.Result as an `int32` value. @@ -249,88 +436,111 @@ library WitnetLib { /// @param _result An instance of Witnet.Result. /// @return The `int128` decoded from the Witnet.Result. function asFixed16(Witnet.Result memory _result) - external pure + public pure returns (int32) { - require(_result.success, "WitnetLib: Tried to read `fixed16` value from errored Witnet.Result"); - return _result.value.decodeFixed16(); + require( + _result.success, + "WitnetLib: tried to read `fixed16` value from errored result." + ); + return _result.value.readFloat16(); } - /// @notice Decode an array of fixed16 values from a Witnet.Result as an `int128[]` value. + /// @notice Decode an array of fixed16 values from a Witnet.Result as an `int32[]` array. /// @param _result An instance of Witnet.Result. /// @return The `int128[]` decoded from the Witnet.Result. function asFixed16Array(Witnet.Result memory _result) - external pure + public pure returns (int32[] memory) { - require(_result.success, "WitnetLib: Tried to read `fixed16[]` value from errored Witnet.Result"); - return _result.value.decodeFixed16Array(); + require( + _result.success, + "WitnetLib: tried to read `fixed16[]` value from errored result." + ); + return _result.value.readFloat16Array(); } /// @notice Decode a integer numeric value from a Witnet.Result as an `int128` value. /// @param _result An instance of Witnet.Result. - /// @return The `int128` decoded from the Witnet.Result. - function asInt128(Witnet.Result memory _result) - external pure - returns (int128) + /// @return The `int` decoded from the Witnet.Result. + function asInt(Witnet.Result memory _result) + public pure + returns (int) { - require(_result.success, "WitnetLib: Tried to read `int128` value from errored Witnet.Result"); - return _result.value.decodeInt128(); + require( + _result.success, + "WitnetLib: tried to read `int` value from errored result." + ); + return _result.value.readInt(); } - /// @notice Decode an array of integer numeric values from a Witnet.Result as an `int128[]` value. + /// @notice Decode an array of integer numeric values from a Witnet.Result as an `int[]` array. /// @param _result An instance of Witnet.Result. - /// @return The `int128[]` decoded from the Witnet.Result. - function asInt128Array(Witnet.Result memory _result) - external pure - returns (int128[] memory) + /// @return The `int[]` decoded from the Witnet.Result. + function asIntArray(Witnet.Result memory _result) + public pure + returns (int[] memory) { - require(_result.success, "WitnetLib: Tried to read `int128[]` value from errored Witnet.Result"); - return _result.value.decodeInt128Array(); + require( + _result.success, + "WitnetLib: tried to read `int[]` value from errored result." + ); + return _result.value.readIntArray(); } /// @notice Decode a string value from a Witnet.Result as a `string` value. /// @param _result An instance of Witnet.Result. /// @return The `string` decoded from the Witnet.Result. function asString(Witnet.Result memory _result) - external pure + public pure returns(string memory) { - require(_result.success, "WitnetLib: Tried to read `string` value from errored Witnet.Result"); - return _result.value.decodeString(); + require( + _result.success, + "WitnetLib: tried to read `string` value from errored result." + ); + return _result.value.readString(); } /// @notice Decode an array of string values from a Witnet.Result as a `string[]` value. /// @param _result An instance of Witnet.Result. /// @return The `string[]` decoded from the Witnet.Result. function asStringArray(Witnet.Result memory _result) - external pure + public pure returns (string[] memory) { - require(_result.success, "WitnetLib: Tried to read `string[]` value from errored Witnet.Result"); - return _result.value.decodeStringArray(); + require( + _result.success, + "WitnetLib: tried to read `string[]` value from errored result."); + return _result.value.readStringArray(); } - /// @notice Decode a natural numeric value from a Witnet.Result as a `uint64` value. + /// @notice Decode a natural numeric value from a Witnet.Result as a `uint` value. /// @param _result An instance of Witnet.Result. - /// @return The `uint64` decoded from the Witnet.Result. - function asUint64(Witnet.Result memory _result) - external pure - returns(uint64) + /// @return The `uint` decoded from the Witnet.Result. + function asUint(Witnet.Result memory _result) + public pure + returns(uint) { - require(_result.success, "WitnetLib: Tried to read `uint64` value from errored Witnet.Result"); - return _result.value.decodeUint64(); + require( + _result.success, + "WitnetLib: tried to read `uint64` value from errored result" + ); + return _result.value.readUint(); } - /// @notice Decode an array of natural numeric values from a Witnet.Result as a `uint64[]` value. + /// @notice Decode an array of natural numeric values from a Witnet.Result as a `uint[]` value. /// @param _result An instance of Witnet.Result. - /// @return The `uint64[]` decoded from the Witnet.Result. - function asUint64Array(Witnet.Result memory _result) - external pure - returns (uint64[] memory) + /// @return The `uint[]` decoded from the Witnet.Result. + function asUintArray(Witnet.Result memory _result) + public pure + returns (uint[] memory) { - require(_result.success, "WitnetLib: Tried to read `uint64[]` value from errored Witnet.Result"); - return _result.value.decodeUint64Array(); + require( + _result.success, + "WitnetLib: tried to read `uint[]` value from errored result." + ); + return _result.value.readUintArray(); } /// @notice Convert a stage index number into the name of the matching Witnet request stage. @@ -405,4 +615,16 @@ library WitnetLib { b2[1] = bytes1(d1); return string(b2); } -} + + /// @notice Decode raw CBOR bytes into a Witnet.Result instance. + /// @param _cborBytes Raw bytes representing a CBOR-encoded value. + /// @return A `Witnet.Result` instance. + function resultFromCborBytes(bytes memory _cborBytes) + public pure + returns (Witnet.Result memory) + { + WitnetCBOR.CBOR memory cborValue = WitnetCBOR.valueFromBytes(_cborBytes); + return _resultFromCborValue(cborValue); + } + +} \ No newline at end of file From 954c4d0c3cede214bc504496f41ade1e8a93deb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 25 Nov 2022 10:55:38 +0100 Subject: [PATCH 011/119] chore(v2): keep polishing interfaces and data structs --- .../WitnetRequestBoardTrustableBase.sol | 99 ++++++-------- .../WitnetRequestBoardTrustlessBase.sol | 8 +- contracts/interfaces/IWitnetRequestParser.sol | 46 +++---- contracts/interfaces/V2/IWitnetDecoder.sol | 68 +++++----- contracts/interfaces/V2/IWitnetRequests.sol | 2 +- contracts/libs/WitnetV2.sol | 128 ++++++++++++++---- 6 files changed, 200 insertions(+), 151 deletions(-) diff --git a/contracts/impls/boards/trustable/WitnetRequestBoardTrustableBase.sol b/contracts/impls/boards/trustable/WitnetRequestBoardTrustableBase.sol index 4692f04ee..531c95684 100644 --- a/contracts/impls/boards/trustable/WitnetRequestBoardTrustableBase.sol +++ b/contracts/impls/boards/trustable/WitnetRequestBoardTrustableBase.sol @@ -69,7 +69,7 @@ abstract contract WitnetRequestBoardTrustableBase } else { // only owner can initialize: require(msg.sender == _owner, "WitnetRequestBoardTrustableBase: only owner"); - } + } if (_state().base != address(0)) { // current implementation cannot be initialized more than once: @@ -590,17 +590,6 @@ abstract contract WitnetRequestBoardTrustableBase return WitnetLib.resultFromCborBytes(_cborBytes); } - /// Decode a CBOR value into a Witnet.Result instance. - /// @param _cborValue An instance of `Witnet.CBOR`. - /// @return A `Witnet.Result` instance. - function resultFromCborValue(WitnetCBOR.CBOR memory _cborValue) - external pure - override - returns (Witnet.Result memory) - { - return WitnetLib.resultFromCborValue(_cborValue); - } - /// Tell if a Witnet.Result is successful. /// @param _result An instance of Witnet.Result. /// @return `true` if successful, `false` if errored. @@ -609,7 +598,7 @@ abstract contract WitnetRequestBoardTrustableBase override returns (bool) { - return _result.isOk(); + return _result.succeeded(); } /// Tell if a Witnet.Result is errored. @@ -620,7 +609,18 @@ abstract contract WitnetRequestBoardTrustableBase override returns (bool) { - return _result.isError(); + return _result.failed(); + } + + /// Decode a boolean value from a Witnet.Result as an `bool` value. + /// @param _result An instance of Witnet.Result. + /// @return The `bool` decoded from the Witnet.Result. + function asBool(Witnet.Result memory _result) + external pure + override + returns (bool) + { + return _result.asBool(); } /// Decode a bytes value from a Witnet.Result as a `bytes` value. @@ -644,7 +644,7 @@ abstract contract WitnetRequestBoardTrustableBase { return _result.asBytes32(); } - + /// Decode an error code from a Witnet.Result as a member of `Witnet.ErrorCodes`. /// @param _result An instance of `Witnet.Result`. /// @return The `CBORValue.Error memory` decoded from the Witnet.Result. @@ -665,39 +665,28 @@ abstract contract WitnetRequestBoardTrustableBase override returns (Witnet.ErrorCodes, string memory) { - try _result.asErrorMessage() returns (Witnet.ErrorCodes _code, string memory _message) { + try _result.asErrorMessage() + returns ( + Witnet.ErrorCodes _code, + string memory _message + ) + { return (_code, _message); } catch Error(string memory _reason) { - return (Witnet.ErrorCodes.Unknown, _reason); + return ( + Witnet.ErrorCodes.Unknown, + _reason + ); } catch (bytes memory) { - return (Witnet.ErrorCodes.UnhandledIntercept, "WitnetRequestBoardTrustableBase: failing assert"); + return ( + Witnet.ErrorCodes.UnhandledIntercept, + "WitnetRequestBoardTrustableBase: failing assert" + ); } } - /// Decode a raw error from a `Witnet.Result` as a `uint64[]`. - /// @param _result An instance of `Witnet.Result`. - /// @return The `uint64[]` raw error as decoded from the `Witnet.Result`. - function asRawError(Witnet.Result memory _result) - external pure - override - returns(uint64[] memory) - { - return _result.asRawError(); - } - - /// Decode a boolean value from a Witnet.Result as an `bool` value. - /// @param _result An instance of Witnet.Result. - /// @return The `bool` decoded from the Witnet.Result. - function asBool(Witnet.Result memory _result) - external pure - override - returns (bool) - { - return _result.asBool(); - } - /// Decode a fixed16 (half-precision) numeric value from a Witnet.Result as an `int32` value. /// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values. /// by 5 decimal orders so as to get a fixed precision of 5 decimal positions, which should be OK for most `fixed16`. @@ -723,26 +712,26 @@ abstract contract WitnetRequestBoardTrustableBase return _result.asFixed16Array(); } - /// Decode a integer numeric value from a Witnet.Result as an `int128` value. + /// Decode a integer numeric value from a Witnet.Result as an `int` value. /// @param _result An instance of Witnet.Result. - /// @return The `int128` decoded from the Witnet.Result. + /// @return The `int` decoded from the Witnet.Result. function asInt128(Witnet.Result memory _result) external pure override - returns (int128) + returns (int) { - return _result.asInt128(); + return _result.asInt(); } - /// Decode an array of integer numeric values from a Witnet.Result as an `int128[]` value. + /// Decode an array of integer numeric values from a Witnet.Result as an `int[]` value. /// @param _result An instance of Witnet.Result. /// @return The `int128[]` decoded from the Witnet.Result. function asInt128Array(Witnet.Result memory _result) external pure override - returns (int128[] memory) + returns (int[] memory) { - return _result.asInt128Array(); + return _result.asIntArray(); } /// Decode a string value from a Witnet.Result as a `string` value. @@ -767,26 +756,26 @@ abstract contract WitnetRequestBoardTrustableBase return _result.asStringArray(); } - /// Decode a natural numeric value from a Witnet.Result as a `uint64` value. + /// Decode a natural numeric value from a Witnet.Result as a `uint` value. /// @param _result An instance of Witnet.Result. - /// @return The `uint64` decoded from the Witnet.Result. + /// @return The `uint` decoded from the Witnet.Result. function asUint64(Witnet.Result memory _result) external pure override - returns(uint64) + returns (uint) { - return _result.asUint64(); + return _result.asUint(); } - /// Decode an array of natural numeric values from a Witnet.Result as a `uint64[]` value. + /// Decode an array of natural numeric values from a Witnet.Result as a `uint[]` value. /// @param _result An instance of Witnet.Result. - /// @return The `uint64[]` decoded from the Witnet.Result. + /// @return The `uint[]` decoded from the Witnet.Result. function asUint64Array(Witnet.Result memory _result) external pure override - returns (uint64[] memory) + returns (uint[] memory) { - return _result.asUint64Array(); + return _result.asUintArray(); } diff --git a/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol b/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol index a96efde52..85ffde368 100644 --- a/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol +++ b/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol @@ -282,7 +282,7 @@ abstract contract WitnetRequestBoardTrustlessBase { return ( estimateReportFee(_drRadHash, _gasPrice) - + __board().bytecodes.lookupDrSlaReward(_drSlaHash) * _witPrice + + __board().bytecodes.lookupRadonSLAReward(_drSlaHash) * _witPrice ); } @@ -329,7 +329,7 @@ abstract contract WitnetRequestBoardTrustlessBase drPostNotDeleted(_drHash) returns (bytes memory) { - return __drPostResponse(_drHash).drTallyResultBytes; + return __drPostResponse(_drHash).drTallyResultCborBytes; } function serviceStats() @@ -471,7 +471,7 @@ abstract contract WitnetRequestBoardTrustlessBase uint256 _drCommitTxEpoch, uint256 _drTallyTxEpoch, bytes32 _drTallyTxHash, - bytes calldata _drTallyResultBytes + bytes calldata _drTallyResultCborBytes ) external payable virtual override @@ -507,7 +507,7 @@ abstract contract WitnetRequestBoardTrustlessBase // drCommitTxEpoch: _drCommitTxEpoch, // drTallyTxEpoch: _drTallyTxEpoch, // drTallyTxHash: _drTallyTxHash, - // drTallyResultBytes: _drTallyResultBytes + // drTallyResultCborBytes: _drTallyResultCborBytes // }); // __board().serviceStats.totalReports ++; } diff --git a/contracts/interfaces/IWitnetRequestParser.sol b/contracts/interfaces/IWitnetRequestParser.sol index 4de3c88b1..0aefa35f9 100644 --- a/contracts/interfaces/IWitnetRequestParser.sol +++ b/contracts/interfaces/IWitnetRequestParser.sol @@ -4,7 +4,6 @@ pragma solidity >=0.7.0 <0.9.0; pragma experimental ABIEncoderV2; import "../libs/Witnet.sol"; -import "../libs/WitnetCBOR.sol"; /// @title The Witnet interface for decoding Witnet-provided request to Data Requests. /// This interface exposes functions to check for the success/failure of @@ -18,11 +17,6 @@ interface IWitnetRequestParser { /// @return A `Witnet.Result` instance. function resultFromCborBytes(bytes memory _cborBytes) external pure returns (Witnet.Result memory); - /// Decode a CBOR value into a Witnet.Result instance. - /// @param _cborValue An instance of `Witnet.CBOR`. - /// @return A `Witnet.Result` instance. - function resultFromCborValue(WitnetCBOR.CBOR memory _cborValue) external pure returns (Witnet.Result memory); - /// Tell if a Witnet.Result is successful. /// @param _result An instance of Witnet.Result. /// @return `true` if successful, `false` if errored. @@ -33,6 +27,11 @@ interface IWitnetRequestParser { /// @return `true` if errored, `false` if successful. function isError(Witnet.Result memory _result) external pure returns (bool); + /// Decode a boolean value from a Witnet.Result as an `bool` value. + /// @param _result An instance of Witnet.Result. + /// @return The `bool` decoded from the Witnet.Result. + function asBool(Witnet.Result memory _result) external pure returns (bool); + /// Decode a bytes value from a Witnet.Result as a `bytes` value. /// @param _result An instance of Witnet.Result. /// @return The `bytes` decoded from the Witnet.Result. @@ -48,45 +47,34 @@ interface IWitnetRequestParser { /// @return The `CBORValue.Error memory` decoded from the Witnet.Result. function asErrorCode(Witnet.Result memory _result) external pure returns (Witnet.ErrorCodes); - /// Generate a suitable error message for a member of `Witnet.ErrorCodes` and its corresponding arguments. /// @dev WARN: Note that client contracts should wrap this function into a try-catch foreseing potential errors generated in this function /// @param _result An instance of `Witnet.Result`. /// @return A tuple containing the `CBORValue.Error memory` decoded from the `Witnet.Result`, plus a loggable error message. function asErrorMessage(Witnet.Result memory _result) external pure returns (Witnet.ErrorCodes, string memory); - /// Decode a raw error from a `Witnet.Result` as a `uint64[]`. - /// @param _result An instance of `Witnet.Result`. - /// @return The `uint64[]` raw error as decoded from the `Witnet.Result`. - function asRawError(Witnet.Result memory _result) external pure returns(uint64[] memory); - - /// Decode a boolean value from a Witnet.Result as an `bool` value. - /// @param _result An instance of Witnet.Result. - /// @return The `bool` decoded from the Witnet.Result. - function asBool(Witnet.Result memory _result) external pure returns (bool); - /// Decode a fixed16 (half-precision) numeric value from a Witnet.Result as an `int32` value. /// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values. /// by 5 decimal orders so as to get a fixed precision of 5 decimal positions, which should be OK for most `fixed16`. /// use cases. In other words, the output of this method is 10,000 times the actual value, encoded into an `int32`. /// @param _result An instance of Witnet.Result. - /// @return The `int128` decoded from the Witnet.Result. + /// @return The `int32` decoded from the Witnet.Result. function asFixed16(Witnet.Result memory _result) external pure returns (int32); - /// Decode an array of fixed16 values from a Witnet.Result as an `int128[]` value. + /// Decode an array of fixed16 values from a Witnet.Result as an `int32[]` value. /// @param _result An instance of Witnet.Result. - /// @return The `int128[]` decoded from the Witnet.Result. + /// @return The `int32[]` decoded from the Witnet.Result. function asFixed16Array(Witnet.Result memory _result) external pure returns (int32[] memory); /// Decode a integer numeric value from a Witnet.Result as an `int128` value. /// @param _result An instance of Witnet.Result. - /// @return The `int128` decoded from the Witnet.Result. - function asInt128(Witnet.Result memory _result) external pure returns (int128); + /// @return The `int` decoded from the Witnet.Result. + function asInt128(Witnet.Result memory _result) external pure returns (int); /// Decode an array of integer numeric values from a Witnet.Result as an `int128[]` value. /// @param _result An instance of Witnet.Result. /// @return The `int128[]` decoded from the Witnet.Result. - function asInt128Array(Witnet.Result memory _result) external pure returns (int128[] memory); + function asInt128Array(Witnet.Result memory _result) external pure returns (int[] memory); /// Decode a string value from a Witnet.Result as a `string` value. /// @param _result An instance of Witnet.Result. @@ -98,14 +86,14 @@ interface IWitnetRequestParser { /// @return The `string[]` decoded from the Witnet.Result. function asStringArray(Witnet.Result memory _result) external pure returns (string[] memory); - /// Decode a natural numeric value from a Witnet.Result as a `uint64` value. + /// Decode a natural numeric value from a Witnet.Result as a `uint` value. /// @param _result An instance of Witnet.Result. - /// @return The `uint64` decoded from the Witnet.Result. - function asUint64(Witnet.Result memory _result) external pure returns(uint64); + /// @return The `uint` decoded from the Witnet.Result. + function asUint64(Witnet.Result memory _result) external pure returns (uint); - /// Decode an array of natural numeric values from a Witnet.Result as a `uint64[]` value. + /// Decode an array of natural numeric values from a Witnet.Result as a `uint[]` value. /// @param _result An instance of Witnet.Result. - /// @return The `uint64[]` decoded from the Witnet.Result. - function asUint64Array(Witnet.Result memory _result) external pure returns (uint64[] memory); + /// @return The `uint[]` decoded from the Witnet.Result. + function asUint64Array(Witnet.Result memory _result) external pure returns (uint[] memory); } diff --git a/contracts/interfaces/V2/IWitnetDecoder.sol b/contracts/interfaces/V2/IWitnetDecoder.sol index a051c498a..9698d4e23 100644 --- a/contracts/interfaces/V2/IWitnetDecoder.sol +++ b/contracts/interfaces/V2/IWitnetDecoder.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.0 <0.9.0; -import "../../libs/WitnetV2.sol"; +import "../../libs/Witnet.sol"; /// @title The Witnet interface for decoding Witnet-provided request to Data Requests. /// This interface exposes functions to check for the success/failure of @@ -23,40 +23,40 @@ interface IWitnetDecoder { /// Decode a boolean value from a Witnet.Result as an `bool` value. /// @param _result An instance of Witnet.Result. /// @return The `bool` decoded from the Witnet.Result. - function toBool(Witnet.Result memory _result) external pure returns (bool); + function asBool(Witnet.Result memory _result) external pure returns (bool); /// Decode a bytes value from a Witnet.Result as a `bytes` value. /// @param _result An instance of Witnet.Result. /// @return The `bytes` decoded from the Witnet.Result. - function toBytes(Witnet.Result memory _result) external pure returns (bytes memory); + function asBytes(Witnet.Result memory _result) external pure returns (bytes memory); /// Decode a bytes value from a Witnet.Result as a `bytes32` value. /// @param _result An instance of Witnet.Result. /// @return The `bytes32` decoded from the Witnet.Result. - function toBytes32(Witnet.Result memory _result) external pure returns (bytes32); + function asBytes32(Witnet.Result memory _result) external pure returns (bytes32); /// Decode a fixed16 (half-precision) numeric value from a Witnet.Result as an `int32` value. /// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values. /// by 5 decimal orders so as to get a fixed precision of 5 decimal positions, which should be OK for most `fixed16`. /// use cases. In other words, the output of this method is 10,000 times the actual value, encoded into an `int32`. /// @param _result An instance of Witnet.Result. - /// @return The `int128` decoded from the Witnet.Result. - function toInt32(Witnet.Result memory _result) external pure returns (int32); + /// @return The `int32` decoded from the Witnet.Result. + function asFixedFloat16(Witnet.Result memory _result) external pure returns (int32); - /// Decode an array of fixed16 values from a Witnet.Result as an `int128[]` value. + /// Decode an array of fixed16 values from a Witnet.Result as an `int32[]` value. /// @param _result An instance of Witnet.Result. - /// @return The `int128[]` decoded from the Witnet.Result. - function toInt32Array(Witnet.Result memory _result) external pure returns (int32[] memory); + /// @return The `int32[]` decoded from the Witnet.Result. + function asFixedFloat16Array(Witnet.Result memory _result) external pure returns (int32[] memory); - /// Decode a integer numeric value from a Witnet.Result as an `int128` value. + /// Decode a integer numeric value from a Witnet.Result as an `int` value. /// @param _result An instance of Witnet.Result. - /// @return The `int128` decoded from the Witnet.Result. - function toInt128(Witnet.Result memory _result) external pure returns (int128); + /// @return The `int` decoded from the Witnet.Result. + function toInt(Witnet.Result memory _result) external pure returns (int); - /// Decode an array of integer numeric values from a Witnet.Result as an `int128[]` value. + /// Decode an array of integer numeric values from a Witnet.Result as an `int[]` value. /// @param _result An instance of Witnet.Result. - /// @return The `int128[]` decoded from the Witnet.Result. - function toInt128Array(Witnet.Result memory _result) external pure returns (int128[] memory); + /// @return The `int[]` decoded from the Witnet.Result. + function toIntArray(Witnet.Result memory _result) external pure returns (int[] memory); /// Decode a string value from a Witnet.Result as a `string` value. /// @param _result An instance of Witnet.Result. @@ -68,15 +68,15 @@ interface IWitnetDecoder { /// @return The `string[]` decoded from the Witnet.Result. function toStringArray(Witnet.Result memory _result) external pure returns (string[] memory); - /// Decode a natural numeric value from a Witnet.Result as a `uint64` value. + /// Decode a natural numeric value from a Witnet.Result as a `uint` value. /// @param _result An instance of Witnet.Result. - /// @return The `uint64` decoded from the Witnet.Result. - function toUint64(Witnet.Result memory _result) external pure returns(uint64); + /// @return The `uint` decoded from the Witnet.Result. + function toUint(Witnet.Result memory _result) external pure returns(uint); - /// Decode an array of natural numeric values from a Witnet.Result as a `uint64[]` value. + /// Decode an array of natural numeric values from a Witnet.Result as a `uint[]` value. /// @param _result An instance of Witnet.Result. - /// @return The `uint64[]` decoded from the Witnet.Result. - function toUint64Array(Witnet.Result memory _result) external pure returns (uint64[] memory); + /// @return The `uint[]` decoded from the Witnet.Result. + function toUintArray(Witnet.Result memory _result) external pure returns (uint64[] memory); /// Decode an error code from a Witnet.Result as a member of `WitnetV2.ErrorCodes`. /// @param _result An instance of `Witnet.Result`. @@ -89,19 +89,15 @@ interface IWitnetDecoder { /// @return A tuple containing the `CBORValue.Error memory` decoded from the `Witnet.Result`, plus a loggable error message. function getErrorMessage(Witnet.Result memory _result) external pure returns (Witnet.ErrorCodes, string memory); - /// Decode a raw error from a `Witnet.Result` as a `uint64[]`. - /// @param _result An instance of `Witnet.Result`. - /// @return The `uint64[]` raw error as decoded from the `Witnet.Result`. - function getRawError(Witnet.Result memory _result) external pure returns(uint64[] memory); - - function isArray(Witnet.Result memory _result) external pure returns (bool); - function getArrayLength(Witnet.Result memory _result) external pure returns (bool); - function getTypeAndSize(Witnet.Result memory _result) external pure returns (WitnetV2.Types, uint); - function getAddressAt(Witnet.Result memory _result, uint _indexes) external pure returns (address); - function getBoolAt(Witnet.Result memory _result, uint _indexes) external pure returns (bool); - function getBytesAt(Witnet.Result memory _result, uint _indexes) external pure returns (bytes memory); - function getInt256At(Witnet.Result memory _result, uint _indexes) external pure returns (int256); - function getStringAt(Witnet.Result memory _result, uint _indexes) external pure returns (string memory); - function getUint256At(Witnet.Result memory _result, uint _indexes) external pure returns (uint256); - function toAddress(Witnet.Result memory _result) external pure returns (address); + + // function isArray(Witnet.Result memory _result) external pure returns (bool); + // function getArrayLength(Witnet.Result memory _result) external pure returns (bool); + // function getTypeAndSize(Witnet.Result memory _result) external pure returns (WitnetV2.RadonDataTypes, uint); + // function getAddressAt(Witnet.Result memory _result, uint _indexes) external pure returns (address); + // function getBoolAt(Witnet.Result memory _result, uint _indexes) external pure returns (bool); + // function getBytesAt(Witnet.Result memory _result, uint _indexes) external pure returns (bytes memory); + // function getInt256At(Witnet.Result memory _result, uint _indexes) external pure returns (int256); + // function getStringAt(Witnet.Result memory _result, uint _indexes) external pure returns (string memory); + // function getUint256At(Witnet.Result memory _result, uint _indexes) external pure returns (uint256); + // function toAddress(Witnet.Result memory _result) external pure returns (address); } diff --git a/contracts/interfaces/V2/IWitnetRequests.sol b/contracts/interfaces/V2/IWitnetRequests.sol index f7910838e..38d16715d 100644 --- a/contracts/interfaces/V2/IWitnetRequests.sol +++ b/contracts/interfaces/V2/IWitnetRequests.sol @@ -51,7 +51,7 @@ interface IWitnetRequests { uint256 _drCommitTxEpoch, uint256 _drTallyTxEpoch, bytes32 _drTallyTxHash, - bytes calldata _drTallyResultBytes + bytes calldata _drTallyResultCborBytes ) external payable; function upgradeDrPostReward(bytes32 _drHash) external payable; function verifyDrPost( diff --git a/contracts/libs/WitnetV2.sol b/contracts/libs/WitnetV2.sol index da37415bd..2d1e9047f 100644 --- a/contracts/libs/WitnetV2.sol +++ b/contracts/libs/WitnetV2.sol @@ -9,8 +9,7 @@ library WitnetV2 { error IndexOutOfBounds(uint256 index, uint256 range); error InsufficientBalance(uint256 weiBalance, uint256 weiExpected); error InsufficientFee(uint256 weiProvided, uint256 weiExpected); - error Unauthorized(address violator); - + error Unauthorized(address violator); function toEpoch(uint _timestamp) internal pure returns (uint) { return 1 + (_timestamp - 11111) / 15; @@ -44,33 +43,13 @@ library WitnetV2 { Idle } - struct DrSla { - uint64 witnessReward; - uint16 numwitnesses; - uint64 commitRevelFee; - uint32 minConsensusPercentage; - uint64 collateral; - } - struct DrPost { uint256 block; DrPostStatus status; DrPostRequest request; DrPostResponse response; } - - enum DrPostStatus { - Void, - Deleted, - Expired, - Posted, - Disputed, - Reported, - Finalized, - Accepted, - Rejected - } - + /// Data kept in EVM-storage for every Request posted to the Witnet Request Board. struct DrPostRequest { uint256 epoch; @@ -89,11 +68,108 @@ library WitnetV2 { uint256 drCommitTxEpoch; uint256 drTallyTxEpoch; bytes32 drTallyTxHash; - bytes drTallyResultBytes; + bytes drTallyResultCborBytes; } - enum Types { - Integer + enum DrPostStatus { + Void, + Deleted, + Expired, + Posted, + Disputed, + Reported, + Finalized, + Accepted, + Rejected + } + + struct DataProvider { + string fqdn; + uint256 totalSources; + uint256 totalRetrievals; + } + + enum DataRequestMethods { + /* 0 */ Unknown, + /* 1 */ HttpGet, + /* 2 */ Rng, + /* 3 */ HttpPost + } + + struct DataSource { + DataRequestMethods method; + RadonDataTypes resultType; + string url; + string body; + string[2][] headers; + bytes script; + } + + enum RadonDataTypes { + /* 0x0 */ Any, + /* 0x1 */ Array, + /* 0x2 */ Bool, + /* 0x3 */ Bytes, + /* 0x4 */ Integer, + /* 0x5 */ Float, + /* 0x6 */ Map, + /* 0x7 */ String + } + + struct RadonFilter { + RadonFilterOpcodes op; + bytes cborArgs; } + enum RadonFilterOpcodes { + /* 0x00 */ GreaterThan, + /* 0x01 */ LessThan, + /* 0x02 */ Equals, + /* 0x03 */ AbsoluteDeviation, + /* 0x04 */ RelativeDeviation, + /* 0x05 */ StandardDeviation, + /* 0x06 */ Top, + /* 0x07 */ Bottom, + /* 0x08 */ Mode, + /* 0x09 */ LessOrEqualThan + } + + struct RadonReducer { + RadonReducerOpcodes op; + RadonFilter[] filters; + } + + enum RadonReducerOpcodes { + /* 0x00 */ Minimum, + /* 0x01 */ Maximum, + /* 0x02 */ Mode, + /* 0x03 */ AverageMean, + /* 0x04 */ AverageMeanWeighted, + /* 0x05 */ AverageMedian, + /* 0x06 */ AverageMedianWeighted, + /* 0x07 */ StandardDeviation, + /* 0x08 */ AverageDeviation, + /* 0x09 */ MedianDeviation, + /* 0x0A */ MaximumDeviation, + /* 0x0B */ ConcatenateAndHash + } + + struct RadonSLA { + uint64 witnessReward; + uint16 numWitnesses; + uint64 commitRevealFee; + uint32 minConsensusPercentage; + uint64 collateral; + } + + + // // ================================================================================================================ + // // --- Internal view/pure methods --------------------------------------------------------------------------------- + + // /// @notice Witnet function that computes the hash of a CBOR-encoded Data Request. + // /// @param _bytecode CBOR-encoded RADON. + // function hash(bytes memory _bytecode) internal pure returns (bytes32) { + // return sha256(_bytecode); + // } + } \ No newline at end of file From 6310bc4024119be8fd5a564722b935411f462880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 25 Nov 2022 10:56:39 +0100 Subject: [PATCH 012/119] feat(libs): add encoding methods --- contracts/libs/WitnetLib.sol | 138 ++++++++++++++++++++++++----------- 1 file changed, 95 insertions(+), 43 deletions(-) diff --git a/contracts/libs/WitnetLib.sol b/contracts/libs/WitnetLib.sol index 9723a62cc..3cfccad76 100644 --- a/contracts/libs/WitnetLib.sol +++ b/contracts/libs/WitnetLib.sol @@ -543,58 +543,110 @@ library WitnetLib { return _result.value.readUintArray(); } - /// @notice Convert a stage index number into the name of the matching Witnet request stage. - /// @param _stageIndex A `uint64` identifying the index of one of the Witnet request stages. - /// @return The name of the matching stage. - function stageName(uint64 _stageIndex) + /// ----------------------------- public encoding methods --------------------------------------------------------- + + /// @dev Encode uint64 into tagged varint. + /// @dev See https://developers.google.com/protocol-buffers/docs/encoding#varints. + /// @param n Number + /// @param t Tag + /// @return buf Marshaled bytes + function encode(uint64 n, bytes1 t) public pure - returns (string memory) + returns (bytes memory buf) { - if (_stageIndex == 0) { - return "retrieval"; - } else if (_stageIndex == 1) { - return "aggregation"; - } else if (_stageIndex == 2) { - return "tally"; - } else { - return "unknown"; + unchecked { + // Count the number of groups of 7 bits + // We need this pre-processing step since Solidity doesn't allow dynamic memory resizing + uint64 tmp = n; + uint64 numBytes = 2; + while (tmp > 0x7F) { + tmp = tmp >> 7; + numBytes += 1; + } + buf = new bytes(numBytes); + tmp = n; + buf[0] = t; + for (uint64 i = 1; i < numBytes; i++) { + // Set the first bit in the byte for each group of 7 bits + buf[i] = bytes1(0x80 | uint8(tmp & 0x7F)); + tmp = tmp >> 7; + } + // Unset the first bit of the last byte + buf[numBytes - 1] &= 0x7F; } } - /// @notice Get an `Witnet.ErrorCodes` item from its `uint64` discriminant. - /// @param _discriminant The numeric identifier of an error. - /// @return A member of `Witnet.ErrorCodes`. - function _supportedErrorOrElseUnknown(uint64 _discriminant) - private pure - returns (Witnet.ErrorCodes) + function encode(WitnetV2.DataSource memory _dds) + public pure + returns (bytes memory) { - return Witnet.ErrorCodes(_discriminant); + assert(_dds.headers[0].length == _dds.headers[1].length); + bytes memory _encodedMethod = encode(uint64(_dds.method), bytes1(0x08)); + bytes memory _encodedUrl; + if (bytes(_dds.url).length > 0) { + _encodedUrl = abi.encodePacked( + encode(uint64(bytes(_dds.url).length), bytes1(0x12)), + bytes(_dds.url) + ); + } + bytes memory _encodedScript; + if (_dds.script.length > 0) { + _encodedScript = abi.encodePacked( + encode(uint64(_dds.script.length), bytes1(0x1a)), + _dds.script + ); + } + bytes memory _encodedBody; + if (bytes(_dds.body).length > 0) { + _encodedBody = abi.encodePacked( + encode(uint64(bytes(_dds.body).length), bytes1(0x22)) + ); + } + bytes memory _encodedHeaders; + if (_dds.headers[0].length > 0) { + bytes memory _partials; + for (uint _ix = 0; _ix < _dds.headers[0].length; ) { + _partials = abi.encodePacked( + _partials, + encode(uint64(bytes(_dds.headers[0][_ix]).length), bytes1(0x0a)), + bytes(_dds.headers[0][_ix]), + encode(uint64(bytes(_dds.headers[1][_ix]).length), bytes1(0x12)), + bytes(_dds.headers[1][_ix]) + ); + } + _encodedHeaders = abi.encodePacked( + encode(uint64(_partials.length), bytes1(0x2a)), + _partials + ); + } + uint _innerSize = ( + _encodedMethod.length + + _encodedUrl.length + + _encodedScript.length + + _encodedBody.length + + _encodedHeaders.length + ); + return abi.encodePacked( + encode(uint64(_innerSize), bytes1(0x12)), + _encodedMethod, + _encodedUrl, + _encodedScript, + _encodedBody, + _encodedHeaders + ); } - /// @notice Convert a `uint64` into a 1, 2 or 3 characters long `string` representing its. - /// three less significant decimal values. - /// @param _u A `uint64` value. - /// @return The `string` representing its decimal value. - function _utoa(uint64 _u) - private pure - returns (string memory) + function encode(WitnetV2.RadonSLA memory _sla) + public pure + returns (bytes memory) { - if (_u < 10) { - bytes memory b1 = new bytes(1); - b1[0] = bytes1(uint8(_u) + 48); - return string(b1); - } else if (_u < 100) { - bytes memory b2 = new bytes(2); - b2[0] = bytes1(uint8(_u / 10) + 48); - b2[1] = bytes1(uint8(_u % 10) + 48); - return string(b2); - } else { - bytes memory b3 = new bytes(3); - b3[0] = bytes1(uint8(_u / 100) + 48); - b3[1] = bytes1(uint8(_u % 100 / 10) + 48); - b3[2] = bytes1(uint8(_u % 10) + 48); - return string(b3); - } + return abi.encodePacked( + encode(uint64(_sla.witnessReward), bytes1(0x10)), + encode(uint64(_sla.numWitnesses), bytes1(0x18)), + encode(uint64(_sla.commitRevealFee), bytes1(0x20)), + encode(uint64(_sla.minConsensusPercentage), bytes1(0x28)), + encode(uint64(_sla.collateral), bytes1(0x30)) + ); } /// @notice Convert a `uint64` into a 2 characters long `string` representing its two less significant hexadecimal values. From 345a997a2f340bd96ca056a5816c74073e6acebe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 25 Nov 2022 10:58:28 +0100 Subject: [PATCH 013/119] feat(libs): implement wildcard replacements in string values --- contracts/libs/WitnetBuffer.sol | 487 +++++++++++++++++++++++--------- contracts/libs/WitnetLib.sol | 24 +- 2 files changed, 369 insertions(+), 142 deletions(-) diff --git a/contracts/libs/WitnetBuffer.sol b/contracts/libs/WitnetBuffer.sol index 192f8d88f..f1db05d15 100644 --- a/contracts/libs/WitnetBuffer.sol +++ b/contracts/libs/WitnetBuffer.sol @@ -10,56 +10,110 @@ pragma solidity >=0.8.0 <0.9.0; /// @author The Witnet Foundation. library WitnetBuffer { + error EmptyBuffer(); + error IndexOutOfBounds(uint index, uint range); + /// Iterable bytes buffer. struct Buffer { bytes data; - uint32 cursor; + uint cursor; } // Ensures we access an existing index in an array - modifier notOutOfBounds(uint32 index, uint256 length) { - require(index < length, "WitnetBuffer: Tried to read from a consumed Buffer (must rewind it first)"); + modifier withinRange(uint _index, uint _range) { + if (_index >= _range) { + revert IndexOutOfBounds(_index, _range); + } _; } /// @notice Read and consume a certain amount of bytes from the buffer. /// @param _buffer An instance of `Buffer`. /// @param _length How many bytes to read and consume from the buffer. - /// @return A `bytes memory` containing the first `_length` bytes from the buffer, counting from the cursor position. - function read(Buffer memory _buffer, uint32 _length) internal pure returns (bytes memory) { - // Make sure not to read out of the bounds of the original bytes - require(_buffer.cursor + _length <= _buffer.data.length, "WitnetBuffer: Not enough bytes in buffer when reading"); - + /// @return _output A `bytes memory` containing the first `_length` bytes from the buffer, counting from the cursor position. + function read(Buffer memory _buffer, uint _length) + internal pure + withinRange(_buffer.cursor + _length, _buffer.data.length + 1) + returns (bytes memory _output) + { // Create a new `bytes memory destination` value - bytes memory destination = new bytes(_length); - + _output = new bytes(_length); // Early return in case that bytes length is 0 - if (_length != 0) { - bytes memory source = _buffer.data; - uint32 offset = _buffer.cursor; - + if (_length > 0) { + bytes memory _input = _buffer.data; + uint _offset = _buffer.cursor; // Get raw pointers for source and destination - uint sourcePointer; - uint destinationPointer; + uint _sourcePointer; + uint _destinationPointer; assembly { - sourcePointer := add(add(source, 32), offset) - destinationPointer := add(destination, 32) + _sourcePointer := add(add(_input, 32), _offset) + _destinationPointer := add(_output, 32) } // Copy `_length` bytes from source to destination - memcpy(destinationPointer, sourcePointer, uint(_length)); - + memcpy( + _destinationPointer, + _sourcePointer, + _length + ); // Move the cursor forward by `_length` bytes - seek(_buffer, _length, true); + seek( + _buffer, + _length, + true + ); } - return destination; } /// @notice Read and consume the next byte from the buffer. /// @param _buffer An instance of `Buffer`. /// @return The next byte in the buffer counting from the cursor position. - function next(Buffer memory _buffer) internal pure notOutOfBounds(_buffer.cursor, _buffer.data.length) returns (bytes1) { + function next(Buffer memory _buffer) + internal pure + withinRange(_buffer.cursor, _buffer.data.length) + returns (bytes1) + { // Return the byte at the position marked by the cursor and advance the cursor all at once - return _buffer.data[_buffer.cursor++]; + return _buffer.data[_buffer.cursor ++]; + } + + function mutate( + WitnetBuffer.Buffer memory _buffer, + uint _length, + bytes memory _genes + ) + internal pure + withinRange(_length, _buffer.data.length - _buffer.cursor) + { + // TODO + } + + // @notice Extract bytes array from buffer starting from current cursor. + /// @param _buffer An instance of `Buffer`. + /// @param _length How many bytes to peek from the Buffer. + // solium-disable-next-line security/no-assign-params + function peek( + WitnetBuffer.Buffer memory _buffer, + uint _length + ) + internal pure + withinRange(_length, _buffer.data.length - _buffer.cursor) + returns (bytes memory) + { + bytes memory _data = _buffer.data; + bytes memory _peek = new bytes(_length); + uint _offset = _buffer.cursor; + uint _destinationPointer; + uint _sourcePointer; + assembly { + _destinationPointer := add(_peek, 32) + _sourcePointer := add(add(_data, 32), _offset) + } + memcpy( + _destinationPointer, + _sourcePointer, + _length + ); + return _peek; } /// @notice Move the inner cursor of the buffer to a relative or absolute position. @@ -69,16 +123,21 @@ library WitnetBuffer { /// buffer (`true`). /// @return The final position of the cursor (will equal `_offset` if `_relative` is `false`). // solium-disable-next-line security/no-assign-params - function seek(Buffer memory _buffer, uint32 _offset, bool _relative) internal pure returns (uint32) { + function seek( + Buffer memory _buffer, + uint _offset, + bool _relative + ) + internal pure + withinRange(_offset, _buffer.data.length + 1) + returns (uint) + { // Deal with relative offsets if (_relative) { - require(_offset + _buffer.cursor > _offset, "WitnetBuffer: Integer overflow when seeking"); _offset += _buffer.cursor; } - // Make sure not to read out of the bounds of the original bytes - require(_offset <= _buffer.data.length, "WitnetBuffer: Not enough bytes in buffer when seeking"); _buffer.cursor = _offset; - return _buffer.cursor; + return _offset; } /// @notice Move the inner cursor a number of bytes forward. @@ -86,141 +145,206 @@ library WitnetBuffer { /// @param _buffer An instance of `Buffer`. /// @param _relativeOffset How many bytes to move the cursor forward. /// @return The final position of the cursor. - function seek(Buffer memory _buffer, uint32 _relativeOffset) internal pure returns (uint32) { - return seek(_buffer, _relativeOffset, true); + function seek( + Buffer memory _buffer, + uint _relativeOffset + ) + internal pure + returns (uint) + { + return seek( + _buffer, + _relativeOffset, + true + ); } /// @notice Move the inner cursor back to the first byte in the buffer. /// @param _buffer An instance of `Buffer`. - function rewind(Buffer memory _buffer) internal pure { + function rewind(Buffer memory _buffer) + internal pure + { _buffer.cursor = 0; } + + /// @notice Read and consume the next 2 bytes from the buffer as an IEEE 754-2008 floating point number enclosed in an + /// `int32`. + /// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values + /// by 5 decimal orders so as to get a fixed precision of 5 decimal positions, which should be OK for most `float16` + /// use cases. In other words, the integer output of this method is 10,000 times the actual value. The input bytes are + /// expected to follow the 16-bit base-2 format (a.k.a. `binary16`) in the IEEE 754-2008 standard. + /// @param _buffer An instance of `Buffer`. + /// @return _result The `int32` value of the next 4 bytes in the buffer counting from the cursor position. + function readFloat16(Buffer memory _buffer) + internal pure + returns (int32 _result) + { + uint32 _value = readUint16(_buffer); + // Get bit at position 0 + uint32 _sign = _value & 0x8000; + // Get bits 1 to 5, then normalize to the [-14, 15] range so as to counterweight the IEEE 754 exponent bias + int32 _exponent = (int32(_value & 0x7c00) >> 10) - 15; + // Get bits 6 to 15 + int32 _significand = int32(_value & 0x03ff); + // Add 1024 to the fraction if the exponent is 0 + if (_exponent == 15) { + _significand |= 0x400; + } + // Compute `2 ^ exponent · (1 + fraction / 1024)` + if (_exponent >= 0) { + _result = ( + int32((int256(1 << uint256(int256(_exponent))) + * 10000 + * int256(uint256(int256(_significand)) | 0x400)) >> 10) + ); + } else { + _result = (int32( + ((int256(uint256(int256(_significand)) | 0x400) * 10000) + / int256(1 << uint256(int256(- _exponent)))) + >> 10 + )); + } + // Make the result negative if the sign bit is not 0 + if (_sign != 0) { + _result *= -1; + } + } + + // Read a text string of a given length from a buffer. Returns a `bytes memory` value for the sake of genericness, + /// but it can be easily casted into a string with `string(result)`. + // solium-disable-next-line security/no-assign-params + function readText( + WitnetBuffer.Buffer memory _buffer, + uint64 _length + ) + internal pure + returns (bytes memory _text) + { + _text = new bytes(_length); + unchecked { + for (uint64 _index = 0; _index < _length; _index ++) { + uint8 _char = readUint8(_buffer); + if (_char & 0x80 != 0) { + if (_char < 0xe0) { + _char = (_char & 0x1f) << 6 + | (readUint8(_buffer) & 0x3f); + _length -= 1; + } else if (_char < 0xf0) { + _char = (_char & 0x0f) << 12 + | (readUint8(_buffer) & 0x3f) << 6 + | (readUint8(_buffer) & 0x3f); + _length -= 2; + } else { + _char = (_char & 0x0f) << 18 + | (readUint8(_buffer) & 0x3f) << 12 + | (readUint8(_buffer) & 0x3f) << 6 + | (readUint8(_buffer) & 0x3f); + _length -= 3; + } + } + _text[_index] = bytes1(_char); + } + // Adjust text to actual length: + assembly { + mstore(_text, _length) + } + } + } /// @notice Read and consume the next byte from the buffer as an `uint8`. /// @param _buffer An instance of `Buffer`. - /// @return The `uint8` value of the next byte in the buffer counting from the cursor position. - function readUint8(Buffer memory _buffer) internal pure notOutOfBounds(_buffer.cursor, _buffer.data.length) returns (uint8) { - bytes memory bytesValue = _buffer.data; - uint32 offset = _buffer.cursor; - uint8 value; + /// @return _value The `uint8` value of the next byte in the buffer counting from the cursor position. + function readUint8(Buffer memory _buffer) + internal pure + withinRange(_buffer.cursor, _buffer.data.length) + returns (uint8 _value) + { + bytes memory _data = _buffer.data; + uint _offset = _buffer.cursor; assembly { - value := mload(add(add(bytesValue, 1), offset)) + _value := mload(add(add(_data, 1), _offset)) } - _buffer.cursor++; - - return value; + _buffer.cursor ++; } /// @notice Read and consume the next 2 bytes from the buffer as an `uint16`. /// @param _buffer An instance of `Buffer`. - /// @return The `uint16` value of the next 2 bytes in the buffer counting from the cursor position. - function readUint16(Buffer memory _buffer) internal pure notOutOfBounds(_buffer.cursor + 1, _buffer.data.length) returns (uint16) { - bytes memory bytesValue = _buffer.data; - uint32 offset = _buffer.cursor; - uint16 value; + /// @return _value The `uint16` value of the next 2 bytes in the buffer counting from the cursor position. + function readUint16(Buffer memory _buffer) + internal pure + withinRange(_buffer.cursor + 1, _buffer.data.length) + returns (uint16 _value) + { + bytes memory _data = _buffer.data; + uint _offset = _buffer.cursor; assembly { - value := mload(add(add(bytesValue, 2), offset)) + _value := mload(add(add(_data, 2), _offset)) } _buffer.cursor += 2; - - return value; } /// @notice Read and consume the next 4 bytes from the buffer as an `uint32`. /// @param _buffer An instance of `Buffer`. - /// @return The `uint32` value of the next 4 bytes in the buffer counting from the cursor position. - function readUint32(Buffer memory _buffer) internal pure notOutOfBounds(_buffer.cursor + 3, _buffer.data.length) returns (uint32) { - bytes memory bytesValue = _buffer.data; - uint32 offset = _buffer.cursor; - uint32 value; + /// @return _value The `uint32` value of the next 4 bytes in the buffer counting from the cursor position. + function readUint32(Buffer memory _buffer) + internal pure + withinRange(_buffer.cursor + 3, _buffer.data.length) + returns (uint32 _value) + { + bytes memory _data = _buffer.data; + uint _offset = _buffer.cursor; assembly { - value := mload(add(add(bytesValue, 4), offset)) + _value := mload(add(add(_data, 4), _offset)) } _buffer.cursor += 4; - - return value; } /// @notice Read and consume the next 8 bytes from the buffer as an `uint64`. /// @param _buffer An instance of `Buffer`. - /// @return The `uint64` value of the next 8 bytes in the buffer counting from the cursor position. - function readUint64(Buffer memory _buffer) internal pure notOutOfBounds(_buffer.cursor + 7, _buffer.data.length) returns (uint64) { - bytes memory bytesValue = _buffer.data; - uint32 offset = _buffer.cursor; - uint64 value; + /// @return _value The `uint64` value of the next 8 bytes in the buffer counting from the cursor position. + function readUint64(Buffer memory _buffer) + internal pure + withinRange(_buffer.cursor + 7, _buffer.data.length) + returns (uint64 _value) + { + bytes memory _data = _buffer.data; + uint _offset = _buffer.cursor; assembly { - value := mload(add(add(bytesValue, 8), offset)) + _value := mload(add(add(_data, 8), _offset)) } _buffer.cursor += 8; - - return value; } /// @notice Read and consume the next 16 bytes from the buffer as an `uint128`. /// @param _buffer An instance of `Buffer`. - /// @return The `uint128` value of the next 16 bytes in the buffer counting from the cursor position. - function readUint128(Buffer memory _buffer) internal pure notOutOfBounds(_buffer.cursor + 15, _buffer.data.length) returns (uint128) { - bytes memory bytesValue = _buffer.data; - uint32 offset = _buffer.cursor; - uint128 value; + /// @return _value The `uint128` value of the next 16 bytes in the buffer counting from the cursor position. + function readUint128(Buffer memory _buffer) + internal pure + withinRange(_buffer.cursor + 15, _buffer.data.length) + returns (uint128 _value) + { + bytes memory _data = _buffer.data; + uint _offset = _buffer.cursor; assembly { - value := mload(add(add(bytesValue, 16), offset)) + _value := mload(add(add(_data, 16), _offset)) } _buffer.cursor += 16; - - return value; } /// @notice Read and consume the next 32 bytes from the buffer as an `uint256`. - /// @return The `uint256` value of the next 32 bytes in the buffer counting from the cursor position. /// @param _buffer An instance of `Buffer`. - function readUint256(Buffer memory _buffer) internal pure notOutOfBounds(_buffer.cursor + 31, _buffer.data.length) returns (uint256) { - bytes memory bytesValue = _buffer.data; - uint32 offset = _buffer.cursor; - uint256 value; + /// @return _value The `uint256` value of the next 32 bytes in the buffer counting from the cursor position. + function readUint256(Buffer memory _buffer) + internal pure + withinRange(_buffer.cursor + 31, _buffer.data.length) + returns (uint256 _value) + { + bytes memory _data = _buffer.data; + uint _offset = _buffer.cursor; assembly { - value := mload(add(add(bytesValue, 32), offset)) + _value := mload(add(add(_data, 32), _offset)) } _buffer.cursor += 32; - - return value; - } - - /// @notice Read and consume the next 2 bytes from the buffer as an IEEE 754-2008 floating point number enclosed in an - /// `int32`. - /// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values - /// by 5 decimal orders so as to get a fixed precision of 5 decimal positions, which should be OK for most `float16` - /// use cases. In other words, the integer output of this method is 10,000 times the actual value. The input bytes are - /// expected to follow the 16-bit base-2 format (a.k.a. `binary16`) in the IEEE 754-2008 standard. - /// @param _buffer An instance of `Buffer`. - /// @return The `uint32` value of the next 4 bytes in the buffer counting from the cursor position. - function readFloat16(Buffer memory _buffer) internal pure returns (int32) { - uint32 bytesValue = readUint16(_buffer); - // Get bit at position 0 - uint32 sign = bytesValue & 0x8000; - // Get bits 1 to 5, then normalize to the [-14, 15] range so as to counterweight the IEEE 754 exponent bias - int32 exponent = (int32(bytesValue & 0x7c00) >> 10) - 15; - // Get bits 6 to 15 - int32 significand = int32(bytesValue & 0x03ff); - - // Add 1024 to the fraction if the exponent is 0 - if (exponent == 15) { - significand |= 0x400; - } - - // Compute `2 ^ exponent · (1 + fraction / 1024)` - int32 result = 0; - if (exponent >= 0) { - result = int32((int256(1 << uint256(int256(exponent))) * 10000 * int256(uint256(int256(significand)) | 0x400)) >> 10); - } else { - result = int32(((int256(uint256(int256(significand)) | 0x400) * 10000) / int256(1 << uint256(int256(- exponent)))) >> 10); - } - - // Make the result negative if the sign bit is not 0 - if (sign != 0) { - result *= - 1; - } - return result; } /// @notice Copy bytes from one memory address into another. @@ -230,9 +354,13 @@ library WitnetBuffer { /// @param _src Address to the source memory. /// @param _len How many bytes to copy. // solium-disable-next-line security/no-assign-params - function memcpy(uint _dest, uint _src, uint _len) private pure { - require(_len > 0, "WitnetBuffer: Cannot copy 0 bytes"); - + function memcpy( + uint _dest, + uint _src, + uint _len + ) + internal pure + { // Copy word-length chunks while possible for (; _len >= 32; _len -= 32) { assembly { @@ -243,13 +371,120 @@ library WitnetBuffer { } if (_len > 0) { // Copy remaining bytes - uint mask = 256 ** (32 - _len) - 1; + uint _mask = 256 ** (32 - _len) - 1; + assembly { + let _srcpart := and(mload(_src), not(_mask)) + let _destpart := and(mload(_dest), _mask) + mstore(_dest, or(_destpart, _srcpart)) + } + } + } + + function concat(bytes[] memory _buffs) + internal pure + returns (bytes memory _output) + { + unchecked { + uint _ix; + uint _destinationPointer; assembly { - let srcpart := and(mload(_src), not(mask)) - let destpart := and(mload(_dest), mask) - mstore(_dest, or(destpart, srcpart)) + _destinationPointer := add(_output, 32) + } + while (_ix < _buffs.length) { + bytes memory _source = _buffs[_ix]; + uint _sourceLength = _source.length; + uint _sourcePointer; + assembly { + // sets source memory pointer + _sourcePointer := add(_source, 32) + } + memcpy( + _destinationPointer, + _sourcePointer, + _sourceLength + ); + assembly { + // increase _output size + mstore(_output, add(mload(_output), _sourceLength)) + // sets destination memory pointer + _destinationPointer := add(_destinationPointer, _sourceLength) + } + _ix ++; + } + } + } + + function replaceWildcards( + WitnetBuffer.Buffer memory _buffer, + uint _length, + string[] memory _args + ) + internal pure + { + bytes memory _peek = replaceWildcards( + peek(_buffer, _length), + _args + ); + if (_peek.length != _length) { + mutate(_buffer, _length, _peek); + } + } + + /// @notice Replace bytecode indexed wildcards by correspondent string. + /// @dev Wildcard format: "\#\", with # in ["0".."9"]. + /// @param _input Bytes array containing strings. + /// @param _args String values for replacing existing indexed wildcards in _input. + function replaceWildcards(bytes memory _input, string[] memory _args) + internal pure + returns (bytes memory _output) + { + _output = _input; + if (_input.length >= 3) { + uint _outputLength = _input.length; + unchecked { + // scan for wildcards as to calculate output length: + for (uint _ix = 0; _ix < _input.length - 2; ) { + if (_input[_ix] == bytes1("\\")) { + if (_input[_ix + 2] == bytes1("\\")) { + if (_input[_ix + 1] >= bytes1("0") && _input[_ix + 1] <= bytes1("9")) { + uint _argIndex = uint(uint8(_input[_ix + 1]) - uint8(bytes1("0"))); + assert(_args.length >= uint(_argIndex) + 1); + _outputLength += bytes(_args[_argIndex]).length - 3; + _ix += 3; + } else { + _ix += 2; + } + continue; + } else { + _ix += 3; + continue; + } + } else { + _ix ++; + } + } + // if wildcards found: + if (_outputLength > _input.length) { + _output = new bytes(_outputLength); + // Replace indexed wildcards: + for (uint _ix = 0; _ix < _input.length - 2; ) { + if (_input[_ix] == bytes1("\\")) { + if (_input[_ix + 2] == bytes1("\\")) { + if (_input[_ix + 1] >= bytes1("0") && _input[_ix + 1] <= bytes1("9")) { + uint _argIndex = uint(uint8(_input[_ix + 1]) - uint8(bytes1("0"))); + for (uint _ax = 0; _ax < bytes(_args[_argIndex]).length; _ax ++) { + _output[_ix + _ax] = bytes(_args[_argIndex])[_ax]; + } + _ix += 3; + continue; + } + } + } + _output[_ix] = _input[_ix ++]; + } + } } } } -} +} \ No newline at end of file diff --git a/contracts/libs/WitnetLib.sol b/contracts/libs/WitnetLib.sol index 3cfccad76..5e9b30558 100644 --- a/contracts/libs/WitnetLib.sol +++ b/contracts/libs/WitnetLib.sol @@ -649,23 +649,15 @@ library WitnetLib { ); } - /// @notice Convert a `uint64` into a 2 characters long `string` representing its two less significant hexadecimal values. - /// @param _u A `uint64` value. - /// @return The `string` representing its hexadecimal value. - function _utohex(uint64 _u) - private pure - returns (string memory) + function replaceCborBytesWildcards( + bytes memory _cborBytes, + string[] memory _args + ) + public pure + returns (WitnetCBOR.CBOR memory _cbor) { - bytes memory b2 = new bytes(2); - uint8 d0 = uint8(_u / 16) + 48; - uint8 d1 = uint8(_u % 16) + 48; - if (d0 > 57) - d0 += 7; - if (d1 > 57) - d1 += 7; - b2[0] = bytes1(d0); - b2[1] = bytes1(d1); - return string(b2); + _cbor = WitnetCBOR.valueFromBytes(_cborBytes); + _cbor.readArray().replaceWildcards(_args); } /// @notice Decode raw CBOR bytes into a Witnet.Result instance. From e46cf77716ce9f69c121e94cb80ddb8d0cef80e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 25 Nov 2022 11:06:52 +0100 Subject: [PATCH 014/119] chore(libs): adapt existing tests to latest changes --- test/TestWitnetCBOR.sol | 219 +++++++++++++++++++++++++--------------- test/TestWitnetLib.sol | 11 +- 2 files changed, 137 insertions(+), 93 deletions(-) diff --git a/test/TestWitnetCBOR.sol b/test/TestWitnetCBOR.sol index 56178989a..585cf408d 100644 --- a/test/TestWitnetCBOR.sol +++ b/test/TestWitnetCBOR.sol @@ -5,7 +5,7 @@ pragma experimental ABIEncoderV2; import "truffle/Assert.sol"; import "../contracts/libs/WitnetCBOR.sol"; - +import "../contracts/libs/WitnetLib.sol"; contract TestWitnetCBOR { @@ -15,8 +15,8 @@ contract TestWitnetCBOR { event Log(string _topic, bytes _value); function testBoolDecode() external { - bool decodedFalse = WitnetCBOR.valueFromBytes(hex"f4").decodeBool(); - bool decodedTrue = WitnetCBOR.valueFromBytes(hex"f5").decodeBool(); + bool decodedFalse = WitnetCBOR.valueFromBytes(hex"f4").readBool(); + bool decodedTrue = WitnetCBOR.valueFromBytes(hex"f5").readBool(); Assert.equal( decodedFalse, false, @@ -30,14 +30,14 @@ contract TestWitnetCBOR { } function helperDecodeBoolRevert() public pure { - WitnetCBOR.valueFromBytes(hex"f6").decodeBool(); + WitnetCBOR.valueFromBytes(hex"f6").readBool(); } function testBoolDecodeRevert() external { bool r; // solhint-disable-next-line avoid-low-level-calls (r,) = address(this).call(abi.encodePacked(this.helperDecodeBoolRevert.selector)); - Assert.isFalse(r, "Invalid CBOR-encoded bool value should revert in decodeBool function"); + Assert.isFalse(r, "Invalid CBOR-encoded bool value should revert in readBool function"); } function testUint64DecodeDiscriminant() external { @@ -46,7 +46,7 @@ contract TestWitnetCBOR { } function testUint64DecodeValue() external { - uint64 decoded = WitnetCBOR.valueFromBytes(hex"1b0020000000000000").decodeUint64(); + uint64 decoded = uint64(WitnetCBOR.valueFromBytes(hex"1b0020000000000000").readUint()); Assert.equal( uint(decoded), 9007199254740992, @@ -60,7 +60,7 @@ contract TestWitnetCBOR { } function testInt128DecodeValue() external { - int128 decoded = WitnetCBOR.valueFromBytes(hex"3bfffffffffffffffe").decodeInt128(); + int128 decoded = int128(WitnetCBOR.valueFromBytes(hex"3bfffffffffffffffe").readInt()); Assert.equal( int(decoded), -18446744073709551615, @@ -69,7 +69,7 @@ contract TestWitnetCBOR { } function testInt128DecodeZeroValue() external { - int128 decoded = WitnetCBOR.valueFromBytes(hex"00").decodeInt128(); + int128 decoded = int128(WitnetCBOR.valueFromBytes(hex"00").readInt()); Assert.equal(int(decoded), 0, "CBOR-encoded Int128 value should be decoded into a WitnetCBOR.CBOR containing the correct Uint64 value"); } @@ -80,7 +80,7 @@ contract TestWitnetCBOR { function testBytes0DecodeValue() external { bytes memory encoded = hex"40"; - bytes memory decoded = WitnetCBOR.valueFromBytes(encoded).decodeBytes(); + bytes memory decoded = WitnetCBOR.valueFromBytes(encoded).readBytes(); Assert.equal(decoded.length, 0, "Empty CBOR-encoded Bytes value should be decoded into an empty WitnetCBOR.CBOR containing an empty bytes value"); } @@ -91,7 +91,7 @@ contract TestWitnetCBOR { function testBytes4DecodeValue() external { bytes memory encoded = hex"4401020304"; - bytes memory decoded = WitnetCBOR.valueFromBytes(encoded).decodeBytes(); + bytes memory decoded = WitnetCBOR.valueFromBytes(encoded).readBytes(); bytes memory expected = abi.encodePacked( uint8(1), uint8(2), @@ -123,205 +123,258 @@ contract TestWitnetCBOR { function testBytes32DecodeValueFrom31bytes() external { bytes memory encoded = hex"581f01020304050607080910111213141516171819202122232425262728293031"; - bytes32 decoded = WitnetCBOR.decodeBytes32(WitnetCBOR.valueFromBytes(encoded)); + bytes32 decoded = WitnetLib.toBytes32(WitnetCBOR.readBytes(WitnetCBOR.valueFromBytes(encoded))); bytes32 expected = 0x0102030405060708091011121314151617181920212223242526272829303100; - Assert.equal(decoded, expected, "CBOR-encoded 31-byte array should be decoded into a bytes32 with right padded zeros"); + Assert.equal( + decoded, + expected, + "CBOR-encoded 31-byte array should be decoded into a bytes32 with right padded zeros" + ); } function testBytes32DecodeValueFrom32bytes() external { bytes memory encoded = hex"58200102030405060708091011121314151617181920212223242526272829303132"; - bytes32 decoded = WitnetCBOR.decodeBytes32(WitnetCBOR.valueFromBytes(encoded)); + bytes32 decoded = WitnetLib.toBytes32(WitnetCBOR.readBytes(WitnetCBOR.valueFromBytes(encoded))); bytes32 expected = 0x0102030405060708091011121314151617181920212223242526272829303132; - Assert.equal(decoded, expected, "CBOR-encoded 32-byte array should be decoded into a bytes32"); + Assert.equal( + decoded, + expected, + "CBOR-encoded 32-byte array should be decoded into a bytes32" + ); } function testBytes32DecodeValueFrom33bytes() external { bytes memory encoded = hex"5821010203040506070809101112131415161718192021222324252627282930313233"; - bytes32 decoded = WitnetCBOR.decodeBytes32(WitnetCBOR.valueFromBytes(encoded)); + bytes32 decoded = WitnetLib.toBytes32(WitnetCBOR.readBytes(WitnetCBOR.valueFromBytes(encoded))); bytes32 expected = 0x0102030405060708091011121314151617181920212223242526272829303132; - Assert.equal(decoded, expected, "CBOR-encoded 33-byte array should be decoded left-aligned into a bytes32"); + Assert.equal( + decoded, + expected, + "CBOR-encoded 33-byte array should be decoded left-aligned into a bytes32" + ); } function testStringDecodeDiscriminant() external { WitnetCBOR.CBOR memory decoded = WitnetCBOR.valueFromBytes(hex"6449455446"); - Assert.equal(uint(decoded.majorType), 3, "CBOR-encoded String value should be decoded into a WitnetCBOR.CBOR with major type 3"); + Assert.equal( + uint(decoded.majorType), + 3, + "CBOR-encoded String value should be decoded into a WitnetCBOR.CBOR with major type 3" + ); } function testStringDecodeValue() external { bytes memory encoded = hex"6449455446"; - string memory decoded = WitnetCBOR.valueFromBytes(encoded).decodeString(); + string memory decoded = WitnetCBOR.valueFromBytes(encoded).readString(); string memory expected = "IETF"; - - Assert.equal(decoded, expected, "CBOR-encoded String value should be decoded into a WitnetCBOR.CBOR containing the correct String value"); + Assert.equal( + decoded, + expected, + "CBOR-encoded String value should be decoded into a WitnetCBOR.CBOR containing the correct String value" + ); } function testFloatDecodeDiscriminant() external { WitnetCBOR.CBOR memory decoded = WitnetCBOR.valueFromBytes(hex"f90001"); - Assert.equal(uint(decoded.majorType), 7, "CBOR-encoded Float value should be decoded into a CBOR with major type 7"); + Assert.equal( + uint(decoded.majorType), + 7, + "CBOR-encoded Float value should be decoded into a CBOR with major type 7" + ); } function testFloatDecodeSmallestSubnormal() external { bytes memory encoded = hex"f90001"; - int32 decoded = WitnetCBOR.valueFromBytes(encoded).decodeFixed16(); + int32 decoded = WitnetCBOR.valueFromBytes(encoded).readFloat16(); int32 expected = 0; - - Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value"); + Assert.equal( + decoded, + expected, + "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value" + ); } function testFloatDecodeLargestSubnormal() external { bytes memory encoded = hex"f903ff"; - int32 decoded = WitnetCBOR.valueFromBytes(encoded).decodeFixed16(); + int32 decoded = WitnetCBOR.valueFromBytes(encoded).readFloat16(); int32 expected = 0; - - Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value"); + Assert.equal( + decoded, + expected, + "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value" + ); } function testFloatDecodeSmallestPositiveNormal() external { bytes memory encoded = hex"f90400"; - int32 decoded = WitnetCBOR.valueFromBytes(encoded).decodeFixed16(); + int32 decoded = WitnetCBOR.valueFromBytes(encoded).readFloat16(); int32 expected = 0; - - Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value"); + Assert.equal( + decoded, + expected, + "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value" + ); } function testFloatDecodeLargestNormal() external { bytes memory encoded = hex"f97bff"; - int32 decoded = WitnetCBOR.valueFromBytes(encoded).decodeFixed16(); + int32 decoded = WitnetCBOR.valueFromBytes(encoded).readFloat16(); int32 expected = 655040000; - - Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value"); + Assert.equal( + decoded, + expected, + "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value" + ); } function testFloatDecodeLargestLessThanOne() external { bytes memory encoded = hex"f93bff"; - int32 decoded = WitnetCBOR.valueFromBytes(encoded).decodeFixed16(); + int32 decoded = WitnetCBOR.valueFromBytes(encoded).readFloat16(); int32 expected = 9995; - - Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value"); + Assert.equal( + decoded, + expected, + "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value" + ); } function testFloatDecodeOne() external { bytes memory encoded = hex"f93c00"; - int32 decoded = WitnetCBOR.valueFromBytes(encoded).decodeFixed16(); + int32 decoded = WitnetCBOR.valueFromBytes(encoded).readFloat16(); int32 expected = 10000; - - Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value"); + Assert.equal( + decoded, + expected, + "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value" + ); } function testFloatDecodeSmallestGreaterThanOne() external { bytes memory encoded = hex"f93c01"; - int32 decoded = WitnetCBOR.valueFromBytes(encoded).decodeFixed16(); + int32 decoded = WitnetCBOR.valueFromBytes(encoded).readFloat16(); int32 expected = 10009; - - Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value"); + Assert.equal( + decoded, + expected, + "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value" + ); } function testFloatDecodeOneThird() external { bytes memory encoded = hex"f93555"; - int32 decoded = WitnetCBOR.valueFromBytes(encoded).decodeFixed16(); + int32 decoded = WitnetCBOR.valueFromBytes(encoded).readFloat16(); int32 expected = 3332; - - Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value"); + Assert.equal( + decoded, + expected, + "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value" + ); } function testFloatDecodeMinusTwo() external { bytes memory encoded = hex"f9c000"; - int32 decoded = WitnetCBOR.valueFromBytes(encoded).decodeFixed16(); + int32 decoded = WitnetCBOR.valueFromBytes(encoded).readFloat16(); int32 expected = -20000; - - Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value"); + Assert.equal( + decoded, + expected, + "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value" + ); } function testFloatDecodeZero() external { bytes memory encoded = hex"f90000"; - int32 decoded = WitnetCBOR.valueFromBytes(encoded).decodeFixed16(); + int32 decoded = WitnetCBOR.valueFromBytes(encoded).readFloat16(); int32 expected = 0; - - Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value"); + Assert.equal( + decoded, + expected, + "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value" + ); } function testFloatDecodeMinusZero() external { bytes memory encoded = hex"f98000"; - int32 decoded = WitnetCBOR.valueFromBytes(encoded).decodeFixed16(); + int32 decoded = WitnetCBOR.valueFromBytes(encoded).readFloat16(); int32 expected = 0; - - Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value"); + Assert.equal( + decoded, + expected, + "CBOR-encoded Float value should be decoded into a WitnetCBOR.CBOR containing the correct Float value" + ); } - function testUint64ArrayDecode() external { + function testUintArrayDecode() external { bytes memory encoded = hex"840102031a002fefd8"; - uint64[] memory decoded = WitnetCBOR.valueFromBytes(encoded).decodeUint64Array(); - uint64[4] memory expected = [ - uint64(1), - uint64(2), - uint64(3), - uint64(3141592) + uint[] memory decoded = WitnetCBOR.valueFromBytes(encoded).readUintArray(); + uint[4] memory expected = [ + uint(1), + uint(2), + uint(3), + uint(3141592) ]; - Assert.equal( uint(decoded[0]), uint(expected[0]), - "CBOR-encoded Array of Uint64 values should be decoded into a WitnetCBOR.CBOR containing the correct Uint64 values (error at item 0)" + "CBOR-encoded Array of uint values should be decoded into a WitnetCBOR.CBOR containing the correct uint values (error at item 0)" ); Assert.equal( uint(decoded[1]), uint(expected[1]), - "CBOR-encoded Array of Uint64 values should be decoded into a WitnetCBOR.CBOR containing the correct Uint64 values (error at item 1)" + "CBOR-encoded Array of uint values should be decoded into a WitnetCBOR.CBOR containing the correct uint values (error at item 1)" ); Assert.equal( uint(decoded[2]), uint(expected[2]), - "CBOR-encoded Array of Uint64 values should be decoded into a WitnetCBOR.CBOR containing the correct Uint64 values (error at item 2)" + "CBOR-encoded Array of uint values should be decoded into a WitnetCBOR.CBOR containing the correct uint values (error at item 2)" ); Assert.equal( uint(decoded[3]), uint(expected[3]), - "CBOR-encoded Array of Uint64 values should be decoded into a WitnetCBOR.CBOR containing the correct Uint64 values (error at item 3)" + "CBOR-encoded Array of uint values should be decoded into a WitnetCBOR.CBOR containing the correct uint values (error at item 3)" ); } - function testInt128ArrayDecode() external { + function testIntArrayDecode() external { bytes memory encoded = hex"840121033a002fefd7"; - int128[] memory decoded = WitnetCBOR.valueFromBytes(encoded).decodeInt128Array(); - int128[4] memory expected = [ - int128(1), - int128(-2), - int128(3), - int128(-3141592) + int[] memory decoded = WitnetCBOR.valueFromBytes(encoded).readIntArray(); + int[4] memory expected = [ + int(1), + int(-2), + int(3), + int(-3141592) ]; - Assert.equal( decoded[0], expected[0], - "CBOR-encoded Array of Int128 values should be decoded into a WitnetCBOR.CBOR containing the correct Int128 values (error at item 0)" + "CBOR-encoded Array of int values should be decoded into a WitnetCBOR.CBOR containing the correct int values (error at item 0)" ); Assert.equal( decoded[1], expected[1], - "CBOR-encoded Array of Int128 values should be decoded into a WitnetCBOR.CBOR containing the correct Int128 values (error at item 1)" + "CBOR-encoded Array of int values should be decoded into a WitnetCBOR.CBOR containing the correct int values (error at item 1)" ); Assert.equal( decoded[2], expected[2], - "CBOR-encoded Array of Int128 values should be decoded into a WitnetCBOR.CBOR containing the correct Int128 values (error at item 2)" + "CBOR-encoded Array of int values should be decoded into a WitnetCBOR.CBOR containing the correct int values (error at item 2)" ); Assert.equal( decoded[3], expected[3], - "CBOR-encoded Array of Int128 values should be decoded into a WitnetCBOR.CBOR containing the correct Int128 values (error at item 3)" + "CBOR-encoded Array of int values should be decoded into a WitnetCBOR.CBOR containing the correct int values (error at item 3)" ); } function testFixed16ArrayDecode() external { bytes memory encoded = hex"84f93c80f9c080f94290f9C249"; - int32[] memory decoded = WitnetCBOR.valueFromBytes(encoded).decodeFixed16Array(); + int32[] memory decoded = WitnetCBOR.valueFromBytes(encoded).readFloat16Array(); int32[4] memory expected = [ int32(11250), int32(-22500), int32(32812), int32(-31425) ]; - Assert.equal( decoded[0], expected[0], @@ -346,14 +399,13 @@ contract TestWitnetCBOR { function testStringArrayDecode() external { bytes memory encoded = hex"846548656c6c6f6d646563656e7472616c697a656465776f726c646121"; - string[] memory decoded = WitnetCBOR.valueFromBytes(encoded).decodeStringArray(); + string[] memory decoded = WitnetCBOR.valueFromBytes(encoded).readStringArray(); string[4] memory expected = [ "Hello", "decentralized", "world", "!" ]; - Assert.equal( decoded[0], expected[0], @@ -375,4 +427,5 @@ contract TestWitnetCBOR { "CBOR-encoded Array of String values should be decoded into a WitnetCBOR.CBOR containing the correct String values (error at item 3)" ); } -} + +} \ No newline at end of file diff --git a/test/TestWitnetLib.sol b/test/TestWitnetLib.sol index a0fb58134..a3b609de8 100644 --- a/test/TestWitnetLib.sol +++ b/test/TestWitnetLib.sol @@ -10,15 +10,6 @@ contract TestWitnetLib { using WitnetLib for Witnet.Result; - // Test the `WitnetLib.stageNames` pure method, which gives strings with the names for the different Witnet request - // stages - function testStageNames() external { - Assert.equal(WitnetLib.stageName(0), "retrieval", "Stage name for stage #1 should be \"retrieval\""); - Assert.equal(WitnetLib.stageName(1), "aggregation", "Stage name for stage #1 should be \"aggregation\""); - Assert.equal(WitnetLib.stageName(2), "tally", "Stage name for stage #1 should be \"tally\""); - - } - // Test decoding of `RadonError` error codes function testErrorCodes1() external { Witnet.ErrorCodes errorCodeEmpty = WitnetLib.resultFromCborBytes(hex"D82780").asErrorCode(); @@ -188,7 +179,7 @@ contract TestWitnetLib { (, string memory errorMessage0xFF) = WitnetLib.resultFromCborBytes(hex"D8278118FF").asErrorMessage(); Assert.equal( errorMessageEmpty, - "Unknown error (no error code)", + "Unknown error: no error code.", "Empty error message `[]` should be properly formatted" ); Assert.equal( From 6ec34e7029f08cc67dec6d7ce11a79e87affe288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 25 Nov 2022 11:22:50 +0100 Subject: [PATCH 015/119] feat: first implementation approach to WitnetBytecodes --- contracts/data/WitnetBytecodesData.sol | 98 ++ contracts/impls/bytecodes/WitnetBytecodes.sol | 657 +++++++++++++ contracts/interfaces/V2/IWitnetBytecodes.sol | 87 +- migrations/jsons/witnet-data-feeds.json | 27 + migrations/jsons/witnet-data-reducers.json | 22 + migrations/jsons/witnet-data-sources.json | 910 ++++++++++++++++++ migrations/jsons/witnet-radon-retrievals.json | 22 + migrations/jsons/witnet-radon-slas.json | 10 + 8 files changed, 1810 insertions(+), 23 deletions(-) create mode 100644 contracts/data/WitnetBytecodesData.sol create mode 100644 contracts/impls/bytecodes/WitnetBytecodes.sol create mode 100644 migrations/jsons/witnet-data-feeds.json create mode 100644 migrations/jsons/witnet-data-reducers.json create mode 100644 migrations/jsons/witnet-data-sources.json create mode 100644 migrations/jsons/witnet-radon-retrievals.json create mode 100644 migrations/jsons/witnet-radon-slas.json diff --git a/contracts/data/WitnetBytecodesData.sol b/contracts/data/WitnetBytecodesData.sol new file mode 100644 index 000000000..d6cdf834b --- /dev/null +++ b/contracts/data/WitnetBytecodesData.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.0 <0.9.0; + +import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +import "../interfaces/V2/IWitnetBytecodes.sol"; + +/// @title Witnet Request Board base data model. +/// @author The Witnet Foundation. +abstract contract WitnetBytecodesData + is + ERC165, + IWitnetBytecodes +{ + + bytes32 internal constant _WITNET_BYTECODES_DATA_SLOTHASH = + /* keccak256("io.witnet.bytecodes.data") */ + 0x673359bdfd0124f9962355e7aed2d07d989b0d4bc4cbe2c94c295e0f81427dec; + + bytes internal constant _WITNET_BYTECODES_RADON_OPCODES_RESULT_TYPE = + hex"00ffffffffffffffffffffffffffffff0401ff010203050406070101ff01ffff07ff02ffffffffffffffffffffffffff0703ffffffffffffffffffffffffffff05070404020205050505ff04ff04ffff0405070202ff04040404ffffffffffff010203050406070101ffffffffffffff02ff050404000106060707ffffffffff"; + + struct Bytecodes { + address base; + address owner; + address pendingOwner; + + Database db; + uint256 totalDataProviders; + // ... + } + + struct Database { + mapping (uint256 => WitnetV2.DataProvider) providers; + mapping (uint256 => mapping (uint256 => bytes32[])) providersSources; + mapping (bytes32 => uint256) providersIndex; + + mapping (bytes32 => bytes) reducersBytecode; + mapping (bytes32 => bytes) retrievalsBytecode; + mapping (bytes32 => bytes) slasBytecode; + // mapping (bytes32 => bytes) sourceBytecodes; + + mapping (bytes32 => IWitnetBytecodes.RadonRetrieval) retrievals; + mapping (bytes32 => WitnetV2.RadonReducer) reducers; + mapping (bytes32 => WitnetV2.RadonSLA) slas; + mapping (bytes32 => WitnetV2.DataSource) sources; + } + + function _lookupOpcodeResultType(uint8 _opcode) + internal pure + returns (WitnetV2.RadonDataTypes) + { + if (_opcode >= _WITNET_BYTECODES_RADON_OPCODES_RESULT_TYPE.length) { + revert IWitnetBytecodes.UnsupportedRadonScriptOpcode(_opcode); + } else { + uint8 _resultType = uint8( + _WITNET_BYTECODES_RADON_OPCODES_RESULT_TYPE[_opcode] + ); + if (_resultType == 0xff) { + revert IWitnetBytecodes.UnsupportedRadonScriptOpcode(_opcode); + } else if (_resultType > uint8(type(WitnetV2.RadonDataTypes).max)) { + revert IWitnetBytecodes.UnsupportedRadonDataType(_resultType, 0); + } else { + return WitnetV2.RadonDataTypes(_resultType); + } + } + } + + + // ================================================================================================================ + // --- Internal state-modifying functions ------------------------------------------------------------------------- + + /// @dev Returns storage pointer to contents of 'Bytecodes' struct. + function __bytecodes() + internal pure + returns (Bytecodes storage _ptr) + { + assembly { + _ptr.slot := _WITNET_BYTECODES_DATA_SLOTHASH + } + } + + /// @dev Returns storage pointer to contents of 'Database' struct. + function __database() + internal view + returns (Database storage _ptr) + { + return __bytecodes().db; + } + + function __retrieval(bytes32 _drRetrievalHash) + internal view + returns (RadonRetrieval storage _ptr) + { + return __database().retrievals[_drRetrievalHash]; + } + +} \ No newline at end of file diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol new file mode 100644 index 000000000..4695b4f13 --- /dev/null +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -0,0 +1,657 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.4 <0.9.0; + +import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; + +import "../WitnetUpgradableBase.sol"; +import "../../data/WitnetBytecodesData.sol"; + +import "../../libs/WitnetLib.sol"; + +/// @title Witnet Request Board "trustless" base implementation contract. +/// @notice Contract to bridge requests to Witnet Decentralized Oracle Network. +/// @dev This contract enables posting requests that Witnet bridges will insert into the Witnet network. +/// The result of the requests will be posted back to this contract by the bridge nodes too. +/// @author The Witnet Foundation +contract WitnetBytecodes + is + WitnetUpgradableBase, + WitnetBytecodesData +{ + using ERC165Checker for address; + using Witnet for bytes; + using WitnetCBOR for WitnetCBOR.CBOR; + using WitnetLib for uint64; + using WitnetLib for WitnetV2.DataSource; + using WitnetLib for WitnetV2.RadonSLA; + using WitnetLib for WitnetV2.RadonDataTypes; + + constructor( + bool _upgradable, + bytes32 _versionTag + ) + WitnetUpgradableBase( + _upgradable, + _versionTag, + "io.witnet.proxiable.bytecodes" + ) + {} + + receive() external payable override { + revert("WitnetBytecodes: no transfers"); + } + + // ================================================================================================================ + // --- Overrides IERC165 interface -------------------------------------------------------------------------------- + + /// @dev See {IERC165-supportsInterface}. + function supportsInterface(bytes4 _interfaceId) + public view + virtual override(WitnetUpgradableBase, ERC165) + returns (bool) + { + return _interfaceId == type(IWitnetBytecodes).interfaceId + || super.supportsInterface(_interfaceId); + } + + + // ================================================================================================================ + // --- Overrides 'Ownable2Step' ----------------------------------------------------------------------------------- + + /// Returns the address of the pending owner. + function pendingOwner() + public view + virtual override + returns (address) + { + return __bytecodes().pendingOwner; + } + + /// Returns the address of the current owner. + function owner() + public view + virtual override + returns (address) + { + return __bytecodes().owner; + } + + /// Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one. + /// @dev Can only be called by the current owner. + function transferOwnership(address _newOwner) + public + virtual override + onlyOwner + { + __bytecodes().pendingOwner = _newOwner; + emit OwnershipTransferStarted(owner(), _newOwner); + } + + /// @dev Transfers ownership of the contract to a new account (`_newOwner`) and deletes any pending owner. + /// @dev Internal function without access restriction. + function _transferOwnership(address _newOwner) + internal + virtual override + { + delete __bytecodes().pendingOwner; + address _oldOwner = owner(); + if (_newOwner != _oldOwner) { + __bytecodes().owner = _newOwner; + emit OwnershipTransferred(_oldOwner, _newOwner); + } + } + + + // ================================================================================================================ + // --- Overrides 'Upgradable' ------------------------------------------------------------------------------------- + + /// Initialize storage-context when invoked as delegatecall. + /// @dev Must fail when trying to initialize same instance more than once. + function initialize(bytes memory) + public + virtual override + { + address _owner = __bytecodes().owner; + if (_owner == address(0)) { + // set owner if none set yet + _owner = msg.sender; + __bytecodes().owner = _owner; + } else { + // only owner can initialize: + if (msg.sender != _owner) revert WitnetUpgradableBase.OnlyOwner(_owner); + } + + if (__bytecodes().base != address(0)) { + // current implementation cannot be initialized more than once: + if(__bytecodes().base == base()) revert WitnetUpgradableBase.AlreadyInitialized(base()); + } + __bytecodes().base = base(); + + emit Upgraded(msg.sender, base(), codehash(), version()); + } + + /// Tells whether provided address could eventually upgrade the contract. + function isUpgradableFrom(address _from) external view override returns (bool) { + address _owner = __bytecodes().owner; + return ( + // false if the WRB is intrinsically not upgradable, or `_from` is no owner + isUpgradable() + && _owner == _from + ); + } + + + // ================================================================================================================ + // --- Implementation of 'IWitnetBytecodes' ----------------------------------------------------------------------- + + function bytecodeOf(bytes32 _drRetrievalHash) + external view + override + returns (bytes memory) + { + return __database().retrievalsBytecode[_drRetrievalHash]; + } + + function bytecodeOf(bytes32 _drRetrievalHash, bytes32 _drSlaHash) + external view + returns (bytes memory) + { + return abi.encodePacked( + __database().retrievalsBytecode[_drRetrievalHash], + __database().slasBytecode[_drSlaHash] + ); + } + + function hashOf(bytes32 _drRetrievalHash, bytes32 _drSlaHash) + public pure + virtual override + returns (bytes32) + { + return sha256(abi.encode( + _drRetrievalHash, + _drSlaHash + )); + } + + function lookupDataProvider(uint256 _index) + external view + override + returns (WitnetV2.DataProvider memory) + { + return __database().providers[_index]; + } + + function lookupDataProviderIndex(string calldata _fqdn) + external view + override + returns (uint256) + { + return __database().providersIndex[keccak256(abi.encodePacked(_fqdn))]; + } + + function lookupDataSource(bytes32 _drDataSourceHash) + external view + override + returns (WitnetV2.DataSource memory) + { + return __database().sources[_drDataSourceHash]; + } + + function lookupRadonRetrievalAggregatorHash(bytes32 _drRetrievalHash) + external view + override + returns (WitnetV2.RadonReducer memory) + { + return __database().reducers[ + __retrieval(_drRetrievalHash).aggregator + ]; + } + + function lookupRadonRetrievalResultMaxSize(bytes32 _drRetrievalHash) + external view + override + returns (uint256) + { + IWitnetBytecodes.RadonRetrieval storage __rr = __retrieval(_drRetrievalHash); + return (__rr.resultMaxSize > 0 + ? __rr.resultMaxSize + : __rr.resultType.size() + ); + } + + function lookupRadonRetrievalResultType(bytes32 _drRetrievalHash) + external view + override + returns (WitnetV2.RadonDataTypes) + { + return __retrieval(_drRetrievalHash).resultType; + } + + function lookupRadonRetrievalSourceHashes(bytes32 _drRetrievalHash) + external view + override + returns (bytes32[] memory) + { + return __retrieval(_drRetrievalHash).sources; + } + + function lookupRadonRetrievalSourcesCount(bytes32 _drRetrievalHash) + external view + override + returns (uint) + { + return __retrieval(_drRetrievalHash).sources.length; + } + + function lookupRadonRetrievalTallyHash(bytes32 _drRetrievalHash) + external view + override + returns (WitnetV2.RadonReducer memory) + { + return __database().reducers[ + __retrieval(_drRetrievalHash).tally + ]; + } + + function lookupRadonSLA(bytes32 _drSlaHash) + external view + override + returns (WitnetV2.RadonSLA memory) + { + return __database().slas[_drSlaHash]; + } + + function lookupRadonSLAReward(bytes32 _drSlaHash) + external view + returns (uint64) + { + WitnetV2.RadonSLA storage __sla = __database().slas[_drSlaHash]; + return __sla.numWitnesses * __sla.witnessReward; + } + + function verifyDataSource( + WitnetV2.DataRequestMethods _method, + string memory _schema, + string memory _fqdn, + string memory _pathQuery, + string memory _body, + string[2][] memory _headers, + bytes memory _script + ) + public + virtual override + returns (bytes32 _dataSourceHash) + { + if (_headers[0].length != _headers[1].length) { + revert UnsupportedDataRequestHeaders(_headers); + } + if (bytes(_fqdn).length > 0 ){ + __pushDataProvider(_fqdn); + } + WitnetV2.DataSource memory _dds = WitnetV2.DataSource({ + method: _verifyDataSourceMethod(_method, _schema), + resultType: _verifyDataSourceScript(_script), + url: string(abi.encodePacked( + _schema, + _fqdn, + bytes("/"), + _pathQuery + )), + body: _body, + headers: _headers, + script: _script + }); + _dataSourceHash = keccak256(abi.encode(_dds)); + if (__database().sources[_dataSourceHash].method == WitnetV2.DataRequestMethods.Unknown) { + __database().sources[_dataSourceHash] = _dds; + emit NewDataSourceHash(_dataSourceHash, _dds.url); + } + } + + function verifyRadonReducer(WitnetV2.RadonReducer calldata _ddr) + public + virtual override + returns (bytes32 _ddrHash) + { + bytes memory _ddrBytes; + for (uint _ix = 0; _ix < _ddr.filters.length; ) { + _ddrBytes = abi.encodePacked( + _ddrBytes, + _verifyDataFilter(_ddr.filters[_ix]) + ); + unchecked { + _ix ++; + } + } + _ddrBytes = abi.encodePacked( + _ddrBytes, + _verifyRadonReducerOps(_ddr.op) + ); + _ddrHash = _ddrBytes.hash(); + if (__database().reducersBytecode[_ddrHash].length == 0) { + __database().reducersBytecode[_ddrHash] = _ddrBytes; + __database().reducers[_ddrHash] = _ddr; + emit NewDataReducerHash(_ddrHash); + } + } + + function verifyRadonRetrieval(RadonRetrieval memory _retrieval) + public + virtual override + returns (bytes32 _drRetrievalHash) + { + // Check number of provided sources and template args: + if (_retrieval.sources.length == 0) { + revert RadonRetrievalNoSources(); + } + if ( _retrieval.sources.length != _retrieval.args.length) { + revert RadonRetrievalArgsMismatch(_retrieval.args); + } + // Check provided result type and result max size: + if (!_verifyDataTypeMaxSize(_retrieval.resultType, _retrieval.resultMaxSize)) { + revert UnsupportedRadonDataType( + uint8(_retrieval.resultType), + _retrieval.resultMaxSize + ); + } + // Build data source bytecode in memory: + bytes[] memory _sourcesBytecodes = new bytes[](_retrieval.sources.length); + for (uint _ix = 0; _ix < _sourcesBytecodes.length; ) { + WitnetV2.DataSource memory _ds = __database().sources[ + _retrieval.sources[_ix] + ]; + // Check all result types of provided sources match w/ `_retrieval.resultType` + if (_ds.resultType != _retrieval.resultType) { + revert RadonRetrievalResultsMismatch( + uint8(_ds.resultType), + uint8(_retrieval.resultType) + ); + } + // Replace wildcards where needed: + if (_retrieval.args[_ix].length > 0) { + _ds = _replaceWildcards(_ds, _retrieval.args[_ix]); + } + // Encode data source: + _sourcesBytecodes[_ix] = _ds.encode(); + unchecked { + _ix ++; + } + } + // Get pointer to aggregator bytecode in storage: + bytes storage __aggregatorBytes = __database().reducersBytecode[_retrieval.aggregator]; + // Get pointer to tally bytecode in storage: + bytes storage __tallyBytes; + if (_retrieval.aggregator == _retrieval.tally) { + __tallyBytes = __aggregatorBytes; + } else { + __tallyBytes = __database().reducersBytecode[_retrieval.tally]; + require( + __tallyBytes.length > 0, + "WitnetBytecodes: no tally" + ); + } + require( + __aggregatorBytes.length > 0, + "WitnetBytecodes: no aggregator" + ); + // Build retrieval bytecode: + bytes memory _retrievalBytes = _encodeRadonRetrieval( + WitnetBuffer.concat(_sourcesBytecodes), + __aggregatorBytes, + __tallyBytes, + _retrieval.resultMaxSize + ); + // Calculate hash and add to storage if new: + _drRetrievalHash = _retrievalBytes.hash(); + if (__database().retrievalsBytecode[_drRetrievalHash].length == 0) { + __database().retrievalsBytecode[_drRetrievalHash] = _retrievalBytes; + __database().retrievals[_drRetrievalHash] = _retrieval; + emit NewRadonRetrievalHash(_drRetrievalHash); + } + } + + function verifyRadonSLA(WitnetV2.RadonSLA memory _drSla) + external + virtual override + returns (bytes32 _drSlaHash) + { + if (_drSla.witnessReward == 0) { + revert RadonRetrievalNoSources(); + } + if (_drSla.numWitnesses == 0) { + revert RadonSlaNoWitnesses(); + } else if (_drSla.numWitnesses > 127) { + revert RadonSlaTooManyWitnesses(_drSla.numWitnesses); + } + if ( + _drSla.minConsensusPercentage < 51 + || _drSla.minConsensusPercentage > 99 + ) { + revert RadonSlaConsensusOutOfRange(_drSla.minConsensusPercentage); + } + if (_drSla.collateral < 10 ** 9) { + revert RadonSlaLowCollateral(_drSla.collateral); + } + bytes memory _drSlaBytecode = _drSla.encode(); + _drSlaHash = _drSlaBytecode.hash(); + if (__database().slasBytecode[_drSlaHash].length == 0) { + __database().slasBytecode[_drSlaHash] = _drSlaBytecode; + __database().slas[_drSlaHash] = _drSla; + emit NewDrSlaHash(_drSlaHash); + } + } + + function totalDataProviders() + external view + override + returns (uint) + { + return __bytecodes().totalDataProviders; + } + + + // ================================================================================================================ + // --- Internal view/pure methods --------------------------------------------------------------------------------- + + function _encodeRadonRetrieval( + bytes memory _encodedSources, + bytes memory _encodedAggregator, + bytes memory _encodedTally, + uint16 _resultMaxSize + ) + virtual + internal pure + returns (bytes memory _encodedRetrieval) + { + _encodedRetrieval = abi.encodePacked( + uint64(_encodedAggregator.length).encode(bytes1(0x1a)), + uint64(_encodedTally.length).encode(bytes1(0x2a)), + _encodeRadonRetrievalResultMaxSize(_resultMaxSize) + ); + uint64 _retrievalSize = uint64( + _encodedSources.length + + _encodedRetrieval.length + ); + return abi.encodePacked( + _retrievalSize.encode(bytes1(0x0a)), + _encodedSources, + _encodedRetrieval + ); + } + + function _encodeRadonRetrievalResultMaxSize(uint16 _maxsize) + virtual + internal pure + returns (bytes memory _encodedMaxSize) + { + if (_maxsize > 0) { + _encodedMaxSize = uint64(_maxsize).encode(0x28); + } + } + + function _replaceWildcards( + WitnetV2.DataSource memory _ds, + string[] memory _args + ) + internal pure + virtual + returns (WitnetV2.DataSource memory) + { + _ds.url = string(WitnetBuffer.replaceWildcards(bytes(_ds.url), _args)); + _ds.body = string(WitnetBuffer.replaceWildcards(bytes(_ds.url), _args)); + WitnetCBOR.CBOR memory _cborScript = WitnetLib.replaceCborBytesWildcards(_ds.script, _args); + _ds.script = _cborScript.buffer.data; + return _ds; + } + + function _uint16InRange(uint16 _value, uint16 _min, uint16 _max) + internal pure + returns (bool) + { + return _value >= _min && _value <= _max; + } + + function _verifyDataFilter(WitnetV2.RadonFilter memory _filter) + internal pure + virtual + returns (bytes memory _filterBytes) + { + _filterBytes = _verifyDataFilterOps(_filter.op, _filter.cborArgs); + if (_filter.cborArgs.length > 0) { + _filterBytes = abi.encodePacked( + _filterBytes, + uint64(_filter.cborArgs.length).encode(bytes1(0x12)), + _filter.cborArgs + ); + } + return abi.encodePacked( + uint64(_filterBytes.length).encode(bytes1(0x0a)), + _filterBytes + ); + } + + function _verifyDataFilterOps(WitnetV2.RadonFilterOpcodes _opcode, bytes memory _args) + virtual + internal pure + returns (bytes memory) + { + if ( + _opcode == WitnetV2.RadonFilterOpcodes.StandardDeviation + && _args.length == 0 + || _opcode != WitnetV2.RadonFilterOpcodes.Mode + ) { + revert UnsupportedRadonFilter(uint8(_opcode), _args); + } + return uint64(_opcode).encode(bytes1(0x08)); + } + + function _verifyRadonReducerOps(WitnetV2.RadonReducerOpcodes _reducer) + virtual + internal view + returns (bytes memory) + { + if (!( + _reducer == WitnetV2.RadonReducerOpcodes.AverageMean + || _reducer == WitnetV2.RadonReducerOpcodes.StandardDeviation + || _reducer == WitnetV2.RadonReducerOpcodes.Mode + || _reducer == WitnetV2.RadonReducerOpcodes.ConcatenateAndHash + || _reducer == WitnetV2.RadonReducerOpcodes.AverageMedian + )) { + revert UnsupportedRadonReducer(uint8(_reducer)); + } + return uint64(_reducer).encode(bytes1(0x10)); + } + + function _verifyDataSourceMethod( + WitnetV2.DataRequestMethods _method, + string memory _schema + ) + virtual + internal pure + returns (WitnetV2.DataRequestMethods) + { + if ( + _method == WitnetV2.DataRequestMethods.Rng + && keccak256(bytes(_schema)) != keccak256(bytes("https://")) + && keccak256(bytes(_schema)) != keccak256(bytes("http://")) + || !_uint16InRange(uint16(_method), uint16(1), uint16(3)) + ) { + revert UnsupportedDataRequestMethod(uint8(_method), _schema); + } + return _method; + } + + function _verifyDataSourceCborScript(WitnetCBOR.CBOR memory _cbor) + virtual + internal view + returns (WitnetV2.RadonDataTypes _resultType) + { + if (_cbor.majorType == 4) { + WitnetCBOR.CBOR[] memory _items = _cbor.readArray(); + if (_items.length > 0) { + return _verifyDataSourceCborScript( + _items[_items.length - 1] + ); + } else { + return WitnetV2.RadonDataTypes.Any; + } + } else if (_cbor.majorType == 0) { + return _lookupOpcodeResultType(uint8(_cbor.readUint())); + } else { + revert WitnetCBOR.UnexpectedMajorType(0, _cbor.majorType); + } + } + + function _verifyDataSourceScript(bytes memory _script) + virtual + internal view + returns (WitnetV2.RadonDataTypes _resultType) + { + return _verifyDataSourceCborScript( + WitnetCBOR.valueFromBytes(_script) + ); + } + + function _verifyDataTypeMaxSize(WitnetV2.RadonDataTypes _dt, uint16 _maxsize) + virtual + internal pure + returns (bool) + { + if ( + _dt == WitnetV2.RadonDataTypes.Array + || _dt == WitnetV2.RadonDataTypes.Bytes + || _dt == WitnetV2.RadonDataTypes.Map + || _dt == WitnetV2.RadonDataTypes.String + ) { + return _uint16InRange(_maxsize, uint16(1), uint16(2048)); + } else { + return _uint16InRange(uint16(_dt), uint16(1), uint16(type(WitnetV2.RadonDataTypes).max)); + } + } + + + // ================================================================================================================ + // --- Internal state-modifying methods --------------------------------------------------------------------------- + + function __pushDataProvider(string memory _fqdn) + internal + virtual + returns (bytes32 _hash) + { + _hash = keccak256(abi.encodePacked(_fqdn)); + uint _index = __database().providersIndex[_hash]; + if (_index == 0) { + _index = ++ __bytecodes().totalDataProviders; + __database().providers[_index] = WitnetV2.DataProvider({ + fqdn: _fqdn, + totalSources: 1, + totalRetrievals: 0 + }); + emit NewDataProvider(_fqdn, _index); + } else { + __database().providers[_index].totalSources ++; + } + } + +} \ No newline at end of file diff --git a/contracts/interfaces/V2/IWitnetBytecodes.sol b/contracts/interfaces/V2/IWitnetBytecodes.sol index 44e4bc1dc..34937641b 100644 --- a/contracts/interfaces/V2/IWitnetBytecodes.sol +++ b/contracts/interfaces/V2/IWitnetBytecodes.sol @@ -5,29 +5,70 @@ pragma solidity >=0.8.0 <0.9.0; import "../../libs/WitnetV2.sol"; interface IWitnetBytecodes { + + struct RadonRetrieval { + WitnetV2.RadonDataTypes resultType; + uint16 resultMaxSize; + string[][] args; + bytes32[] sources; + bytes32 aggregator; + bytes32 tally; + } + + error RadonRetrievalNoSources(); + error RadonRetrievalArgsMismatch(string[][] args); + error RadonRetrievalResultsMismatch(uint8 read, uint8 expected); + + error RadonSlaNoReward(); + error RadonSlaNoWitnesses(); + error RadonSlaTooManyWitnesses(uint256 numWitnesses); + error RadonSlaConsensusOutOfRange(uint256 percentage); + error RadonSlaLowCollateral(uint256 collateral); + + error UnsupportedDataRequestMethod(uint8 method, string schema); + error UnsupportedDataRequestHeaders(string[2][] headers); + error UnsupportedRadonDataType(uint8 datatype, uint256 maxlength); + error UnsupportedRadonFilter(uint8 filter, bytes args); + error UnsupportedRadonReducer(uint8 reducer); + error UnsupportedRadonScript(bytes script, uint256 offset); + error UnsupportedRadonScriptOpcode(uint8 opcode); + + event NewDataProvider(string fqdn, uint256 index); + event NewDataReducerHash(bytes32 hash); + event NewDataSourceHash(bytes32 hash, string url); + event NewRadonRetrievalHash(bytes32 hash); + event NewDrSlaHash(bytes32 hash); + + function bytecodeOf(bytes32 _drRetrievalHash) external view returns (bytes memory); + function bytecodeOf(bytes32 _drRetrievalHash, bytes32 _drSlaHash) external view returns (bytes memory); + + function hashOf(bytes32 _drRetrievalHash, bytes32 _drSlaHash) external pure returns (bytes32 _drQueryHash); + + function lookupDataProvider(uint256) external view returns (WitnetV2.DataProvider memory); + function lookupDataProviderIndex(string calldata) external view returns (uint); + function lookupDataSource(bytes32 _drDataSourceHash) external view returns (WitnetV2.DataSource memory); + function lookupRadonRetrievalAggregatorHash(bytes32 _drRetrievalHash) external view returns (WitnetV2.RadonReducer memory); + function lookupRadonRetrievalResultMaxSize(bytes32 _drRetrievalHash) external view returns (uint256); + function lookupRadonRetrievalResultType(bytes32 _drRetrievalHash) external view returns (WitnetV2.RadonDataTypes); + function lookupRadonRetrievalSourceHashes(bytes32 _drRetrievalHash) external view returns (bytes32[] memory); + function lookupRadonRetrievalSourcesCount(bytes32 _drRetrievalHash) external view returns (uint); + function lookupRadonRetrievalTallyHash(bytes32 _drRetrievalHash) external view returns (WitnetV2.RadonReducer memory); + function lookupRadonSLA(bytes32 _drSlaHash) external view returns (WitnetV2.RadonSLA memory); + function lookupRadonSLAReward(bytes32 _drSlaHash) external view returns (uint64); - event DrQueryHash(bytes32 drQueryHash); - event DrRadonHash(bytes32 drRadonHash); - event DrRadonTemplateHash(bytes32 drRadonTemplateHash, WitnetV2.Types[] types); - event DrSlaHash(bytes32 drSlaHash); - - function bytecodeOf(bytes32 _drRadonHash) external view returns (bytes memory); - function bytecodeOf(bytes32 _drRadonHash, bytes32 _drSlaHash) external view returns (bytes memory); - function bytecodeOf(bytes32 _drRadonHash, bytes[] calldata _drArgs) external view returns (bytes memory); - function bytecodeOf(bytes32 _drRadonHash, bytes32 _drSlaHash, bytes[] calldata _drArgs) external view returns (bytes memory); - - function hashOf(bytes32 _drRadonHash, bytes32 _drSlaHash) external pure returns (bytes32); - function hashOf(bytes32 _drRadonHash, bytes32 _drSlaHash, bytes[] calldata _drArgs) external view returns (bytes32); - - function lookupDrSla(bytes32 _drSlaHash) external view returns (WitnetV2.DrSla memory); - function lookupDrSlaReward(bytes32 _drSlaHash) external view returns (uint256); - function lookupDrSources(bytes32 _drRadHash) external view returns (string[] memory); - function lookupDrInputTypes(bytes32 _drRadonHash) external view returns (WitnetV2.Types[] memory); - function lookupDrResultSize(bytes32 _drRadonHash) external view returns (uint256); - function lookupDrResultType(bytes32 _drRadonHash) external view returns (WitnetV2.Types); - - function verifyDrRadonBytes(bytes calldata _drRadonBytes) external returns (bytes32 _drRadonHash); - function verifyDrRadonTemplateBytes(bytes calldata _drRadonBytes, WitnetV2.Types[] calldata _types) external returns (bytes32 _drRadonHash); - function verifyDrSla(WitnetV2.DrSla calldata _drSla) external returns (bytes32 _drSlaHash); + function verifyDataSource( + WitnetV2.DataRequestMethods _requestMethod, + string calldata _requestSchema, + string calldata _requestFQDN, + string calldata _requestPathQuery, + string calldata _requestBody, + string[2][] calldata _requestHeaders, + bytes calldata _witnetScript + ) external returns (bytes32); + function verifyRadonReducer(WitnetV2.RadonReducer calldata) external returns (bytes32); + function verifyRadonRetrieval(RadonRetrieval calldata) external returns (bytes32); + function verifyRadonSLA(WitnetV2.RadonSLA calldata _drSla) external returns (bytes32); + function totalDataProviders() external view returns (uint); + } \ No newline at end of file diff --git a/migrations/jsons/witnet-data-feeds.json b/migrations/jsons/witnet-data-feeds.json new file mode 100644 index 000000000..92ad574de --- /dev/null +++ b/migrations/jsons/witnet-data-feeds.json @@ -0,0 +1,27 @@ +{ + "default": { + "ethereum.goerli": [ + "Price-BTC/USD-6", + "Price-ETH/USD-6", + "Price-NATION/USDT-6" + ], + "ethereum.mainnet": [ + "Price-ETH/USD-6" + ] + }, + "arbitrum": { + "arbitrum.goerli": [ + "Price-ETH/USD-6" + ] + }, + "avalanche": { + "avalanche.testnet": [ + "Price-AVAX/USD-6", + "Price-BTC/USD-6", + "Price-ETH/USD-6" + ], + "avalanche.mainnet": [ + "Price-AVAX/USD-6" + ] + } +} \ No newline at end of file diff --git a/migrations/jsons/witnet-data-reducers.json b/migrations/jsons/witnet-data-reducers.json new file mode 100644 index 000000000..cf030b8a5 --- /dev/null +++ b/migrations/jsons/witnet-data-reducers.json @@ -0,0 +1,22 @@ +{ + "Avg-Stdev-1.5": { + "hash": "0x", + "reducer": 3, + "filters": { + "filter": 5, + "args": 1.5 + } + }, + "Avg-Stdev-2.5": { + "hash": "0x", + "reducer": 3, + "filters": { + "filter": 5, + "args": 1.5 + } + }, + "Concat-And-Hash": { + "hash": "0x", + "reducer": 12 + } +} \ No newline at end of file diff --git a/migrations/jsons/witnet-data-sources.json b/migrations/jsons/witnet-data-sources.json new file mode 100644 index 000000000..db21e6af6 --- /dev/null +++ b/migrations/jsons/witnet-data-sources.json @@ -0,0 +1,910 @@ +{ + "api.aex.zone": { + "ticker": { + "hash": "0x", + "requestPath": "v3/ticker.php", + "requestQuery": "mk_type=\\1\\&coinname=\\0\\", + "templateValues": [ [ "BOBA" ], [ "USDT" ]], + "witnetScript": [ + "parseJSONMap", + [ "getMap", "data" ], + [ "getMap", "ticker" ], + [ "getFloat", "last" ], + [ "multiply", 1000000 ], + "round" + ] + } + }, + "api.binance.com": { + "ticker": { + "hash": "0x", + "requestPath": "api/v3/ticker/price", + "requestQuery": "symbol=\\0\\\\1\\", + "templateValues": [ + [ + "CFX", "FTM", "FXS", "GLMR", "KAVA", + "KLAY", "OMVR", "OMG", "OP", "REEF", + "SYS" + ], + [ "BTC", "ETH", "USD", "USDT" ] + ], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getFloat", "price" ], + [ "multiply", 1000000 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "api.binance.us": { + "ticker": { + "hash": "0x", + "requestPath": "api/v3/ticker/price", + "requestQuery": "symbol=\\0\\\\1\\", + "templateValues": [ + [ + "AVAX", "APE", "AVAX", "BTC", "DAI", + "DOGE", "ETH", "MATIC", "USDC", "USDT" + ], + [ "USD"] + ], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getFloat", "price" ], + [ "multiply", 1000000 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "api.bitfinex.com": { + "ticker": { + "hash": "0x", + "requestPath": "v1/pubticker/\\0\\\\1\\", + "templateValues": [ + [ "alg", "btc", "eth", "omg" ], + [ "eth", "btc", "usd" ] + ], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getFloat", "last_price" ], + [ "multiply", 1000000 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "api.bitget.com": { + "ticker": { + "hash": "0x", + "requestPath": "api/spot/v1/market/ticker", + "requestQuery": "symbol=\\0\\", + "templateValues": [[ "SYSUSDT_SPBL" ]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getMap", "data" ], + [ "getFloat", "close" ], + [ "multiply", 1e6 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "api.bittrex.com": { + "ticker": { + "hash": "0x", + "requestPath": "v3/markets/\\0\\-\\1\\/ticker", + "templateValues": [ + [ + "ADA", "ATOM", "AVAX", "BCH", "BTC", + "CELO", "CRO", "DAI", "DOGE", "DOT", + "EOS", "ETH", "FTM", "TUSDT", "USDC", + "USDT", "XML" + ], + [ "EUR", "USD", "USDT" ] + ], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getFloat", "lastTradeRate" ], + [ "multiply", 1000000 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "api.bkex.com": { + "ticker": { + "hash": "0x", + "requestPath": "v2/q/ticker/price", + "requestQuery": "symbol=\\0\\_\\1\\", + "templateValues": [[ "OP", "REEF", "SYS" ], [ "USDT" ]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getArray", "data" ], + [ "getMap", 0 ], + [ "getFloat", "price" ], + [ "multiply", 1e6 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "api.coinbase.com": { + "ticker": { + "hash": "0x", + "requestPath": "v2/exchange-rates", + "requestQuery": "currency=\\1\\", + "templateValues": [ + [ + "ADA", "ALGO", "APE", "ATOM", "AVAX", + "BCH", "BTC", "CGLD", "CRO", "DOGE", + "DOT", "EOS", "ETH", "LINK", "MATIC", + "SHIB", "SOL", "WBTC", "XLM" + ], + [ "EUR", "USD" ] + ], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getMap", "data" ], + [ "getMap", "rates" ], + [ "getFloat", "\\0\\" ], + [ "power", -1 ], + [ "multiply", 1000000 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "api.coinone.co.kr": { + "ticker": { + "hash": "0x", + "requestPath": "public/v2/ticker_new/\\0\\/\\1\\", + "templateValues": [[ "krw" ], [ "ksp" ]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getArray", "tickers" ], + [ "getMap", 0 ], + [ "getFloat", "last" ], + [ "multiply", 1000 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "api.exchangerate.host": { + "ticker": { + "hash": "0x", + "requestPath": "latest", + "requestQuery": "base=\\0\\&symbol=\\1\\", + "templateValues": [[ "KRW" ], [ "USD" ]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getMap", "rates" ], + [ "getFloat", "\\1\\" ], + [ "multiply", 1e9 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "api.fastforex.io": { + "ticker": { + "hash": "0x", + "requestPath": "fetch-one", + "requestQuery": "from=\\0\\&to=\\1\\&api_key=demo", + "templateValues": [[ "KRW" ], [ "USD" ]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getMap", "result" ], + [ "getFloat", "\\1\\" ], + [ "multiply", 1e9 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "api.gemini.com": { + "ticker": { + "hash": "0x", + "requestPath": "v1/pubticker/\\0\\\\1\\", + "templateValues": [[ "usdc" ], [ "usd" ]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getFloat", "last" ], + [ "multiply", 1e6 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "api.hitbtc.com": { + "ticker": { + "hash": "0x", + "requestPath": "api/3/public/ticker", + "requestQuery": "symbols=\\0\\", + "templateValues": [[ "REEFUSDT" ]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getMap", "\\0\\" ], + [ "getFloat", "last" ], + [ "multiply", 1e6 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "api.hotbit.io": { + "ticker": { + "hash": "0x", + "requestPath": "api/v1/market.last", + "requestQuery": "market=\\0\\\\1\\", + "templateValues": [[ "GLMR", "METIS" ], [ "USDT" ]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getFloat", "result" ], + [ "multiply", 1000000 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "api.huobi.pro": { + "ticker": { + "hash": "0x", + "requestPath": "market/detail/merged", + "requestQuery": "symbol=\\0\\\\1\\", + "templateValues": [ + [ "boba", "boring", "cube", "elon", "ftm", "kava" ], + [ "usdt" ] + ], + "witnetScript": { + "dsl": [], + "bytecode": "0x" + } + } + }, + "api.killswitch.finance": { + "ticker": { + "hash": "0x", + "requestPath": "ksw2/prices", + "requestQuery": "chain=56", + "templateValues": [[]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getFloat", "\\0\\" ], + [ "multiply", 1e6 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "api.korbit.co.kr": { + "ticker": { + "hash": "0x", + "requestPath": "v1/ticker/detailed", + "requestQuery": "currency_pair=\\0\\_\\1\\", + "tempalteValues": [[ "ksp" ], [ "krw" ]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getFloat", "last" ], + [ "multiply", 1000 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "api.kraken.com": { + "ticker": { + "alias": "ticker", + "hash": "0x", + "requestPath": "0/public/Ticker", + "requestQuery": "pair=\\0\\", + "templateValues": [ + [ + "ADA", "ALGO", "APE", "ATOM", "AVAX", + "BCH", "BTC", "DAI", "DOGE", "DOT", + "EOS", "ETH", "MATIC", "OMG", "SHIB", + "SOL", "USDC", "USDT", "WBTC", "XLM" + ], + [ "BTC", "ETH", "USD" ] + ], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getMap", "result" ], + [ "getMap", "\\0\\\\1\\" ], + [ "getArray", "a" ], + [ "getFloat", 0 ], + [ "multiply", 1000000 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "api.kucoin.com": { + "ticker": { + "hash": "0x", + "requestPath": "api/v1/market/orderbook/level1", + "requestQuery": "symbol=\\0\\-\\1\\", + "templateValues": [ + [ + "CFX", "CRO", "ELON", "FTM", "FXS", + "GLMR", "KAVA", "KCS", "KLAY", "MJT", + "MOVR", "MTRG", "OP", "REEF", "SYS" + ], + [ "KCS", "USDT" ] + ], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getMap", "data" ], + [ "getFloat", "price" ], + [ "multiply", 1000000 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "api.pancakeswap.info": { + "ticker": { + "hash": "0x", + "requestPath": "api/v2/tokens/\\0\\", + "templateValues": [[]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getMap", "data" ], + [ "getFloat", "price" ], + [ "multiply", 1e6 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "api.thegraph.com": { + "beamswap-pair-token0": { + "hash": "0x", + "requestMethod": "http-post", + "requestPath": "subgraphs/name/beamswap/beamswap-dex", + "requestBody": "{ pair (id: \"\\0\\\") { token0Price } }", + "templateValues": [[]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getMap", "data" ], + [ "getMap", "pair" ], + [ "getFloat", "token0Price" ], + [ "multiply", 1000000 ], + "round" + ], + "bytecode": "0x" + } + }, + "uniswap-pair-token0#celo": { + "hash": "0x", + "requestMethod": "http-post", + "requestPath": "subgraphs/name/jesse-sawa/uniswap-celo", + "requestBody": "{ pair (id: \"\\0\\\") { token1Price } }", + "templateValues": [[]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getMap", "data" ], + [ "getMap", "pool" ], + [ "getFloat", "token1Price" ], + [ "multiply", 1e6 ], + "round" + ], + "bytecode": "0x" + } + }, + "oolongswap-pairs": { + "hash": "0x", + "requestMethod": "http-post", + "requestPath": "subgraphs/name/oolongswap/oolongswap-mainnet", + "requestBody": "{ pairs ( where: { token0: \"\\0\\\", token1: \"\\1\\\" } ) { token1Price } }", + "templateValues": [[], []], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getMap", "data" ], + [ "getMap", "pairs" ], + [ "getMap", 0 ], + [ "getFloat", "token1Price" ], + [ "multiply", 1e6 ], + "round" + ], + "bytecode": "0x" + } + }, + "stellaswap-pair-token1": { + "hash": "0x", + "requestMethod": "http-post", + "requestPath": "subgraphs/name/stellaswap/stella-swap", + "requestBody": "{ pair (id: \"\\0\\\") { token1Price } }", + "templateValues": [[]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getMap", "data" ], + [ "getMap", "pair" ], + [ "getFloat", "token1Price" ], + [ "multiply", 1e6 ], + "round" + ], + "bytecode": "0x" + } + }, + "sushiswap-pair-token1#matic": { + "hash": "0x", + "requestMethod": "http-post", + "requestPath": "subgraphs/name/sushiswap/matic-exchange", + "requestBody": "{ pair (id: \"\\0\\\") { token1Price } }", + "templateValues": [[]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getMap", "data" ], + [ "getMap", "pair" ], + [ "getFloat", "token1Price" ], + [ "multiply", 1e6 ], + "round" + ], + "bytecode": "0x" + } + }, + "ubeswap-pairs": { + "hash": "0x", + "requestPath": "subgraphs/name/ubeswap/ubeswap", + "requestBody": "query PairsCurrent { pairs (first: 100, orderBy: reserveUSD, orderDirection: desc, subgraphError: allow ) { id token0Price } }", + "templateValues": [[]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getMap", "data" ], + [ "getArray", "pairs" ], + [ "filter", [ + [ "getString", "id" ], + [ "match", [ + [ "\\0\\", true ], + false + ]] + ]], + [ "getMap", 0 ], + [ "getFloat", "token0Price" ], + [ "multiply", 1000000 ], + "round" + ], + "bytecode": "0x" + } + }, + "uniswap-v3-pool-token1": { + "hash": "0x", + "requestMethod": "http-post", + "requestPath": "subgraphs/name/uniswap/uniswap-v3", + "requestBody": "{ pool (id: \"\\0\\\") { token1Price }}", + "templateValues": [[]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getMap", "data" ], + [ "getMap", "pool" ], + [ "getFloat", "token1Price" ], + [ "multiply", 1e6 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "api-pub.bitfinex.com": { + "ticker": { + "hash": "0x", + "requestPath": "v2/ticker/\\0\\", + "templateValues": [ [ "tTSDUSD" ]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getFloat", 0 ], + [ "multiply", 1000000 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "ascendex.com": { + "ticker": { + "hash": "0x", + "requestPath": "api/pro/v1/spot/ticker", + "requestQuery": "symbol=\\0\\/\\1\\", + "templateValues": [[ "CRO", "KCS" ], [ "USDT" ]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getMap", "data" ], + [ "getFloat", "close" ], + [ "multiply", 1000000 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "cdn.jsdelivr.net": { + "ticker": { + "hash": "0x", + "requestPath": "gh/fawazahmed0/currency-api@1/latest/currencies/\\0\\.json", + "templateValues": [[ "krw"], [ "usd" ]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getMap", "\\0\\" ], + [ "getFloat", "\\1\\" ], + [ "multiply", 1e9 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "coinyep.com": { + "ticker": { + "hash": "0x", + "requestPath": "api/v1/", + "requestQuery": "from=\\0\\&to=\\1\\&lang=es&format=json", + "templateValues": [[ "OMG" ], [ "ETH" ]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getFloat", "price" ], + [ "multiply", 1e9 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "data.gateapi.io": { + "ticker": { + "hash": "0x", + "requestPath": "api2/1/ticker/\\0\\_\\1\\", + "templateValues": [ + [ + "boba", "boring", "cfx", "cro", "cube", + "dai", "elon", "frax", "ftm", "fxs", + "glmr", "kava", "metis", "movr", "mtrg", + "mtr", "okt", "omg", "op", "reef", + "shib", "sys" ], + [ "btc", "eth", "usd", "usdt"] + ], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getFloat", "last" ], + [ "multiply", 1000000 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "data.messari.io": { + "ticker": { + "hash": "0x", + "requestPath": "api/v1/assets/\\0\\/metrics/market-data", + "requestQuery": "fields=market_data/price_\\1\\", + "templateValues": [[ "omg" ], [ "eth "]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getMap", "data" ], + [ "getMap", "market_data" ], + [ "getFloat", "price_\\1\\" ], + [ "multiply", 1e9 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "info.kuswap.finance": { + "pair-token0": { + "hash": "0x", + "requestMethod": "http-post", + "requestPath": "subgraphs/name/kuswap/swap", + "requestBody": "{ pair (id: \"\\0\\\") { token0Price } }", + "templateValues": [[]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getMap", "data" ], + [ "getMap", "pair" ], + [ "getFloat", "token0Price" ], + [ "multiply", 1e6 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "openapi.bitrue.com": { + "ticker": { + "hash": "0x", + "requestPath": "api/v1/ticker/price", + "requestQuery": "symbol=\\0\\", + "templateValues": [[ "REEFUSDT" ]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getFloat", "price" ], + [ "multiply", 1e6 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "openapi.digifinex.com": { + "ticker": { + "hash": "0x", + "requestPath": "v3/ticker", + "requestQuery": "symbol=\\0\\_\\1\\", + "templateValues": [[ "op", "reef", "sys" ], [ "usdt" ]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getArray", "ticker" ], + [ "getMap", 0 ], + [ "getFloat", "last" ], + [ "multiply", 1e6 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "v2api.coinflex.com": { + "ticker": { + "hash": "0x", + "requestPath": "v3/tickers", + "requestQuery": "marketCode=\\0\\-\\1\\", + "templateValues": [[], []], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getArray", "data" ], + [ "getMap", 0 ], + [ "getFloat", "markPrice" ], + [ "multiply", 1000000 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "thegraph.kcc.network": { + "mojito-pair-token0": { + "hash": "0x", + "requestMethod": "http-post", + "requestPath": "subgraphs/name/mojito/swap", + "requestBody": "{ pair (id: \"\\0\\\") { token0Price } }", + "templateValues": [[]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getMap", "data" ], + [ "getMap", "pair" ], + [ "getFloat", "token0Price" ], + [ "multiply", 1000000 ], + "round" + ], + "bytecode": "0x" + } + }, + "mojito-pair-token1": { + "hash": "0x", + "requestMethod": "http-post", + "requestPath": "subgraphs/name/mojito/swap", + "requestBody": "{ pair (id: \"\\0\\\") { token1Price } }", + "templateValues": [[]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getMap", "data" ], + [ "getMap", "pair" ], + [ "getFloat", "token1Price" ], + [ "multiply", 1e9 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "www.bitstamp.net": { + "ticker": { + "alias": "ticker", + "hash": "0x", + "requestPath": "api/v2/ticker/\\0\\\\1\\", + "templateValues": [ + [ + "ada", "algo", "bch", "btc", "dai", + "eth", "link", "matic", "omg", "usdc", + "usdt", "xlm" + ], + [ "btc", "usd" ] + ], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getFloat", "last" ], + [ "getMap", "pool" ], + [ "multiply", 1000000 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "www.freeforexapi.com": { + "ticker": { + "hash": "0x", + "requestPath": "api/live", + "requestQuery": "pairs=\\0\\", + "templateValues": [[ "USDKRW" ]], + "witnetScript": [ + "parseJSONMap", + [ "getMap", "rates" ], + [ "getMap", "\\0\\" ], + [ "getFloat", "rate" ], + [ "power", -1 ], + [ "multiply", 1e9 ], + "round" + ] + } + }, + "www.live-rates.com": { + "ticker": { + "hash": "0x", + "requestPath": "rates", + "templateValues": [[ "KRW" ], [ "USD" ]], + "witnetScript": { + "dsl": [ + "parseJSONArray", + [ "filter", [ + [ "getString", "currency" ], + [ "match", [ + [ "\\1\\/\\0\\", true ], + false + ]] + ]], + [ "getMap", 0 ], + [ "getFloat", "rate" ], + [ "power", -1 ], + [ "multiply", 1e9 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "www.mexc.com": { + "ticker": { + "hash": "0x", + "requestPath": "open/api/v2/market/ticker", + "requestQuery": "symbol=\\0\\_\\1\\", + "templateValues": [ + [ + "BOBA", "BORING", "CFX", "ELON", "FTM", + "FXS", "GLMR", "KAVA", "KCS", "METIS", + "MTRG", "MTR", "NATION", "OKT", "OMG", + "SYS" + ], + [ "BTC", "ETH", "USDT" ] + ], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getArray", "data" ], + [ "getMap", 0 ], + [ "getFloat", "last" ], + [ "multiply", 1000000 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "www.okcoin.com": { + "ticker": { + "hash": "0x", + "requestPath": "api/spot/v3/instruments/\\0\\-\\1\\/ticker", + "templateValues": [[ "CELO" ], [ "USD" ]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getFloat", "last" ], + [ "multiply", 1000000 ], + "round" + ], + "bytecode": "0xº" + } + } + }, + "www.okx.com": { + "ticker": { + "hash": "0x", + "requestPath": "api/v5/market/ticker", + "requestQuery": "instId=\\0\\-\\1\\", + "templateValues": [[], []], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getArray", "data" ], + [ "getMap", 0 ], + [ "getFloat", "last" ], + [ "multiply", 1000000 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "www.xt.pub": { + "ticker": { + "hash": "0x", + "requestPath": "exchange/api/markets/returnTicker", + "templateValues": [[ "CUBE" ], [ "USDT" ]], + "witnetScript": { + "dsl": [ + "parseJSONMap", + [ "getMap", "\\0\\_\\1\\" ], + [ "getFloat", "last" ], + [ "multiply", 1000000 ], + "round" + ], + "bytecode": "0x" + } + } + }, + "witnet.eth": { + "randomness": { + "hash": "0x", + "requestMethod": "rng" + } + } +} \ No newline at end of file diff --git a/migrations/jsons/witnet-radon-retrievals.json b/migrations/jsons/witnet-radon-retrievals.json new file mode 100644 index 000000000..1f00e1480 --- /dev/null +++ b/migrations/jsons/witnet-radon-retrievals.json @@ -0,0 +1,22 @@ +{ + "Price-BTC/USD-6": { + "hash": "0x", + "id4": "0x", + "sources": [ + [ "api.binance.us#ticker", [ "BTC", "USD" ] ], + [ "api.bitfinex.com#ticker", [ "btc", "usd" ] ], + [ "api.bittrex.com#ticker", [ "BTC", "USD" ] ], + [ "api.coinbase.com#exchange-rates", [ "BTC", "USD" ] ], + [ "api.kraken.com#ticker", [ "BTC", "USD" ] ], + [ "www.bitstamp.net#ticker", [ "btc", "usd" ] ] + ], + "aggregator": "avg_stdev_15", + "tally": "avg_stdev_25" + }, + "Randomness": { + "hash": "0x", + "sources": [ "witnet.eth#randomness" ], + "aggregator": "Concat-And-Hash", + "tally": "Concat-And-Hash" + } +} \ No newline at end of file diff --git a/migrations/jsons/witnet-radon-slas.json b/migrations/jsons/witnet-radon-slas.json new file mode 100644 index 000000000..9e44e586b --- /dev/null +++ b/migrations/jsons/witnet-radon-slas.json @@ -0,0 +1,10 @@ +{ + "Vanilla": { + "hash": "0x", + "witnessReward": 1000000, + "numWitnesses": 10, + "commitRevealFee": 1000000, + "minConsensusPercentage": 51, + "collateral": 5000000000 + } +} \ No newline at end of file From 479ab9282f997d0200bd7c31b55bb76642471797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 1 Dec 2022 12:04:58 +0100 Subject: [PATCH 016/119] test(libs): add tests for WitnetBuffer.replace --- test/TestWitnetBuffer.sol | 56 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/test/TestWitnetBuffer.sol b/test/TestWitnetBuffer.sol index e8c5e3d54..f3d3cc25d 100644 --- a/test/TestWitnetBuffer.sol +++ b/test/TestWitnetBuffer.sol @@ -13,6 +13,62 @@ contract TestWitnetBuffer { event Log(string _topic, uint256 _value); + function testReplace0Args() external { + string memory input = "In a village of La Mancha, the name of which I have no desire to call to mind, there lived not long since one of those gentlemen that keep a lance in the lance-rack, an old buckler, a lean hack, and a greyhound for coursing"; + string[] memory args = new string[](1); + args[0] = "Don Quixote"; + bytes memory phrase = WitnetBuffer.replace(bytes(input), args); + emit Log(string(phrase), phrase.length); + Assert.equal( + keccak256(phrase), + keccak256(bytes(input)), + "String replacement not good :/" + ); + } + + function testReplace1Args() external { + string memory input = "\\0\\"; + string[] memory args = new string[](1); + args[0] = "Hello!"; + bytes memory phrase = WitnetBuffer.replace(bytes(input), args); + emit Log(string(phrase), phrase.length); + Assert.equal( + keccak256(phrase), + keccak256(bytes("Hello!")), + "String replacement not good :/" + ); + } + + function testReplace4Args() external { + string memory input = "Test: \\0\\ \\1\\ \\2\\!"; + string[] memory args = new string[](3); + args[0] = "Hello"; + args[1] = "decentralized"; + args[2] = "world"; + bytes memory phrase = WitnetBuffer.replace(bytes(input), args); + emit Log(string(phrase), phrase.length); + Assert.equal( + keccak256(phrase), + keccak256(bytes("Test: Hello decentralized world!")), + "String replacement not good :/" + ); + } + + function testReplace4ArgsUnordered() external { + string memory input = "Test: \\2\\ \\0\\ \\3\\!"; + string[] memory args = new string[](4); + args[2] = "Hello"; + args[0] = "decentralized"; + args[3] = "world"; + bytes memory phrase = WitnetBuffer.replace(bytes(input), args); + emit Log(string(phrase), phrase.length); + Assert.equal( + keccak256(phrase), + keccak256(bytes("Test: Hello decentralized world!")), + "String replacement not good :/" + ); + } + function testRead31bytes() external { bytes memory data = hex"58207eadcf3ba9a9a860b4421ee18caa6dca4738fef266aa7b3668a2ff97304cfcab"; WitnetBuffer.Buffer memory buf = WitnetBuffer.Buffer(data, 1); From 44ead2dd36aae3d9d4647e3e0ef110ca58d652b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 1 Dec 2022 12:07:53 +0100 Subject: [PATCH 017/119] test(libs): add tests for WitnetBuffer.concat --- test/TestWitnetBuffer.sol | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/TestWitnetBuffer.sol b/test/TestWitnetBuffer.sol index f3d3cc25d..3bb35b258 100644 --- a/test/TestWitnetBuffer.sol +++ b/test/TestWitnetBuffer.sol @@ -13,6 +13,37 @@ contract TestWitnetBuffer { event Log(string _topic, uint256 _value); + + function testConcatShortStrings() external { + bytes[] memory strs = new bytes[](4); + strs[0] = bytes("Hello "); + strs[1] = bytes("decentralized "); + strs[2] = bytes("world"); + strs[3] = bytes("!"); + bytes memory phrase = WitnetBuffer.concat(strs); + emit Log(string(phrase), phrase.length); + Assert.equal( + keccak256(phrase), + keccak256(bytes("Hello decentralized world!")), + "Concat of strings not good :/" + ); + } + + function testConcatLongStrings() external { + bytes[] memory strs = new bytes[](4); + strs[0] = bytes("0123456789012345678901234567890123456789012345678901234567890123"); + strs[1] = bytes("01234567890123456789012345678901234567890123456789012345678901234"); + strs[2] = bytes("012345678901234567890123456789012345678901234567890123456789012345"); + strs[3] = bytes("0123456789012345678901234567890123456789012345678901234567890123456"); + bytes memory phrase = WitnetBuffer.concat(strs); + emit Log(string(phrase), phrase.length); + Assert.equal( + keccak256(phrase), + keccak256(bytes("0123456789012345678901234567890123456789012345678901234567890123012345678901234567890123456789012345678901234567890123456789012340123456789012345678901234567890123456789012345678901234567890123450123456789012345678901234567890123456789012345678901234567890123456")), + "Concat of strings not good :/" + ); + } + function testReplace0Args() external { string memory input = "In a village of La Mancha, the name of which I have no desire to call to mind, there lived not long since one of those gentlemen that keep a lance in the lance-rack, an old buckler, a lean hack, and a greyhound for coursing"; string[] memory args = new string[](1); From 89e1cb71f874482647e883af1e5de86abb420ff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 1 Dec 2022 12:08:11 +0100 Subject: [PATCH 018/119] test(libs): add test for WitnetBuffer.mutate --- test/TestWitnetBuffer.sol | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/TestWitnetBuffer.sol b/test/TestWitnetBuffer.sol index 3bb35b258..ac0d1e8bc 100644 --- a/test/TestWitnetBuffer.sol +++ b/test/TestWitnetBuffer.sol @@ -13,6 +13,20 @@ contract TestWitnetBuffer { event Log(string _topic, uint256 _value); + function testMutate() external { + WitnetBuffer.Buffer memory buff = WitnetBuffer.Buffer( + hex"00000000000000000000000000000000000000000000635C305C0102030405060708090a", + 23 + ); + emit Log(string(buff.data), buff.data.length); + buff.mutate(3, bytes("token1Price")); + emit Log(string(buff.data), buff.data.length); + Assert.equal( + keccak256(buff.data), + keccak256(hex"0000000000000000000000000000000000000000000063746F6B656E3150726963650102030405060708090a"), + "Wildcards replacement not good :/" + ); + } function testConcatShortStrings() external { bytes[] memory strs = new bytes[](4); From a11e68ff31ddbc22a54ed566b29d5c9bc58a9554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 1 Dec 2022 12:08:51 +0100 Subject: [PATCH 019/119] test(libs): add test for WitnetBuffer.fork --- test/TestWitnetBuffer.sol | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/TestWitnetBuffer.sol b/test/TestWitnetBuffer.sol index ac0d1e8bc..030e87d95 100644 --- a/test/TestWitnetBuffer.sol +++ b/test/TestWitnetBuffer.sol @@ -13,6 +13,26 @@ contract TestWitnetBuffer { event Log(string _topic, uint256 _value); + function testFork() external { + WitnetBuffer.Buffer memory buff = WitnetBuffer.Buffer( + hex"00000000000000000000000000000000000000000000635C305C0102030405060708090a", + 0 + ); + WitnetBuffer.Buffer memory fork = buff.fork(); + buff.data[0] = 0xff; + fork.next(); + Assert.notEqual( + buff.cursor, + fork.cursor, + "not forked :/" + ); + Assert.equal( + uint(uint8(buff.data[0])), + uint(uint8(fork.data[0])), + "bad fork :/" + ); + } + function testMutate() external { WitnetBuffer.Buffer memory buff = WitnetBuffer.Buffer( hex"00000000000000000000000000000000000000000000635C305C0102030405060708090a", From 8ff50d0ffc371754804eac566764fe38ccf58bb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 1 Dec 2022 12:09:48 +0100 Subject: [PATCH 020/119] fix(lib): fix and optimize gas consumptions in WitnetBuffer --- contracts/libs/WitnetBuffer.sol | 451 ++++++++++++++++++-------------- 1 file changed, 256 insertions(+), 195 deletions(-) diff --git a/contracts/libs/WitnetBuffer.sol b/contracts/libs/WitnetBuffer.sol index f1db05d15..6aee21d9c 100644 --- a/contracts/libs/WitnetBuffer.sol +++ b/contracts/libs/WitnetBuffer.sol @@ -12,6 +12,7 @@ library WitnetBuffer { error EmptyBuffer(); error IndexOutOfBounds(uint index, uint range); + error MissingArgs(uint index, string[] args); /// Iterable bytes buffer. struct Buffer { @@ -27,88 +28,115 @@ library WitnetBuffer { _; } - /// @notice Read and consume a certain amount of bytes from the buffer. - /// @param _buffer An instance of `Buffer`. - /// @param _length How many bytes to read and consume from the buffer. - /// @return _output A `bytes memory` containing the first `_length` bytes from the buffer, counting from the cursor position. - function read(Buffer memory _buffer, uint _length) + /// @notice Concatenate undefinite number of bytes chunks. + /// @dev Faster than looping on `abi.encodePacked(_output, _buffs[_ix])`. + function concat(bytes[] memory _buffs) internal pure - withinRange(_buffer.cursor + _length, _buffer.data.length + 1) returns (bytes memory _output) { - // Create a new `bytes memory destination` value - _output = new bytes(_length); - // Early return in case that bytes length is 0 - if (_length > 0) { - bytes memory _input = _buffer.data; - uint _offset = _buffer.cursor; - // Get raw pointers for source and destination - uint _sourcePointer; + unchecked { uint _destinationPointer; + uint _destinationLength; assembly { - _sourcePointer := add(add(_input, 32), _offset) + // get safe scratch location + _output := mload(0x40) + // set starting destination pointer _destinationPointer := add(_output, 32) + } + for (uint _ix = 1; _ix <= _buffs.length; _ix ++) { + uint _source; + uint _sourceLength; + uint _sourcePointer; + assembly { + // load source length pointer + _source := mload(add(_buffs, mul(_ix, 32))) + // load source length + _sourceLength := mload(_source) + // sets source memory pointer + _sourcePointer := add(_source, 32) + } + _memcpy( + _destinationPointer, + _sourcePointer, + _sourceLength + ); + assembly { + // increase total destination length + _destinationLength := add(_destinationLength, _sourceLength) + // sets destination memory pointer + _destinationPointer := add(_destinationPointer, _sourceLength) + } + } + assembly { + // protect output bytes + mstore(_output, _destinationLength) + // set final output length + mstore(0x40, add(mload(0x40), add(_destinationLength, 32))) } - // Copy `_length` bytes from source to destination - memcpy( - _destinationPointer, - _sourcePointer, - _length - ); - // Move the cursor forward by `_length` bytes - seek( - _buffer, - _length, - true - ); } } - /// @notice Read and consume the next byte from the buffer. - /// @param _buffer An instance of `Buffer`. - /// @return The next byte in the buffer counting from the cursor position. - function next(Buffer memory _buffer) + function fork(WitnetBuffer.Buffer memory _buffer) internal pure - withinRange(_buffer.cursor, _buffer.data.length) - returns (bytes1) + returns (WitnetBuffer.Buffer memory _forked) { - // Return the byte at the position marked by the cursor and advance the cursor all at once - return _buffer.data[_buffer.cursor ++]; + _forked.data = _buffer.data; + _forked.cursor = _buffer.cursor; } function mutate( WitnetBuffer.Buffer memory _buffer, - uint _length, - bytes memory _genes + uint _peekLength, + bytes memory _pokeBytes ) internal pure - withinRange(_length, _buffer.data.length - _buffer.cursor) + withinRange(_peekLength, _buffer.data.length - _buffer.cursor) { - // TODO + bytes[] memory _parts = new bytes[](3); + _parts[0] = peek( + _buffer, + 0, + _buffer.cursor + ); + _parts[1] = _pokeBytes; + _parts[2] = peek( + _buffer, + _buffer.cursor + _peekLength, + _buffer.data.length - _buffer.cursor - _peekLength + ); + _buffer.data = concat(_parts); } - // @notice Extract bytes array from buffer starting from current cursor. + /// @notice Read and consume the next byte from the buffer. /// @param _buffer An instance of `Buffer`. - /// @param _length How many bytes to peek from the Buffer. - // solium-disable-next-line security/no-assign-params + /// @return The next byte in the buffer counting from the cursor position. + function next(Buffer memory _buffer) + internal pure + withinRange(_buffer.cursor, _buffer.data.length) + returns (bytes1) + { + // Return the byte at the position marked by the cursor and advance the cursor all at once + return _buffer.data[_buffer.cursor ++]; + } + function peek( WitnetBuffer.Buffer memory _buffer, + uint _offset, uint _length ) internal pure - withinRange(_length, _buffer.data.length - _buffer.cursor) + withinRange(_offset + _length, _buffer.data.length + 1) returns (bytes memory) { bytes memory _data = _buffer.data; bytes memory _peek = new bytes(_length); - uint _offset = _buffer.cursor; uint _destinationPointer; uint _sourcePointer; assembly { _destinationPointer := add(_peek, 32) _sourcePointer := add(add(_data, 32), _offset) } - memcpy( + _memcpy( _destinationPointer, _sourcePointer, _length @@ -116,55 +144,60 @@ library WitnetBuffer { return _peek; } - /// @notice Move the inner cursor of the buffer to a relative or absolute position. + // @notice Extract bytes array from buffer starting from current cursor. /// @param _buffer An instance of `Buffer`. - /// @param _offset How many bytes to move the cursor forward. - /// @param _relative Whether to count `_offset` from the last position of the cursor (`true`) or the beginning of the - /// buffer (`true`). - /// @return The final position of the cursor (will equal `_offset` if `_relative` is `false`). + /// @param _length How many bytes to peek from the Buffer. // solium-disable-next-line security/no-assign-params - function seek( - Buffer memory _buffer, - uint _offset, - bool _relative - ) - internal pure - withinRange(_offset, _buffer.data.length + 1) - returns (uint) - { - // Deal with relative offsets - if (_relative) { - _offset += _buffer.cursor; - } - _buffer.cursor = _offset; - return _offset; - } - - /// @notice Move the inner cursor a number of bytes forward. - /// @dev This is a simple wrapper around the relative offset case of `seek()`. - /// @param _buffer An instance of `Buffer`. - /// @param _relativeOffset How many bytes to move the cursor forward. - /// @return The final position of the cursor. - function seek( - Buffer memory _buffer, - uint _relativeOffset + function peek( + WitnetBuffer.Buffer memory _buffer, + uint _length ) internal pure - returns (uint) + withinRange(_length, _buffer.data.length - _buffer.cursor) + returns (bytes memory) { - return seek( + return peek( _buffer, - _relativeOffset, - true + _buffer.cursor, + _length ); } - /// @notice Move the inner cursor back to the first byte in the buffer. + /// @notice Read and consume a certain amount of bytes from the buffer. /// @param _buffer An instance of `Buffer`. - function rewind(Buffer memory _buffer) + /// @param _length How many bytes to read and consume from the buffer. + /// @return _output A `bytes memory` containing the first `_length` bytes from the buffer, counting from the cursor position. + function read(Buffer memory _buffer, uint _length) internal pure + withinRange(_buffer.cursor + _length, _buffer.data.length + 1) + returns (bytes memory _output) { - _buffer.cursor = 0; + // Create a new `bytes memory destination` value + _output = new bytes(_length); + // Early return in case that bytes length is 0 + if (_length > 0) { + bytes memory _input = _buffer.data; + uint _offset = _buffer.cursor; + // Get raw pointers for source and destination + uint _sourcePointer; + uint _destinationPointer; + assembly { + _sourcePointer := add(add(_input, 32), _offset) + _destinationPointer := add(_output, 32) + } + // Copy `_length` bytes from source to destination + _memcpy( + _destinationPointer, + _sourcePointer, + _length + ); + // Move the cursor forward by `_length` bytes + seek( + _buffer, + _length, + true + ); + } } /// @notice Read and consume the next 2 bytes from the buffer as an IEEE 754-2008 floating point number enclosed in an @@ -347,141 +380,169 @@ library WitnetBuffer { _buffer.cursor += 32; } - /// @notice Copy bytes from one memory address into another. - /// @dev This function was borrowed from Nick Johnson's `solidity-stringutils` lib, and reproduced here under the terms - /// of [Apache License 2.0](https://github.com/Arachnid/solidity-stringutils/blob/master/LICENSE). - /// @param _dest Address of the destination memory. - /// @param _src Address to the source memory. - /// @param _len How many bytes to copy. - // solium-disable-next-line security/no-assign-params - function memcpy( - uint _dest, - uint _src, - uint _len - ) + /// @notice Replace bytecode indexed wildcards by correspondent string. + /// @dev Wildcard format: "\#\", with # in ["0".."9"]. + /// @param _input Bytes array containing strings. + /// @param _args String values for replacing existing indexed wildcards in _input. + function replace(bytes memory _input, string[] memory _args) internal pure + returns (bytes memory _output) { - // Copy word-length chunks while possible - for (; _len >= 32; _len -= 32) { - assembly { - mstore(_dest, mload(_src)) + uint _ix = 0; uint _lix = 0; + uint _inputLength; + uint _inputPointer; + uint _outputLength; + uint _outputPointer; + uint _source; + uint _sourceLength; + uint _sourcePointer; + + assembly { + // set starting input pointer + _inputPointer := add(_input, 32) + // get safe output location + _output := mload(0x40) + // set starting output pointer + _outputPointer := add(_output, 32) + } + + 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") + ) { + if (_ix > _lix) { + _inputLength = (_ix - _lix); + _memcpy( + _outputPointer, + _inputPointer, + _inputLength + ); + _inputPointer += _inputLength + 3; + _outputPointer += _inputLength; + } + uint _ax = uint(uint8(_input[_ix + 1]) - uint8(bytes1("0"))); + if (_ax >= _args.length) { + revert MissingArgs(_ax, _args); + } + assembly { + _source := mload(add(_args, mul(32, add(_ax, 1)))) + _sourceLength := mload(_source) + _sourcePointer := add(_source, 32) + } + _memcpy( + _outputPointer, + _sourcePointer, + _sourceLength + ); + _outputLength += _inputLength + _sourceLength; + _outputPointer += _sourceLength; + _ix += 3; + _lix = _ix; + } else { + _ix ++; + } } - _dest += 32; - _src += 32; + _ix = _input.length; } - if (_len > 0) { - // Copy remaining bytes - uint _mask = 256 ** (32 - _len) - 1; + if (_outputLength > 0) { + if (_ix > _lix ) { + _memcpy( + _outputPointer, + _inputPointer, + _ix - _lix + ); + _outputLength += (_ix - _lix); + } assembly { - let _srcpart := and(mload(_src), not(_mask)) - let _destpart := and(mload(_dest), _mask) - mstore(_dest, or(_destpart, _srcpart)) + // set final output length + mstore(_output, _outputLength) + // protect output bytes + mstore(0x40, add(mload(0x40), add(_outputLength, 32))) } } + else { + return _input; + } } - function concat(bytes[] memory _buffs) + /// @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. + /// @param _relative Whether to count `_offset` from the last position of the cursor (`true`) or the beginning of the + /// buffer (`true`). + /// @return The final position of the cursor (will equal `_offset` if `_relative` is `false`). + // solium-disable-next-line security/no-assign-params + function seek( + Buffer memory _buffer, + uint _offset, + bool _relative + ) internal pure - returns (bytes memory _output) + withinRange(_offset, _buffer.data.length + 1) + returns (uint) { - unchecked { - uint _ix; - uint _destinationPointer; - assembly { - _destinationPointer := add(_output, 32) - } - while (_ix < _buffs.length) { - bytes memory _source = _buffs[_ix]; - uint _sourceLength = _source.length; - uint _sourcePointer; - assembly { - // sets source memory pointer - _sourcePointer := add(_source, 32) - } - memcpy( - _destinationPointer, - _sourcePointer, - _sourceLength - ); - assembly { - // increase _output size - mstore(_output, add(mload(_output), _sourceLength)) - // sets destination memory pointer - _destinationPointer := add(_destinationPointer, _sourceLength) - } - _ix ++; - } + // Deal with relative offsets + if (_relative) { + _offset += _buffer.cursor; } + _buffer.cursor = _offset; + return _offset; } - function replaceWildcards( - WitnetBuffer.Buffer memory _buffer, - uint _length, - string[] memory _args + /// @notice Move the inner cursor a number of bytes forward. + /// @dev This is a simple wrapper around the relative offset case of `seek()`. + /// @param _buffer An instance of `Buffer`. + /// @param _relativeOffset How many bytes to move the cursor forward. + /// @return The final position of the cursor. + function seek( + Buffer memory _buffer, + uint _relativeOffset ) internal pure + returns (uint) { - bytes memory _peek = replaceWildcards( - peek(_buffer, _length), - _args + return seek( + _buffer, + _relativeOffset, + true ); - if (_peek.length != _length) { - mutate(_buffer, _length, _peek); - } } - /// @notice Replace bytecode indexed wildcards by correspondent string. - /// @dev Wildcard format: "\#\", with # in ["0".."9"]. - /// @param _input Bytes array containing strings. - /// @param _args String values for replacing existing indexed wildcards in _input. - function replaceWildcards(bytes memory _input, string[] memory _args) - internal pure - returns (bytes memory _output) + /// @notice Copy bytes from one memory address into another. + /// @dev This function was borrowed from Nick Johnson's `solidity-stringutils` lib, and reproduced here under the terms + /// of [Apache License 2.0](https://github.com/Arachnid/solidity-stringutils/blob/master/LICENSE). + /// @param _dest Address of the destination memory. + /// @param _src Address to the source memory. + /// @param _len How many bytes to copy. + // solium-disable-next-line security/no-assign-params + function _memcpy( + uint _dest, + uint _src, + uint _len + ) + private pure { - _output = _input; - if (_input.length >= 3) { - uint _outputLength = _input.length; - unchecked { - // scan for wildcards as to calculate output length: - for (uint _ix = 0; _ix < _input.length - 2; ) { - if (_input[_ix] == bytes1("\\")) { - if (_input[_ix + 2] == bytes1("\\")) { - if (_input[_ix + 1] >= bytes1("0") && _input[_ix + 1] <= bytes1("9")) { - uint _argIndex = uint(uint8(_input[_ix + 1]) - uint8(bytes1("0"))); - assert(_args.length >= uint(_argIndex) + 1); - _outputLength += bytes(_args[_argIndex]).length - 3; - _ix += 3; - } else { - _ix += 2; - } - continue; - } else { - _ix += 3; - continue; - } - } else { - _ix ++; - } + unchecked { + // Copy word-length chunks while possible + for (; _len >= 32; _len -= 32) { + assembly { + mstore(_dest, mload(_src)) } - // if wildcards found: - if (_outputLength > _input.length) { - _output = new bytes(_outputLength); - // Replace indexed wildcards: - for (uint _ix = 0; _ix < _input.length - 2; ) { - if (_input[_ix] == bytes1("\\")) { - if (_input[_ix + 2] == bytes1("\\")) { - if (_input[_ix + 1] >= bytes1("0") && _input[_ix + 1] <= bytes1("9")) { - uint _argIndex = uint(uint8(_input[_ix + 1]) - uint8(bytes1("0"))); - for (uint _ax = 0; _ax < bytes(_args[_argIndex]).length; _ax ++) { - _output[_ix + _ax] = bytes(_args[_argIndex])[_ax]; - } - _ix += 3; - continue; - } - } - } - _output[_ix] = _input[_ix ++]; - } + _dest += 32; + _src += 32; + } + if (_len > 0) { + // Copy remaining bytes + uint _mask = 256 ** (32 - _len) - 1; + assembly { + let _srcpart := and(mload(_src), not(_mask)) + let _destpart := and(mload(_dest), _mask) + mstore(_dest, or(_destpart, _srcpart)) } } } From 3e0b768f5499afb523a13773b1fab6f53fa1c334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 1 Dec 2022 12:12:19 +0100 Subject: [PATCH 021/119] test(libs): add test for WitnetLib.rplaceCborStringsFromBytes --- test/TestWitnetLib.sol | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/TestWitnetLib.sol b/test/TestWitnetLib.sol index a3b609de8..1290462c3 100644 --- a/test/TestWitnetLib.sol +++ b/test/TestWitnetLib.sol @@ -10,6 +10,22 @@ contract TestWitnetLib { using WitnetLib for Witnet.Result; + event Log(bytes data, uint256 length); + + function testReplaceCborStringsFromBytes() external { + bytes memory radon = hex"861877821866646461746182186664706F6F6C821864635C305C8218571A000F4240185B"; + emit Log(radon, radon.length); + string[] memory args = new string[](1); + args[0] = "token1Price"; + WitnetCBOR.CBOR memory cbor = WitnetLib.replaceCborStringsFromBytes(radon, args); + emit Log(cbor.buffer.data, cbor.buffer.data.length); + Assert.equal( + keccak256(cbor.buffer.data), + keccak256(hex'861877821866646461746182186664706F6F6C8218646B746F6B656E3150726963658218571A000F4240185B'), + "not good :/" + ); + } + // Test decoding of `RadonError` error codes function testErrorCodes1() external { Witnet.ErrorCodes errorCodeEmpty = WitnetLib.resultFromCborBytes(hex"D82780").asErrorCode(); From 547f0dc91a937bab7a33ca03962e1cd4e4e3b270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 1 Dec 2022 12:18:34 +0100 Subject: [PATCH 022/119] fix(libs): fix, refactor and and optimize WitnetLib.replaceCborStringsFromBytes --- contracts/data/WitnetBytecodesData.sol | 7 +- contracts/impls/bytecodes/WitnetBytecodes.sol | 10 +- contracts/libs/WitnetCBOR.sol | 415 +++++++++++------- contracts/libs/WitnetLib.sol | 111 ++++- 4 files changed, 358 insertions(+), 185 deletions(-) diff --git a/contracts/data/WitnetBytecodesData.sol b/contracts/data/WitnetBytecodesData.sol index d6cdf834b..7565e9060 100644 --- a/contracts/data/WitnetBytecodesData.sol +++ b/contracts/data/WitnetBytecodesData.sol @@ -17,7 +17,7 @@ abstract contract WitnetBytecodesData /* keccak256("io.witnet.bytecodes.data") */ 0x673359bdfd0124f9962355e7aed2d07d989b0d4bc4cbe2c94c295e0f81427dec; - bytes internal constant _WITNET_BYTECODES_RADON_OPCODES_RESULT_TYPE = + bytes internal constant _WITNET_BYTECODES_RADON_OPCODES_RESULT_TYPES = hex"00ffffffffffffffffffffffffffffff0401ff010203050406070101ff01ffff07ff02ffffffffffffffffffffffffff0703ffffffffffffffffffffffffffff05070404020205050505ff04ff04ffff0405070202ff04040404ffffffffffff010203050406070101ffffffffffffff02ff050404000106060707ffffffffff"; struct Bytecodes { @@ -38,7 +38,6 @@ abstract contract WitnetBytecodesData mapping (bytes32 => bytes) reducersBytecode; mapping (bytes32 => bytes) retrievalsBytecode; mapping (bytes32 => bytes) slasBytecode; - // mapping (bytes32 => bytes) sourceBytecodes; mapping (bytes32 => IWitnetBytecodes.RadonRetrieval) retrievals; mapping (bytes32 => WitnetV2.RadonReducer) reducers; @@ -50,11 +49,11 @@ abstract contract WitnetBytecodesData internal pure returns (WitnetV2.RadonDataTypes) { - if (_opcode >= _WITNET_BYTECODES_RADON_OPCODES_RESULT_TYPE.length) { + if (_opcode >= _WITNET_BYTECODES_RADON_OPCODES_RESULT_TYPES.length) { revert IWitnetBytecodes.UnsupportedRadonScriptOpcode(_opcode); } else { uint8 _resultType = uint8( - _WITNET_BYTECODES_RADON_OPCODES_RESULT_TYPE[_opcode] + _WITNET_BYTECODES_RADON_OPCODES_RESULT_TYPES[_opcode] ); if (_resultType == 0xff) { revert IWitnetBytecodes.UnsupportedRadonScriptOpcode(_opcode); diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol index 4695b4f13..91461fe1e 100644 --- a/contracts/impls/bytecodes/WitnetBytecodes.sol +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -498,9 +498,9 @@ contract WitnetBytecodes virtual returns (WitnetV2.DataSource memory) { - _ds.url = string(WitnetBuffer.replaceWildcards(bytes(_ds.url), _args)); - _ds.body = string(WitnetBuffer.replaceWildcards(bytes(_ds.url), _args)); - WitnetCBOR.CBOR memory _cborScript = WitnetLib.replaceCborBytesWildcards(_ds.script, _args); + _ds.url = string(WitnetBuffer.replace(bytes(_ds.url), _args)); + _ds.body = string(WitnetBuffer.replace(bytes(_ds.url), _args)); + WitnetCBOR.CBOR memory _cborScript = WitnetLib.replaceCborStringsFromBytes(_ds.script, _args); _ds.script = _cborScript.buffer.data; return _ds; } @@ -589,9 +589,9 @@ contract WitnetBytecodes { if (_cbor.majorType == 4) { WitnetCBOR.CBOR[] memory _items = _cbor.readArray(); - if (_items.length > 0) { + if (_items.length > 1) { return _verifyDataSourceCborScript( - _items[_items.length - 1] + _items[_items.length - 2] ); } else { return WitnetV2.RadonDataTypes.Any; diff --git a/contracts/libs/WitnetCBOR.sol b/contracts/libs/WitnetCBOR.sol index de4ab04f5..c5d41dab3 100644 --- a/contracts/libs/WitnetCBOR.sol +++ b/contracts/libs/WitnetCBOR.sol @@ -17,17 +17,29 @@ import "./WitnetBuffer.sol"; library WitnetCBOR { using WitnetBuffer for WitnetBuffer.Buffer; + using WitnetCBOR for WitnetCBOR.CBOR; - enum MajorTypes { - /* 0 */ Uint, - /* 1 */ Int, - /* 2 */ Bytes, - /* 3 */ String, - /* 4 */ Array, - /* 5 */ Map, - /* 6 */ Tag, - /* 7 */ Primitive + /// Data struct following the RFC-7049 standard: Concise Binary Object Representation. + struct CBOR { + WitnetBuffer.Buffer buffer; + uint8 initialByte; + uint8 majorType; + uint8 additionalInformation; + uint64 len; + uint64 tag; } + + uint8 internal constant MAJOR_TYPE_INT = 0; + uint8 internal constant MAJOR_TYPE_NEGATIVE_INT = 1; + uint8 internal constant MAJOR_TYPE_BYTES = 2; + uint8 internal constant MAJOR_TYPE_STRING = 3; + uint8 internal constant MAJOR_TYPE_ARRAY = 4; + uint8 internal constant MAJOR_TYPE_MAP = 5; + uint8 internal constant MAJOR_TYPE_TAG = 6; + uint8 internal constant MAJOR_TYPE_CONTENT_FREE = 7; + + uint32 internal constant UINT32_MAX = type(uint32).max; + uint64 internal constant UINT64_MAX = type(uint64).max; error EmptyArray(); error InvalidLengthEncoding(uint length); @@ -37,10 +49,10 @@ library WitnetCBOR { modifier isMajorType( WitnetCBOR.CBOR memory _cbor, - MajorTypes _expected + uint8 _expected ) { - if (_cbor.majorType != uint(_expected)) { - revert UnexpectedMajorType(_cbor.majorType, uint(_expected)); + if (_cbor.majorType != _expected) { + revert UnexpectedMajorType(_cbor.majorType, _expected); } _; } @@ -52,19 +64,13 @@ library WitnetCBOR { _; } - /// Data struct following the RFC-7049 standard: Concise Binary Object Representation. - struct CBOR { - WitnetBuffer.Buffer buffer; - uint8 initialByte; - uint8 majorType; - uint8 additionalInformation; - uint64 len; - uint64 tag; + function eof(CBOR memory cbor) + internal pure + returns (bool) + { + return cbor.buffer.cursor >= cbor.buffer.data.length; } - uint32 constant internal _UINT32_MAX = type(uint32).max; - uint64 constant internal _UINT64_MAX = type(uint64).max; - /// @notice Decode a CBOR structure from raw bytes. /// @dev This is the main factory for CBOR instances, which can be later decoded into native EVM types. /// @param _cborBytes Raw bytes representing a CBOR-encoded value. @@ -74,37 +80,41 @@ library WitnetCBOR { returns (CBOR memory) { WitnetBuffer.Buffer memory buffer = WitnetBuffer.Buffer(_cborBytes, 0); - return _valueFromBuffer(buffer); + return valueFromBuffer(buffer); } /// @notice Decode a CBOR structure from raw bytes. /// @dev This is an alternate factory for CBOR instances, which can be later decoded into native EVM types. /// @param _buffer A Buffer structure representing a CBOR-encoded value. /// @return A `CBOR` instance containing a partially decoded value. - function _valueFromBuffer(WitnetBuffer.Buffer memory _buffer) - private pure + function valueFromBuffer(WitnetBuffer.Buffer memory _buffer) + internal pure notEmpty(_buffer) returns (CBOR memory) { uint8 _initialByte; uint8 _majorType = 255; uint8 _additionalInformation; - uint64 _tag = _UINT64_MAX; + uint64 _tag = UINT64_MAX; + uint256 _len; bool _isTagged = true; while (_isTagged) { // Extract basic CBOR properties from input bytes _initialByte = _buffer.readUint8(); + _len ++; _majorType = _initialByte >> 5; _additionalInformation = _initialByte & 0x1f; // Early CBOR tag parsing. - if (_majorType == 6) { - _tag = _readLength(_buffer, _additionalInformation); + if (_majorType == MAJOR_TYPE_TAG) { + uint _cursor = _buffer.cursor; + _tag = readLength(_buffer, _additionalInformation); + _len += _buffer.cursor - _cursor; } else { _isTagged = false; } } - if (_majorType > 7) { + if (_majorType > MAJOR_TYPE_CONTENT_FREE) { revert UnsupportedMajorType(_majorType); } return CBOR( @@ -112,11 +122,55 @@ library WitnetCBOR { _initialByte, _majorType, _additionalInformation, - 0, + uint64(_len), _tag ); } + /// @notice Decode a CBOR structure from raw bytes. + /// @dev This is an alternate factory for CBOR instances, which can be later decoded into native EVM types. + /// @param _buffer A Buffer structure representing a CBOR-encoded value. + /// @return _value A `CBOR` instance containing a partially decoded value. + function _valueFromForkedBuffer(WitnetBuffer.Buffer memory _buffer) + private pure + notEmpty(_buffer) + returns (CBOR memory _value) + { + uint8 _initialByte; + uint8 _majorType = 255; + uint8 _additionalInformation; + uint64 _tag = UINT64_MAX; + uint256 _len = 0; + + WitnetBuffer.Buffer memory _newBuffer = _buffer.fork(); + bool _isTagged = true; + while (_isTagged) { + // Extract basic CBOR properties from input bytes + _initialByte = _newBuffer.readUint8(); + _len ++; + _majorType = _initialByte >> 5; + _additionalInformation = _initialByte & 0x1f; + // Early CBOR tag parsing. + if (_majorType == MAJOR_TYPE_TAG) { + uint _cursor = _newBuffer.cursor; + _tag = readLength(_newBuffer, _additionalInformation); + _len += _newBuffer.cursor - _cursor; + + } else { + _isTagged = false; + } + } + if (_majorType > MAJOR_TYPE_CONTENT_FREE) { + revert UnsupportedMajorType(_majorType); + } + _value.buffer = _newBuffer; + _value.initialByte = _initialByte; + _value.majorType = _majorType; + _value.additionalInformation = _additionalInformation; + _value.len = uint64(_len); + _value.tag = _tag; + } + /// Read the length of a CBOR indifinite-length item (arrays, maps, byte strings and text) from a buffer, consuming /// as many bytes as specified by the first byte. function _readIndefiniteStringLength( @@ -128,90 +182,59 @@ library WitnetCBOR { { uint8 _initialByte = _buffer.readUint8(); if (_initialByte == 0xff) { - return _UINT64_MAX; + return UINT64_MAX; } - _length = _readLength( + _length = readLength( _buffer, _initialByte & 0x1f ); - if (_length >= _UINT64_MAX) { + if (_length >= UINT64_MAX) { revert InvalidLengthEncoding(_length); } else if (_majorType != (_initialByte >> 5)) { revert UnexpectedMajorType((_initialByte >> 5), _majorType); } } - /// Reads the length of the next CBOR item from a buffer, consuming a different number of bytes depending on the - /// value of the `additionalInformation` argument. - function _readLength( - WitnetBuffer.Buffer memory _buffer, - uint8 _additionalInformation - ) - private pure - returns (uint64) - { - if (_additionalInformation < 24) { - return _additionalInformation; - } - if (_additionalInformation == 24) { - return _buffer.readUint8(); - } - if (_additionalInformation == 25) { - return _buffer.readUint16(); - } - if (_additionalInformation == 26) { - return _buffer.readUint32(); - } - if (_additionalInformation == 27) { - return _buffer.readUint64(); - } - if (_additionalInformation == 31) { - return _UINT64_MAX; - } - revert InvalidLengthEncoding(_additionalInformation); - } - function _seekNext(WitnetCBOR.CBOR memory _cbor) private pure returns (WitnetCBOR.CBOR memory) { - if (_cbor.majorType == 0 || _cbor.majorType == 1) { - return _skipInt(_cbor); - } else if (_cbor.majorType == 2) { - return _skipBytes(_cbor); - } else if (_cbor.majorType == 3) { - return _skipText(_cbor); - } else if (_cbor.majorType == 4) { - return _skipArray(_cbor); - } else if (_cbor.majorType == 7) { - return _skipPrimitive(_cbor); + if (_cbor.majorType == MAJOR_TYPE_INT || _cbor.majorType == MAJOR_TYPE_NEGATIVE_INT) { + readInt(_cbor); + } else if (_cbor.majorType == MAJOR_TYPE_BYTES) { + readBytes(_cbor); + } else if (_cbor.majorType == MAJOR_TYPE_STRING) { + readString(_cbor); + } else if (_cbor.majorType == MAJOR_TYPE_ARRAY) { + CBOR[] memory _items = readArray(_cbor); + _cbor = _items[_items.length - 1]; } else { revert UnsupportedMajorType(_cbor.majorType); } + return _cbor; } function _skipArray(CBOR memory _cbor) private pure - isMajorType(_cbor, MajorTypes.Array) - returns (CBOR memory) + isMajorType(_cbor, MAJOR_TYPE_ARRAY) + returns (CBOR memory _next) { + _next = _valueFromForkedBuffer(_cbor.buffer); CBOR[] memory _items = readArray(_cbor); - if (_items.length > 0) { - return _seekNext(_items[_items.length - 1]); - } else { + if (_items.length == 0) { revert EmptyArray(); } } function _skipBytes(CBOR memory _cbor) internal pure - isMajorType(_cbor, MajorTypes.Bytes) + isMajorType(_cbor, MAJOR_TYPE_BYTES) returns (CBOR memory) { - _cbor.len = _readLength(_cbor.buffer, _cbor.additionalInformation); - if (_cbor.len < _UINT32_MAX) { + _cbor.len = readLength(_cbor.buffer, _cbor.additionalInformation); + if (_cbor.len < UINT32_MAX) { _cbor.buffer.seek(_cbor.len); - return _valueFromBuffer(_cbor.buffer); + return _valueFromForkedBuffer(_cbor.buffer); } // TODO: support skipping indefitine length bytes array revert InvalidLengthEncoding(_cbor.len); @@ -219,19 +242,10 @@ library WitnetCBOR { function _skipInt(CBOR memory _cbor) private pure - returns (CBOR memory) + returns (CBOR memory _next) { - if (_cbor.majorType == 0 || _cbor.majorType == 1) { - uint _offset = 1; - if (_cbor.additionalInformation >= 24) { - if (_cbor.additionalInformation <= 27) { - _offset += 1 << (_cbor.additionalInformation - 24); - } else { - revert InvalidLengthEncoding(_cbor.additionalInformation); - } - } - _cbor.buffer.seek(_offset); - return _valueFromBuffer(_cbor.buffer); + if (_cbor.majorType == MAJOR_TYPE_INT || _cbor.majorType == MAJOR_TYPE_NEGATIVE_INT) { + _next = _valueFromForkedBuffer(_cbor.buffer); } else { revert UnexpectedMajorType(_cbor.majorType, 1); } @@ -239,7 +253,7 @@ library WitnetCBOR { function _skipPrimitive(CBOR memory _cbor) private pure - isMajorType(_cbor, MajorTypes.Primitive) + isMajorType(_cbor, MAJOR_TYPE_CONTENT_FREE) returns (WitnetCBOR.CBOR memory) { if (_cbor.additionalInformation == 25) { @@ -251,59 +265,143 @@ library WitnetCBOR { ) { revert UnsupportedPrimitive(_cbor.additionalInformation); } - return _valueFromBuffer(_cbor.buffer); + return _valueFromForkedBuffer(_cbor.buffer); } function _skipText(CBOR memory _cbor) internal pure - isMajorType(_cbor, MajorTypes.String) + isMajorType(_cbor, MAJOR_TYPE_STRING) returns (CBOR memory) { - _cbor.len = _readLength(_cbor.buffer, _cbor.additionalInformation); - if (_cbor.len < _UINT64_MAX) { + _cbor.len = readLength(_cbor.buffer, _cbor.additionalInformation); + if (_cbor.len < UINT64_MAX) { _cbor.buffer.seek(_cbor.len); - return _valueFromBuffer(_cbor.buffer); + return valueFromBuffer(_cbor.buffer); } // TODO: support skipping indefitine length text array revert InvalidLengthEncoding(_cbor.len); } - function readArray(CBOR memory _cbor) + function next(CBOR memory self) + internal pure + { + uint8 _initialByte; + uint8 _majorType = 255; + uint8 _additionalInformation; + uint64 _tag = UINT64_MAX; + uint256 _len = 0; + bool _isTagged = true; + while (_isTagged) { + // Extract basic CBOR properties from input bytes + _initialByte = self.buffer.readUint8(); + _len ++; + _majorType = _initialByte >> 5; + _additionalInformation = _initialByte & 0x1f; + // Early CBOR tag parsing. + if (_majorType == MAJOR_TYPE_TAG) { + uint _cursor = self.buffer.cursor; + _tag = readLength(self.buffer, _additionalInformation); + _len += self.buffer.cursor - _cursor; + + } else { + _isTagged = false; + } + } + if (_majorType > MAJOR_TYPE_CONTENT_FREE) { + revert UnsupportedMajorType(_majorType); + } + self.initialByte = _initialByte; + self.majorType = _majorType; + self.additionalInformation = _additionalInformation; + self.len = uint64(_len); + self.tag = _tag; + } + + function skip(CBOR memory self) + internal pure + { + if ( + self.majorType == MAJOR_TYPE_INT + || self.majorType == MAJOR_TYPE_NEGATIVE_INT + ) { + self.buffer.cursor += self.length(); + } else if ( + self.majorType == MAJOR_TYPE_STRING + || self.majorType == MAJOR_TYPE_BYTES + ) { + self.buffer.cursor += readLength(self.buffer, self.additionalInformation); + } else if ( + self.majorType == MAJOR_TYPE_ARRAY + ) { + readLength(self.buffer, self.additionalInformation); + } else if ( + self.majorType != MAJOR_TYPE_CONTENT_FREE + ) { + revert UnsupportedMajorType(self.majorType); + } + if (!self.eof()) { + self.next(); + } + } + + function length(CBOR memory self) internal pure - isMajorType(_cbor, MajorTypes.Array) - returns (CBOR[] memory _items) + returns (uint64) { - uint64 _length = _readLength(_cbor.buffer, _cbor.additionalInformation); - _items = new CBOR[](_length); - for (uint _ix = 0; _ix < _length; _ix ++) { - _items[_ix] = _valueFromBuffer(_cbor.buffer); - _cbor = _seekNext(_items[_ix]); + if (self.additionalInformation < 24) { + return self.additionalInformation; + } else if (self.additionalInformation > 27) { + revert InvalidLengthEncoding(self.additionalInformation); + } else { + return uint64(1 << (self.additionalInformation - 24)); } } - function _replaceWildcards(CBOR memory _cbor, string[] memory _args) - private pure - isMajorType(_cbor, MajorTypes.String) - returns (CBOR memory) + function readArray(CBOR memory self) + internal pure + isMajorType(self, MAJOR_TYPE_ARRAY) + returns (CBOR[] memory items) { - CBOR memory _copy = _valueFromBuffer(_cbor.buffer); - _copy.buffer.replaceWildcards( - _readLength(_copy.buffer, _copy.additionalInformation), - _args - ); - return _cbor; + uint64 len = readLength(self.buffer, self.additionalInformation); + items = new CBOR[](len + 1); + for (uint _ix = 0; _ix < len; _ix ++) { + items[_ix] = _valueFromForkedBuffer(self.buffer); + self.buffer.cursor = items[_ix].buffer.cursor; + self.majorType = items[_ix].majorType; + self.additionalInformation = items[_ix].additionalInformation; + self = _seekNext(self); + } + items[len] = self; } - function replaceWildcards(CBOR[] memory _items, string[] memory _args) + /// Reads the length of the next CBOR item from a buffer, consuming a different number of bytes depending on the + /// value of the `additionalInformation` argument. + function readLength( + WitnetBuffer.Buffer memory _buffer, + uint8 _additionalInformation + ) internal pure + returns (uint64) { - for (uint _ix = 0; _ix < _items.length; _ix ++) { - if (_items[_ix].majorType == 4) { - replaceWildcards(readArray(_items[_ix]), _args); - } else if (_items[_ix].majorType == 3) { - _replaceWildcards(_items[_ix], _args); - } + if (_additionalInformation < 24) { + return _additionalInformation; + } + if (_additionalInformation == 24) { + return _buffer.readUint8(); } + if (_additionalInformation == 25) { + return _buffer.readUint16(); + } + if (_additionalInformation == 26) { + return _buffer.readUint32(); + } + if (_additionalInformation == 27) { + return _buffer.readUint64(); + } + if (_additionalInformation == 31) { + return UINT64_MAX; + } + revert InvalidLengthEncoding(_additionalInformation); } /// @notice Read a `CBOR` structure into a native `bool` value. @@ -311,14 +409,9 @@ library WitnetCBOR { /// @return The value represented by the input, as a `bool` value. function readBool(CBOR memory _cbor) internal pure - isMajorType(_cbor, MajorTypes.Primitive) + isMajorType(_cbor, MAJOR_TYPE_CONTENT_FREE) returns (bool) { - // uint64 _primitive = _readLength(_cbor.buffer, _cbor.additionalInformation); - // if (_primitive == 20) { - // return false; - // } else if (_primitive == 21) { - // return true; if (_cbor.additionalInformation == 20) { return false; } else if (_cbor.additionalInformation == 21) { @@ -333,26 +426,26 @@ library WitnetCBOR { /// @return _output The value represented by the input, as a `bytes` value. function readBytes(CBOR memory _cbor) internal pure - isMajorType(_cbor, MajorTypes.Bytes) + isMajorType(_cbor, MAJOR_TYPE_BYTES) returns (bytes memory _output) { - _cbor.len = _readLength( + _cbor.len = readLength( _cbor.buffer, _cbor.additionalInformation ); - if (_cbor.len == _UINT32_MAX) { + if (_cbor.len == UINT32_MAX) { // These checks look repetitive but the equivalent loop would be more expensive. uint32 _length = uint32(_readIndefiniteStringLength( _cbor.buffer, _cbor.majorType )); - if (_length < _UINT32_MAX) { + if (_length < UINT32_MAX) { _output = abi.encodePacked(_cbor.buffer.read(_length)); _length = uint32(_readIndefiniteStringLength( _cbor.buffer, _cbor.majorType )); - if (_length < _UINT32_MAX) { + if (_length < UINT32_MAX) { _output = abi.encodePacked( _output, _cbor.buffer.read(_length) @@ -372,7 +465,7 @@ library WitnetCBOR { /// @return The value represented by the input, as an `int128` value. function readFloat16(CBOR memory _cbor) internal pure - isMajorType(_cbor, MajorTypes.Primitive) + isMajorType(_cbor, MAJOR_TYPE_CONTENT_FREE) returns (int32) { if (_cbor.additionalInformation == 25) { @@ -387,14 +480,14 @@ library WitnetCBOR { /// @param _cbor An instance of `CBOR`. function readFloat16Array(CBOR memory _cbor) internal pure - isMajorType(_cbor, MajorTypes.Array) + isMajorType(_cbor, MAJOR_TYPE_ARRAY) returns (int32[] memory _values) { - uint64 _length = _readLength(_cbor.buffer, _cbor.additionalInformation); - if (_length < _UINT64_MAX) { + uint64 _length = readLength(_cbor.buffer, _cbor.additionalInformation); + if (_length < UINT64_MAX) { _values = new int32[](_length); for (uint64 _i = 0; _i < _length; ) { - CBOR memory _item = _valueFromBuffer(_cbor.buffer); + CBOR memory _item = valueFromBuffer(_cbor.buffer); _values[_i] = readFloat16(_item); unchecked { _i ++; @@ -413,7 +506,7 @@ library WitnetCBOR { returns (int) { if (_cbor.majorType == 1) { - uint64 _value = _readLength( + uint64 _value = readLength( _cbor.buffer, _cbor.additionalInformation ); @@ -433,14 +526,14 @@ library WitnetCBOR { /// @return _array The value represented by the input, as an `int[]` value. function readIntArray(CBOR memory _cbor) internal pure - isMajorType(_cbor, MajorTypes.Array) + isMajorType(_cbor, MAJOR_TYPE_ARRAY) returns (int[] memory _array) { - uint64 _length = _readLength(_cbor.buffer, _cbor.additionalInformation); - if (_length < _UINT64_MAX) { + uint64 _length = readLength(_cbor.buffer, _cbor.additionalInformation); + if (_length < UINT64_MAX) { _array = new int[](_length); for (uint _i = 0; _i < _length; ) { - CBOR memory _item = _valueFromBuffer(_cbor.buffer); + CBOR memory _item = valueFromBuffer(_cbor.buffer); _array[_i] = readInt(_item); unchecked { _i ++; @@ -456,18 +549,18 @@ library WitnetCBOR { /// @return _text The value represented by the input, as a `string` value. function readString(CBOR memory _cbor) internal pure - isMajorType(_cbor, MajorTypes.String) + isMajorType(_cbor, MAJOR_TYPE_STRING) returns (string memory _text) { - _cbor.len = _readLength(_cbor.buffer, _cbor.additionalInformation); - if (_cbor.len == _UINT64_MAX) { + _cbor.len = readLength(_cbor.buffer, _cbor.additionalInformation); + if (_cbor.len == UINT64_MAX) { bool _done; while (!_done) { uint64 _length = _readIndefiniteStringLength( _cbor.buffer, _cbor.majorType ); - if (_length < _UINT64_MAX) { + if (_length < UINT64_MAX) { _text = string(abi.encodePacked( _text, _cbor.buffer.readText(_length / 4) @@ -486,14 +579,14 @@ library WitnetCBOR { /// @return _strings The value represented by the input, as an `string[]` value. function readStringArray(CBOR memory _cbor) internal pure - isMajorType(_cbor, MajorTypes.Array) + isMajorType(_cbor, MAJOR_TYPE_ARRAY) returns (string[] memory _strings) { - uint _length = _readLength(_cbor.buffer, _cbor.additionalInformation); - if (_length < _UINT64_MAX) { + uint _length = readLength(_cbor.buffer, _cbor.additionalInformation); + if (_length < UINT64_MAX) { _strings = new string[](_length); for (uint _i = 0; _i < _length; ) { - CBOR memory _item = _valueFromBuffer(_cbor.buffer); + CBOR memory _item = valueFromBuffer(_cbor.buffer); _strings[_i] = readString(_item); unchecked { _i ++; @@ -509,10 +602,10 @@ library WitnetCBOR { /// @return The value represented by the input, as an `uint64` value. function readUint(CBOR memory _cbor) internal pure - isMajorType(_cbor, MajorTypes.Uint) + isMajorType(_cbor, MAJOR_TYPE_INT) returns (uint) { - return _readLength( + return readLength( _cbor.buffer, _cbor.additionalInformation ); @@ -523,14 +616,14 @@ library WitnetCBOR { /// @return _values The value represented by the input, as an `uint64[]` value. function readUintArray(CBOR memory _cbor) internal pure - isMajorType(_cbor, MajorTypes.Array) + isMajorType(_cbor, MAJOR_TYPE_ARRAY) returns (uint[] memory _values) { - uint64 _length = _readLength(_cbor.buffer, _cbor.additionalInformation); - if (_length < _UINT64_MAX) { + uint64 _length = readLength(_cbor.buffer, _cbor.additionalInformation); + if (_length < UINT64_MAX) { _values = new uint[](_length); for (uint _ix = 0; _ix < _length; ) { - CBOR memory _item = _valueFromBuffer(_cbor.buffer); + CBOR memory _item = valueFromBuffer(_cbor.buffer); _values[_ix] = readUint(_item); unchecked { _ix ++; diff --git a/contracts/libs/WitnetLib.sol b/contracts/libs/WitnetLib.sol index 5e9b30558..ee24deba7 100644 --- a/contracts/libs/WitnetLib.sol +++ b/contracts/libs/WitnetLib.sol @@ -10,6 +10,7 @@ import "./WitnetV2.sol"; /// @author The Witnet Foundation. library WitnetLib { + using WitnetBuffer for WitnetBuffer.Buffer; using WitnetCBOR for WitnetCBOR.CBOR; using WitnetCBOR for WitnetCBOR.CBOR[]; using WitnetLib for bytes; @@ -186,7 +187,7 @@ library WitnetLib { _result.success, "WitnetLib: tried to read `address` from errored result." ); - if (_result.value.majorType == uint8(WitnetCBOR.MajorTypes.Bytes)) { + if (_result.value.majorType == uint8(WitnetCBOR.MAJOR_TYPE_BYTES)) { return _result.value.readBytes().toAddress(); } else { revert("WitnetLib: reading address from string not yet supported."); @@ -543,8 +544,76 @@ library WitnetLib { return _result.value.readUintArray(); } + /// @notice Decode raw CBOR bytes into a Witnet.Result instance. + /// @param _cborBytes Raw bytes representing a CBOR-encoded value. + /// @return A `Witnet.Result` instance. + function resultFromCborBytes(bytes memory _cborBytes) + public pure + returns (Witnet.Result memory) + { + WitnetCBOR.CBOR memory cborValue = WitnetCBOR.valueFromBytes(_cborBytes); + return _resultFromCborValue(cborValue); + } + /// ----------------------------- public encoding methods --------------------------------------------------------- + /// @notice Encode bytes array into given major type (UTF-8 not yet supported) + /// @param buf Bytes array + /// @return Marshaled bytes + function encode(bytes memory buf, uint majorType) + public pure + returns (bytes memory) + { + uint len = buf.length; + if (len < 23) { + return abi.encodePacked( + uint8((majorType << 5) | uint8(len)), + buf + ); + } else { + uint8 buf0 = uint8((majorType << 5)); + bytes memory buf1; + if (len <= 0xff) { + buf0 |= 24; + buf1 = abi.encodePacked(uint8(len)); + } else if (len <= 0xffff) { + buf0 |= 25; + buf1 = abi.encodePacked(uint16(len)); + } else if (len <= 0xffffffff) { + buf0 |= 26; + buf1 = abi.encodePacked(uint32(len)); + } else { + buf0 |= 27; + buf1 = abi.encodePacked(uint64(len)); + } + return abi.encodePacked( + buf0, + buf1, + buf + ); + } + } + + /// @notice Encode bytes array. + /// @param buf Bytes array + /// @return Mashaled bytes + function encode(bytes memory buf) + public pure + returns (bytes memory) + { + return encode(buf, WitnetCBOR.MAJOR_TYPE_BYTES); + } + + /// @notice Encode string array (UTF-8 not yet supported). + /// @param str String bytes. + /// @return Mashaled bytes + function encode(string memory str) + public pure + returns (bytes memory) + { + return encode(bytes(str), WitnetCBOR.MAJOR_TYPE_STRING); + } + /// @dev Encode uint64 into tagged varint. /// @dev See https://developers.google.com/protocol-buffers/docs/encoding#varints. /// @param n Number @@ -649,26 +718,38 @@ library WitnetLib { ); } - function replaceCborBytesWildcards( - bytes memory _cborBytes, - string[] memory _args + function replaceCborStringsFromBytes( + bytes memory data, + string[] memory args ) public pure - returns (WitnetCBOR.CBOR memory _cbor) + returns (WitnetCBOR.CBOR memory cbor) { - _cbor = WitnetCBOR.valueFromBytes(_cborBytes); - _cbor.readArray().replaceWildcards(_args); + cbor = WitnetCBOR.valueFromBytes(data); + while (!cbor.eof()) { + if (cbor.majorType == WitnetCBOR.MAJOR_TYPE_STRING) { + _replaceWildcards(cbor, args); + } else { + cbor.skip(); + } + } } - /// @notice Decode raw CBOR bytes into a Witnet.Result instance. - /// @param _cborBytes Raw bytes representing a CBOR-encoded value. - /// @return A `Witnet.Result` instance. - function resultFromCborBytes(bytes memory _cborBytes) - public pure - returns (Witnet.Result memory) + function _replaceWildcards(WitnetCBOR.CBOR memory self, string[] memory args) + private pure { - WitnetCBOR.CBOR memory cborValue = WitnetCBOR.valueFromBytes(_cborBytes); - return _resultFromCborValue(cborValue); + uint _rewind = self.len; + uint _start = self.buffer.cursor; + bytes memory _current_text = bytes(self.readString()); + uint _current_cbor_length = _current_text.length + _rewind; + bytes memory _new_text = WitnetBuffer.replace(bytes(_current_text), args); + if (keccak256(_new_text) != keccak256(bytes(_current_text))) { + bytes memory _new_cbor_pokes = encode(string(_new_text)); + self.buffer.cursor = _start - _rewind; + self.buffer.mutate(_current_cbor_length, _new_cbor_pokes); + } + self.buffer.cursor = _start; + self.skip(); } } \ No newline at end of file From 5e0038a1504f355abcbde0bb747ba1922f5a4a76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 1 Dec 2022 12:21:01 +0100 Subject: [PATCH 023/119] chore(libs): fix solhint warnings --- contracts/libs/WitnetLib.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/libs/WitnetLib.sol b/contracts/libs/WitnetLib.sol index ee24deba7..e2fe8fe45 100644 --- a/contracts/libs/WitnetLib.sol +++ b/contracts/libs/WitnetLib.sol @@ -740,13 +740,13 @@ library WitnetLib { { uint _rewind = self.len; uint _start = self.buffer.cursor; - bytes memory _current_text = bytes(self.readString()); - uint _current_cbor_length = _current_text.length + _rewind; - bytes memory _new_text = WitnetBuffer.replace(bytes(_current_text), args); - if (keccak256(_new_text) != keccak256(bytes(_current_text))) { - bytes memory _new_cbor_pokes = encode(string(_new_text)); + bytes memory _currentText = bytes(self.readString()); + uint _currentCborLength = _currentText.length + _rewind; + bytes memory _newText = WitnetBuffer.replace(bytes(_currentText), args); + if (keccak256(_newText) != keccak256(bytes(_currentText))) { + bytes memory _newCborPokes = encode(string(_newText)); self.buffer.cursor = _start - _rewind; - self.buffer.mutate(_current_cbor_length, _new_cbor_pokes); + self.buffer.mutate(_currentCborLength, _newCborPokes); } self.buffer.cursor = _start; self.skip(); From d9342c744a4971f3b9b32c359b07b8808bba80d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 1 Dec 2022 12:36:00 +0100 Subject: [PATCH 024/119] fix(test): refactor test/helpers/UsingTestHelper.sol --- test/helpers/UsingWitnetTestHelper.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/helpers/UsingWitnetTestHelper.sol b/test/helpers/UsingWitnetTestHelper.sol index 04be108a8..f5c2429e7 100644 --- a/test/helpers/UsingWitnetTestHelper.sol +++ b/test/helpers/UsingWitnetTestHelper.sol @@ -61,7 +61,7 @@ contract UsingWitnetTestHelper is UsingWitnet { return witnet.estimateReward(_gasPrice); } - function witnetAsUint64() external view returns (uint64) { + function witnetAsUint64() external view returns (uint) { return witnet.asUint64(result); } From ef418d117003820ad46679b820c49dabe152251a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 7 Dec 2022 09:06:07 +0100 Subject: [PATCH 025/119] chore: rebasing from feat/v2 branch --- contracts/data/WitnetReporting1Data.sol | 10 ++ .../WitnetRequestBoardTrustlessBase.sol | 117 +++++++++++------- .../WitnetRequestBoardTrustlessReporting1.sol | 16 ++- contracts/interfaces/V2/IWitnetRequests.sol | 2 +- 4 files changed, 93 insertions(+), 52 deletions(-) diff --git a/contracts/data/WitnetReporting1Data.sol b/contracts/data/WitnetReporting1Data.sol index 45b384fd0..3fe7940e2 100644 --- a/contracts/data/WitnetReporting1Data.sol +++ b/contracts/data/WitnetReporting1Data.sol @@ -32,6 +32,16 @@ abstract contract WitnetReporting1Data mapping (address => Escrow) escrows; } + constructor() { + __reporting().settings = IWitnetReporting1.SignUpConfig({ + weiRejectionFee: 0.01 ether, + weiSignUpFee: 0.1 ether, + acceptanceBlocks: 32, + banningBlocks: 256, + exitBlocks: 64 + }); + } + // --- Internal view functions diff --git a/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol b/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol index 85ffde368..9cd90585f 100644 --- a/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol +++ b/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol @@ -50,6 +50,7 @@ abstract contract WitnetRequestBoardTrustlessBase bool _upgradable, bytes32 _versionTag ) + Payable(address(0x0)) WitnetUpgradableBase( _upgradable, _versionTag, @@ -57,6 +58,22 @@ abstract contract WitnetRequestBoardTrustlessBase ) {} + receive() external payable override { + revert("WitnetRequestBoardTrustlessBase: no transfers accepted"); + } + + function blocks() override external view returns (IWitnetBlocks) { + return __board().blocks; + } + + function bytecodes() override external view returns (IWitnetBytecodes) { + return __board().bytecodes; + } + + function decoder() override external view returns (IWitnetDecoder) { + return __board().decoder; + } + // ================================================================================================================ // --- Overrides IERC165 interface -------------------------------------------------------------------------------- @@ -75,9 +92,9 @@ abstract contract WitnetRequestBoardTrustlessBase // ================================================================================================================ // --- Internal virtual methods ----------------------------------------------------------------------------------- - function _cancelDeadline(uint256 _postEpoch) internal view virtual returns (uint256); - function _reportDeadlineEpoch(uint256 _postEpoch) internal view virtual returns (uint256); - function _selectReporter(bytes32 _drHash) internal virtual view returns (address); + // function _cancelDeadline(uint256 _postEpoch) internal view virtual returns (uint256); + // function _reportDeadlineEpoch(uint256 _postEpoch) internal view virtual returns (uint256); + // function _selectReporter(bytes32 _drHash) internal virtual view returns (address); // ================================================================================================================ @@ -197,14 +214,14 @@ abstract contract WitnetRequestBoardTrustlessBase if (_refs.length > 1 && _refs[1] != address(0)) setBytecodes(_refs[1]); if (_refs.length > 2 && _refs[2] != address(0)) setDecoder(_refs[2]); - // All complying references must be provided: - if (address(__board().blocks) == address(0)) { - revert WitnetUpgradableBase.NotCompliant(type(IWitnetBlocks).interfaceId); - } else if (address(__board().bytecodes) == address(0)) { - revert WitnetUpgradableBase.NotCompliant(type(IWitnetBytecodes).interfaceId); - } else if (address(__board().decoder) == address(0)) { - revert WitnetUpgradableBase.NotCompliant(type(IWitnetDecoder).interfaceId); - } +// // All complying references must be provided: +// if (address(__board().blocks) == address(0)) { +// revert WitnetUpgradableBase.NotCompliant(type(IWitnetBlocks).interfaceId); +// } else if (address(__board().bytecodes) == address(0)) { +// revert WitnetUpgradableBase.NotCompliant(type(IWitnetBytecodes).interfaceId); +// } else if (address(__board().decoder) == address(0)) { +// revert WitnetUpgradableBase.NotCompliant(type(IWitnetDecoder).interfaceId); +// } // Set deliveryTag if not done yet: if (__board().serviceTag == bytes4(0)) { @@ -282,14 +299,17 @@ abstract contract WitnetRequestBoardTrustlessBase { return ( estimateReportFee(_drRadHash, _gasPrice) - + __board().bytecodes.lookupRadonSLAReward(_drSlaHash) * _witPrice + // TODO: + __board().bytecodes.lookupDrSlaReward(_drSlaHash) * _witPrice ); } function estimateReportFee(bytes32 _drRadHash, uint256 _gasPrice) public view - virtual - returns (uint256); + virtual override + returns (uint256) + { + revert("WitnetRequestBoardTrustlessBase: not yet implemented"); + } function getDrPost(bytes32 _drHash) public view @@ -416,54 +436,55 @@ abstract contract WitnetRequestBoardTrustlessBase function postDr( bytes32 _drRadHash, - bytes32 _drSlaHash, - uint256 _weiWitPrice + bytes32 _drSlaHash ) external payable returns (bytes32 _drHash) { // TODO - // // Calculate current epoch in Witnet terms: - // uint256 _currentEpoch = block.timestamp.toEpoch(); - - // // Calculate data request delivery tag: - // bytes8 _drDeliveryTag = bytes8(keccak256(abi.encode( - // _msgSender(), - // _drRadHash, - // _drSlaHash, - // _currentEpoch, - // ++ __board().serviceStats.totalPosts - // ))); - // _drDeliveryTag |= bytes8(serviceTag()); - - // // Calculate data request post hash: - // _drHash = WitnetV2.hash(abi.encodePacked( - // _drRadHash, - // _drSlaHash, - // _drDeliveryTag - // )); - - // // Check minimum base fee is covered: + // Calculate current epoch in Witnet terms: + uint256 _currentEpoch = block.timestamp; // TODO: .toEpoch(); + + // Calculate data request delivery tag: + bytes8 _drDeliveryTag = bytes8(keccak256(abi.encode( + _msgSender(), + _drRadHash, + _drSlaHash, + _currentEpoch, + ++ __board().serviceStats.totalPosts + ))); + _drDeliveryTag |= bytes8(serviceTag()); + + // Calculate data request post hash: + _drHash = Witnet.hash(abi.encodePacked( + _drRadHash, + _drSlaHash, + _drDeliveryTag + )); + + // Check minimum base fee is covered: // uint256 _minBaseFee = estimateBaseFee( // _drRadHash, // _getGasPrice(), // _drSlaHash, - // _weiWitPrice // ); // if (_getMsgValue() < _minBaseFee) { // revert IWitnetRequests.DrPostLowReward(_drHash, _minBaseFee, _getMsgValue()); // } - // // Save DrPost in storage: - // __drPostRequest(_drHash) = WitnetV2.DrPostRequest({ - // epoch: _currentEpoch, - // from: _msgSender(), - // to: _selectReporter(), - // radHash: _drRadHash, - // slaHash: _drSlaHash, - // weiReward: _getMsgValue() - // }); - // emit DrPost(__drPostRequest(_drHash)); + // Save DrPost in storage: + WitnetV2.DrPost storage __dr = __drPost(_drHash); + __dr.block = block.number; + __dr.status = WitnetV2.DrPostStatus.Posted; + __dr.request = WitnetV2.DrPostRequest({ + epoch: _currentEpoch, + requester: _msgSender(), + reporter: msg.sender, // TODO: _selectReporter(), + radHash: _drRadHash, + slaHash: _drSlaHash, + weiReward: _getMsgValue() + }); + emit DrPost(msg.sender, _drHash);//__drPostRequest(_drHash)); } function reportDrPost( diff --git a/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessReporting1.sol b/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessReporting1.sol index d8e5d3c5b..8297ea0b6 100644 --- a/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessReporting1.sol +++ b/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessReporting1.sol @@ -11,7 +11,7 @@ import "../../../data/WitnetReporting1Data.sol"; /// @dev This contract enables posting requests that Witnet bridges will insert into the Witnet network. /// The result of the requests will be posted back to this contract by the bridge nodes too. /// @author The Witnet Foundation -abstract contract WitnetRequestBoardTrustlessReporting1 +contract WitnetRequestBoardTrustlessReporting1 is WitnetRequestBoardTrustlessBase, WitnetReporting1Data @@ -221,8 +221,8 @@ function deleteDrPost(bytes32 _drHash) external override drPostInStatus(_drHash, WitnetV2.DrPostStatus.Posted) - onlySignedUpReporters - onlyExpectedReporterFor(_drHash) + // onlySignedUpReporters + // onlyExpectedReporterFor(_drHash) { if (_msgSender() != __drPostRequest(_drHash).requester) { revert WitnetV2.Unauthorized(_msgSender()); @@ -267,4 +267,14 @@ function deleteDrPost(bytes32 _drHash) } + // ================================================================================================================ + // --- IWitnetReporting1Admin implementation ---------------------------------------------------------------------- + + function setSignUpConfig(IWitnetReporting1.SignUpConfig calldata) + virtual override + external + { + revert("WitnetRequestBoardTrustlessReporting1: not yet implemented"); + } + } \ No newline at end of file diff --git a/contracts/interfaces/V2/IWitnetRequests.sol b/contracts/interfaces/V2/IWitnetRequests.sol index 38d16715d..1e795bf79 100644 --- a/contracts/interfaces/V2/IWitnetRequests.sol +++ b/contracts/interfaces/V2/IWitnetRequests.sol @@ -23,7 +23,7 @@ interface IWitnetRequests { error DrPostOnlyRequester(bytes32 drHash, address requester); error DrPostOnlyReporter(bytes32 drHash, address reporter); - event DrPost(WitnetV2.DrPostRequest request); + event DrPost(address indexed reporter, bytes32 drHash); event DrPostDeleted (address indexed from, bytes32 drHash); event DrPostDisputed(address indexed from, bytes32 drHash); event DrPostReported(address indexed from, bytes32 drHash); From 51147888cfbe862f7c45d07f08882c3f412136cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 7 Dec 2022 09:10:31 +0100 Subject: [PATCH 026/119] refactor(libs): no undescored vars in libs --- contracts/libs/WitnetBuffer.sol | 540 ++++++++++++++++---------------- test/TestWitnetBuffer.sol | 1 - 2 files changed, 271 insertions(+), 270 deletions(-) diff --git a/contracts/libs/WitnetBuffer.sol b/contracts/libs/WitnetBuffer.sol index 6aee21d9c..f0dd791a6 100644 --- a/contracts/libs/WitnetBuffer.sol +++ b/contracts/libs/WitnetBuffer.sol @@ -21,180 +21,182 @@ library WitnetBuffer { } // Ensures we access an existing index in an array - modifier withinRange(uint _index, uint _range) { - if (_index >= _range) { - revert IndexOutOfBounds(_index, _range); + modifier withinRange(uint index, uint _range) { + if (index >= _range) { + revert IndexOutOfBounds(index, _range); } _; } /// @notice Concatenate undefinite number of bytes chunks. - /// @dev Faster than looping on `abi.encodePacked(_output, _buffs[_ix])`. + /// @dev Faster than looping on `abi.encodePacked(output, _buffs[ix])`. function concat(bytes[] memory _buffs) internal pure - returns (bytes memory _output) + returns (bytes memory output) { unchecked { - uint _destinationPointer; - uint _destinationLength; + uint destinationPointer; + uint destinationLength; assembly { // get safe scratch location - _output := mload(0x40) + output := mload(0x40) // set starting destination pointer - _destinationPointer := add(_output, 32) + destinationPointer := add(output, 32) } - for (uint _ix = 1; _ix <= _buffs.length; _ix ++) { - uint _source; - uint _sourceLength; - uint _sourcePointer; + for (uint ix = 1; ix <= _buffs.length; ix ++) { + uint source; + uint sourceLength; + uint sourcePointer; assembly { // load source length pointer - _source := mload(add(_buffs, mul(_ix, 32))) + source := mload(add(_buffs, mul(ix, 32))) // load source length - _sourceLength := mload(_source) + sourceLength := mload(source) // sets source memory pointer - _sourcePointer := add(_source, 32) + sourcePointer := add(source, 32) } _memcpy( - _destinationPointer, - _sourcePointer, - _sourceLength + destinationPointer, + sourcePointer, + sourceLength ); assembly { // increase total destination length - _destinationLength := add(_destinationLength, _sourceLength) + destinationLength := add(destinationLength, sourceLength) // sets destination memory pointer - _destinationPointer := add(_destinationPointer, _sourceLength) + destinationPointer := add(destinationPointer, sourceLength) } } assembly { // protect output bytes - mstore(_output, _destinationLength) + mstore(output, destinationLength) // set final output length - mstore(0x40, add(mload(0x40), add(_destinationLength, 32))) + mstore(0x40, add(mload(0x40), add(destinationLength, 32))) } } } - function fork(WitnetBuffer.Buffer memory _buffer) + function fork(WitnetBuffer.Buffer memory buffer) internal pure - returns (WitnetBuffer.Buffer memory _forked) + returns (WitnetBuffer.Buffer memory) { - _forked.data = _buffer.data; - _forked.cursor = _buffer.cursor; + return Buffer( + buffer.data, + buffer.cursor + ); } function mutate( - WitnetBuffer.Buffer memory _buffer, - uint _peekLength, - bytes memory _pokeBytes + WitnetBuffer.Buffer memory buffer, + uint length, + bytes memory pokes ) internal pure - withinRange(_peekLength, _buffer.data.length - _buffer.cursor) + withinRange(length, buffer.data.length - buffer.cursor) { - bytes[] memory _parts = new bytes[](3); - _parts[0] = peek( - _buffer, + bytes[] memory parts = new bytes[](3); + parts[0] = peek( + buffer, 0, - _buffer.cursor + buffer.cursor ); - _parts[1] = _pokeBytes; - _parts[2] = peek( - _buffer, - _buffer.cursor + _peekLength, - _buffer.data.length - _buffer.cursor - _peekLength + parts[1] = pokes; + parts[2] = peek( + buffer, + buffer.cursor + length, + buffer.data.length - buffer.cursor - length ); - _buffer.data = concat(_parts); + buffer.data = concat(parts); } /// @notice Read and consume the next byte from the buffer. - /// @param _buffer An instance of `Buffer`. + /// @param buffer An instance of `Buffer`. /// @return The next byte in the buffer counting from the cursor position. - function next(Buffer memory _buffer) + function next(Buffer memory buffer) internal pure - withinRange(_buffer.cursor, _buffer.data.length) + withinRange(buffer.cursor, buffer.data.length) returns (bytes1) { // Return the byte at the position marked by the cursor and advance the cursor all at once - return _buffer.data[_buffer.cursor ++]; + return buffer.data[buffer.cursor ++]; } function peek( - WitnetBuffer.Buffer memory _buffer, - uint _offset, - uint _length + WitnetBuffer.Buffer memory buffer, + uint offset, + uint length ) internal pure - withinRange(_offset + _length, _buffer.data.length + 1) + withinRange(offset + length, buffer.data.length + 1) returns (bytes memory) { - bytes memory _data = _buffer.data; - bytes memory _peek = new bytes(_length); - uint _destinationPointer; - uint _sourcePointer; + bytes memory data = buffer.data; + bytes memory peeks = new bytes(length); + uint destinationPointer; + uint sourcePointer; assembly { - _destinationPointer := add(_peek, 32) - _sourcePointer := add(add(_data, 32), _offset) + destinationPointer := add(peeks, 32) + sourcePointer := add(add(data, 32), offset) } _memcpy( - _destinationPointer, - _sourcePointer, - _length + destinationPointer, + sourcePointer, + length ); - return _peek; + return peeks; } // @notice Extract bytes array from buffer starting from current cursor. - /// @param _buffer An instance of `Buffer`. - /// @param _length How many bytes to peek from the Buffer. + /// @param buffer An instance of `Buffer`. + /// @param length How many bytes to peek from the Buffer. // solium-disable-next-line security/no-assign-params function peek( - WitnetBuffer.Buffer memory _buffer, - uint _length + WitnetBuffer.Buffer memory buffer, + uint length ) internal pure - withinRange(_length, _buffer.data.length - _buffer.cursor) + withinRange(length, buffer.data.length - buffer.cursor) returns (bytes memory) { return peek( - _buffer, - _buffer.cursor, - _length + buffer, + buffer.cursor, + length ); } /// @notice Read and consume a certain amount of bytes from the buffer. - /// @param _buffer An instance of `Buffer`. - /// @param _length How many bytes to read and consume from the buffer. - /// @return _output A `bytes memory` containing the first `_length` bytes from the buffer, counting from the cursor position. - function read(Buffer memory _buffer, uint _length) + /// @param buffer An instance of `Buffer`. + /// @param length How many bytes to read and consume from the buffer. + /// @return output A `bytes memory` containing the first `length` bytes from the buffer, counting from the cursor position. + function read(Buffer memory buffer, uint length) internal pure - withinRange(_buffer.cursor + _length, _buffer.data.length + 1) - returns (bytes memory _output) + withinRange(buffer.cursor + length, buffer.data.length + 1) + returns (bytes memory output) { // Create a new `bytes memory destination` value - _output = new bytes(_length); + output = new bytes(length); // Early return in case that bytes length is 0 - if (_length > 0) { - bytes memory _input = _buffer.data; - uint _offset = _buffer.cursor; + if (length > 0) { + bytes memory input = buffer.data; + uint offset = buffer.cursor; // Get raw pointers for source and destination - uint _sourcePointer; - uint _destinationPointer; + uint sourcePointer; + uint destinationPointer; assembly { - _sourcePointer := add(add(_input, 32), _offset) - _destinationPointer := add(_output, 32) + sourcePointer := add(add(input, 32), offset) + destinationPointer := add(output, 32) } - // Copy `_length` bytes from source to destination + // Copy `length` bytes from source to destination _memcpy( - _destinationPointer, - _sourcePointer, - _length + destinationPointer, + sourcePointer, + length ); - // Move the cursor forward by `_length` bytes + // Move the cursor forward by `length` bytes seek( - _buffer, - _length, + buffer, + length, true ); } @@ -206,40 +208,40 @@ library WitnetBuffer { /// by 5 decimal orders so as to get a fixed precision of 5 decimal positions, which should be OK for most `float16` /// use cases. In other words, the integer output of this method is 10,000 times the actual value. The input bytes are /// expected to follow the 16-bit base-2 format (a.k.a. `binary16`) in the IEEE 754-2008 standard. - /// @param _buffer An instance of `Buffer`. - /// @return _result The `int32` value of the next 4 bytes in the buffer counting from the cursor position. - function readFloat16(Buffer memory _buffer) + /// @param buffer An instance of `Buffer`. + /// @return result The `int32` value of the next 4 bytes in the buffer counting from the cursor position. + function readFloat16(Buffer memory buffer) internal pure - returns (int32 _result) + returns (int32 result) { - uint32 _value = readUint16(_buffer); + uint32 value = readUint16(buffer); // Get bit at position 0 - uint32 _sign = _value & 0x8000; + uint32 sign = value & 0x8000; // Get bits 1 to 5, then normalize to the [-14, 15] range so as to counterweight the IEEE 754 exponent bias - int32 _exponent = (int32(_value & 0x7c00) >> 10) - 15; + int32 exponent = (int32(value & 0x7c00) >> 10) - 15; // Get bits 6 to 15 - int32 _significand = int32(_value & 0x03ff); + int32 significand = int32(value & 0x03ff); // Add 1024 to the fraction if the exponent is 0 - if (_exponent == 15) { - _significand |= 0x400; + if (exponent == 15) { + significand |= 0x400; } // Compute `2 ^ exponent · (1 + fraction / 1024)` - if (_exponent >= 0) { - _result = ( - int32((int256(1 << uint256(int256(_exponent))) + if (exponent >= 0) { + result = ( + int32((int256(1 << uint256(int256(exponent))) * 10000 - * int256(uint256(int256(_significand)) | 0x400)) >> 10) + * int256(uint256(int256(significand)) | 0x400)) >> 10) ); } else { - _result = (int32( - ((int256(uint256(int256(_significand)) | 0x400) * 10000) - / int256(1 << uint256(int256(- _exponent)))) + result = (int32( + ((int256(uint256(int256(significand)) | 0x400) * 10000) + / int256(1 << uint256(int256(- exponent)))) >> 10 )); } // Make the result negative if the sign bit is not 0 - if (_sign != 0) { - _result *= -1; + if (sign != 0) { + result *= -1; } } @@ -247,268 +249,268 @@ library WitnetBuffer { /// but it can be easily casted into a string with `string(result)`. // solium-disable-next-line security/no-assign-params function readText( - WitnetBuffer.Buffer memory _buffer, - uint64 _length + WitnetBuffer.Buffer memory buffer, + uint64 length ) internal pure - returns (bytes memory _text) + returns (bytes memory text) { - _text = new bytes(_length); + text = new bytes(length); unchecked { - for (uint64 _index = 0; _index < _length; _index ++) { - uint8 _char = readUint8(_buffer); - if (_char & 0x80 != 0) { - if (_char < 0xe0) { - _char = (_char & 0x1f) << 6 - | (readUint8(_buffer) & 0x3f); - _length -= 1; - } else if (_char < 0xf0) { - _char = (_char & 0x0f) << 12 - | (readUint8(_buffer) & 0x3f) << 6 - | (readUint8(_buffer) & 0x3f); - _length -= 2; + for (uint64 index = 0; index < length; index ++) { + uint8 char = readUint8(buffer); + if (char & 0x80 != 0) { + if (char < 0xe0) { + char = (char & 0x1f) << 6 + | (readUint8(buffer) & 0x3f); + length -= 1; + } else if (char < 0xf0) { + char = (char & 0x0f) << 12 + | (readUint8(buffer) & 0x3f) << 6 + | (readUint8(buffer) & 0x3f); + length -= 2; } else { - _char = (_char & 0x0f) << 18 - | (readUint8(_buffer) & 0x3f) << 12 - | (readUint8(_buffer) & 0x3f) << 6 - | (readUint8(_buffer) & 0x3f); - _length -= 3; + char = (char & 0x0f) << 18 + | (readUint8(buffer) & 0x3f) << 12 + | (readUint8(buffer) & 0x3f) << 6 + | (readUint8(buffer) & 0x3f); + length -= 3; } } - _text[_index] = bytes1(_char); + text[index] = bytes1(char); } // Adjust text to actual length: assembly { - mstore(_text, _length) + mstore(text, length) } } } /// @notice Read and consume the next byte from the buffer as an `uint8`. - /// @param _buffer An instance of `Buffer`. - /// @return _value The `uint8` value of the next byte in the buffer counting from the cursor position. - function readUint8(Buffer memory _buffer) + /// @param buffer An instance of `Buffer`. + /// @return value The `uint8` value of the next byte in the buffer counting from the cursor position. + function readUint8(Buffer memory buffer) internal pure - withinRange(_buffer.cursor, _buffer.data.length) - returns (uint8 _value) + withinRange(buffer.cursor, buffer.data.length) + returns (uint8 value) { - bytes memory _data = _buffer.data; - uint _offset = _buffer.cursor; + bytes memory data = buffer.data; + uint offset = buffer.cursor; assembly { - _value := mload(add(add(_data, 1), _offset)) + value := mload(add(add(data, 1), offset)) } - _buffer.cursor ++; + buffer.cursor ++; } /// @notice Read and consume the next 2 bytes from the buffer as an `uint16`. - /// @param _buffer An instance of `Buffer`. - /// @return _value The `uint16` value of the next 2 bytes in the buffer counting from the cursor position. - function readUint16(Buffer memory _buffer) + /// @param buffer An instance of `Buffer`. + /// @return value The `uint16` value of the next 2 bytes in the buffer counting from the cursor position. + function readUint16(Buffer memory buffer) internal pure - withinRange(_buffer.cursor + 1, _buffer.data.length) - returns (uint16 _value) + withinRange(buffer.cursor + 1, buffer.data.length) + returns (uint16 value) { - bytes memory _data = _buffer.data; - uint _offset = _buffer.cursor; + bytes memory data = buffer.data; + uint offset = buffer.cursor; assembly { - _value := mload(add(add(_data, 2), _offset)) + value := mload(add(add(data, 2), offset)) } - _buffer.cursor += 2; + buffer.cursor += 2; } /// @notice Read and consume the next 4 bytes from the buffer as an `uint32`. - /// @param _buffer An instance of `Buffer`. - /// @return _value The `uint32` value of the next 4 bytes in the buffer counting from the cursor position. - function readUint32(Buffer memory _buffer) + /// @param buffer An instance of `Buffer`. + /// @return value The `uint32` value of the next 4 bytes in the buffer counting from the cursor position. + function readUint32(Buffer memory buffer) internal pure - withinRange(_buffer.cursor + 3, _buffer.data.length) - returns (uint32 _value) + withinRange(buffer.cursor + 3, buffer.data.length) + returns (uint32 value) { - bytes memory _data = _buffer.data; - uint _offset = _buffer.cursor; + bytes memory data = buffer.data; + uint offset = buffer.cursor; assembly { - _value := mload(add(add(_data, 4), _offset)) + value := mload(add(add(data, 4), offset)) } - _buffer.cursor += 4; + buffer.cursor += 4; } /// @notice Read and consume the next 8 bytes from the buffer as an `uint64`. - /// @param _buffer An instance of `Buffer`. - /// @return _value The `uint64` value of the next 8 bytes in the buffer counting from the cursor position. - function readUint64(Buffer memory _buffer) + /// @param buffer An instance of `Buffer`. + /// @return value The `uint64` value of the next 8 bytes in the buffer counting from the cursor position. + function readUint64(Buffer memory buffer) internal pure - withinRange(_buffer.cursor + 7, _buffer.data.length) - returns (uint64 _value) + withinRange(buffer.cursor + 7, buffer.data.length) + returns (uint64 value) { - bytes memory _data = _buffer.data; - uint _offset = _buffer.cursor; + bytes memory data = buffer.data; + uint offset = buffer.cursor; assembly { - _value := mload(add(add(_data, 8), _offset)) + value := mload(add(add(data, 8), offset)) } - _buffer.cursor += 8; + buffer.cursor += 8; } /// @notice Read and consume the next 16 bytes from the buffer as an `uint128`. - /// @param _buffer An instance of `Buffer`. - /// @return _value The `uint128` value of the next 16 bytes in the buffer counting from the cursor position. - function readUint128(Buffer memory _buffer) + /// @param buffer An instance of `Buffer`. + /// @return value The `uint128` value of the next 16 bytes in the buffer counting from the cursor position. + function readUint128(Buffer memory buffer) internal pure - withinRange(_buffer.cursor + 15, _buffer.data.length) - returns (uint128 _value) + withinRange(buffer.cursor + 15, buffer.data.length) + returns (uint128 value) { - bytes memory _data = _buffer.data; - uint _offset = _buffer.cursor; + bytes memory data = buffer.data; + uint offset = buffer.cursor; assembly { - _value := mload(add(add(_data, 16), _offset)) + value := mload(add(add(data, 16), offset)) } - _buffer.cursor += 16; + buffer.cursor += 16; } /// @notice Read and consume the next 32 bytes from the buffer as an `uint256`. - /// @param _buffer An instance of `Buffer`. - /// @return _value The `uint256` value of the next 32 bytes in the buffer counting from the cursor position. - function readUint256(Buffer memory _buffer) + /// @param buffer An instance of `Buffer`. + /// @return value The `uint256` value of the next 32 bytes in the buffer counting from the cursor position. + function readUint256(Buffer memory buffer) internal pure - withinRange(_buffer.cursor + 31, _buffer.data.length) - returns (uint256 _value) + withinRange(buffer.cursor + 31, buffer.data.length) + returns (uint256 value) { - bytes memory _data = _buffer.data; - uint _offset = _buffer.cursor; + bytes memory data = buffer.data; + uint offset = buffer.cursor; assembly { - _value := mload(add(add(_data, 32), _offset)) + value := mload(add(add(data, 32), offset)) } - _buffer.cursor += 32; + buffer.cursor += 32; } /// @notice Replace bytecode indexed wildcards by correspondent string. /// @dev Wildcard format: "\#\", with # in ["0".."9"]. - /// @param _input Bytes array containing strings. - /// @param _args String values for replacing existing indexed wildcards in _input. - function replace(bytes memory _input, string[] memory _args) + /// @param input Bytes array containing strings. + /// @param args String values for replacing existing indexed wildcards in input. + function replace(bytes memory input, string[] memory args) internal pure - returns (bytes memory _output) + returns (bytes memory output) { - uint _ix = 0; uint _lix = 0; - uint _inputLength; - uint _inputPointer; - uint _outputLength; - uint _outputPointer; - uint _source; - uint _sourceLength; - uint _sourcePointer; + uint ix = 0; uint lix = 0; + uint inputLength; + uint inputPointer; + uint outputLength; + uint outputPointer; + uint source; + uint sourceLength; + uint sourcePointer; assembly { // set starting input pointer - _inputPointer := add(_input, 32) + inputPointer := add(input, 32) // get safe output location - _output := mload(0x40) + output := mload(0x40) // set starting output pointer - _outputPointer := add(_output, 32) + outputPointer := add(output, 32) } unchecked { - uint _length = _input.length - 2; - for (; _ix < _length; ) { + 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") + input[ix] == bytes1("\\") + && input[ix + 2] == bytes1("\\") + && input[ix + 1] >= bytes1("0") + && input[ix + 1] <= bytes1("9") ) { - if (_ix > _lix) { - _inputLength = (_ix - _lix); + if (ix > lix) { + inputLength = (ix - lix); _memcpy( - _outputPointer, - _inputPointer, - _inputLength + outputPointer, + inputPointer, + inputLength ); - _inputPointer += _inputLength + 3; - _outputPointer += _inputLength; + inputPointer += inputLength + 3; + outputPointer += inputLength; } - uint _ax = uint(uint8(_input[_ix + 1]) - uint8(bytes1("0"))); - if (_ax >= _args.length) { - revert MissingArgs(_ax, _args); + uint ax = uint(uint8(input[ix + 1]) - uint8(bytes1("0"))); + if (ax >= args.length) { + revert MissingArgs(ax, args); } assembly { - _source := mload(add(_args, mul(32, add(_ax, 1)))) - _sourceLength := mload(_source) - _sourcePointer := add(_source, 32) + source := mload(add(args, mul(32, add(ax, 1)))) + sourceLength := mload(source) + sourcePointer := add(source, 32) } _memcpy( - _outputPointer, - _sourcePointer, - _sourceLength + outputPointer, + sourcePointer, + sourceLength ); - _outputLength += _inputLength + _sourceLength; - _outputPointer += _sourceLength; - _ix += 3; - _lix = _ix; + outputLength += inputLength + sourceLength; + outputPointer += sourceLength; + ix += 3; + lix = ix; } else { - _ix ++; + ix ++; } } - _ix = _input.length; + ix = input.length; } - if (_outputLength > 0) { - if (_ix > _lix ) { + if (outputLength > 0) { + if (ix > lix ) { _memcpy( - _outputPointer, - _inputPointer, - _ix - _lix + outputPointer, + inputPointer, + ix - lix ); - _outputLength += (_ix - _lix); + outputLength += (ix - lix); } assembly { // set final output length - mstore(_output, _outputLength) + mstore(output, outputLength) // protect output bytes - mstore(0x40, add(mload(0x40), add(_outputLength, 32))) + mstore(0x40, add(mload(0x40), add(outputLength, 32))) } } else { - return _input; + return input; } } /// @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. - /// @param _relative Whether to count `_offset` from the last position of the cursor (`true`) or the beginning of the + /// @param buffer An instance of `Buffer`. + /// @param offset How many bytes to move the cursor forward. + /// @param relative Whether to count `offset` from the last position of the cursor (`true`) or the beginning of the /// buffer (`true`). - /// @return The final position of the cursor (will equal `_offset` if `_relative` is `false`). + /// @return The final position of the cursor (will equal `offset` if `relative` is `false`). // solium-disable-next-line security/no-assign-params function seek( - Buffer memory _buffer, - uint _offset, - bool _relative + Buffer memory buffer, + uint offset, + bool relative ) internal pure - withinRange(_offset, _buffer.data.length + 1) + withinRange(offset, buffer.data.length + 1) returns (uint) { // Deal with relative offsets - if (_relative) { - _offset += _buffer.cursor; + if (relative) { + offset += buffer.cursor; } - _buffer.cursor = _offset; - return _offset; + buffer.cursor = offset; + return offset; } /// @notice Move the inner cursor a number of bytes forward. /// @dev This is a simple wrapper around the relative offset case of `seek()`. - /// @param _buffer An instance of `Buffer`. - /// @param _relativeOffset How many bytes to move the cursor forward. + /// @param buffer An instance of `Buffer`. + /// @param relativeOffset How many bytes to move the cursor forward. /// @return The final position of the cursor. function seek( - Buffer memory _buffer, - uint _relativeOffset + Buffer memory buffer, + uint relativeOffset ) internal pure returns (uint) { return seek( - _buffer, - _relativeOffset, + buffer, + relativeOffset, true ); } @@ -516,33 +518,33 @@ library WitnetBuffer { /// @notice Copy bytes from one memory address into another. /// @dev This function was borrowed from Nick Johnson's `solidity-stringutils` lib, and reproduced here under the terms /// of [Apache License 2.0](https://github.com/Arachnid/solidity-stringutils/blob/master/LICENSE). - /// @param _dest Address of the destination memory. - /// @param _src Address to the source memory. - /// @param _len How many bytes to copy. + /// @param dest Address of the destination memory. + /// @param src Address to the source memory. + /// @param len How many bytes to copy. // solium-disable-next-line security/no-assign-params function _memcpy( - uint _dest, - uint _src, - uint _len + uint dest, + uint src, + uint len ) private pure { unchecked { // Copy word-length chunks while possible - for (; _len >= 32; _len -= 32) { + for (; len >= 32; len -= 32) { assembly { - mstore(_dest, mload(_src)) + mstore(dest, mload(src)) } - _dest += 32; - _src += 32; + dest += 32; + src += 32; } - if (_len > 0) { + if (len > 0) { // Copy remaining bytes - uint _mask = 256 ** (32 - _len) - 1; + uint _mask = 256 ** (32 - len) - 1; assembly { - let _srcpart := and(mload(_src), not(_mask)) - let _destpart := and(mload(_dest), _mask) - mstore(_dest, or(_destpart, _srcpart)) + let srcpart := and(mload(src), not(_mask)) + let destpart := and(mload(dest), _mask) + mstore(dest, or(destpart, srcpart)) } } } diff --git a/test/TestWitnetBuffer.sol b/test/TestWitnetBuffer.sol index 030e87d95..e431f9af4 100644 --- a/test/TestWitnetBuffer.sol +++ b/test/TestWitnetBuffer.sol @@ -6,7 +6,6 @@ pragma experimental ABIEncoderV2; import "truffle/Assert.sol"; import "../contracts/libs/WitnetBuffer.sol"; - contract TestWitnetBuffer { using WitnetBuffer for WitnetBuffer.Buffer; From 4262fe8713323ccd29017f35334cf398525e6c77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 7 Dec 2022 09:12:11 +0100 Subject: [PATCH 027/119] fix(libs): test, refine, refactor WitnetCBOR --- contracts/libs/WitnetCBOR.sol | 602 ++++++++++++++-------------------- test/TestWitnetCBOR.sol | 66 ++-- 2 files changed, 273 insertions(+), 395 deletions(-) diff --git a/contracts/libs/WitnetCBOR.sol b/contracts/libs/WitnetCBOR.sol index c5d41dab3..d7d426ddb 100644 --- a/contracts/libs/WitnetCBOR.sol +++ b/contracts/libs/WitnetCBOR.sol @@ -48,17 +48,17 @@ library WitnetCBOR { error UnsupportedMajorType(uint unexpected); modifier isMajorType( - WitnetCBOR.CBOR memory _cbor, - uint8 _expected + WitnetCBOR.CBOR memory cbor, + uint8 expected ) { - if (_cbor.majorType != _expected) { - revert UnexpectedMajorType(_cbor.majorType, _expected); + if (cbor.majorType != expected) { + revert UnexpectedMajorType(cbor.majorType, expected); } _; } - modifier notEmpty(WitnetBuffer.Buffer memory _buf) { - if (_buf.data.length == 0) { + modifier notEmpty(WitnetBuffer.Buffer memory buffer) { + if (buffer.data.length == 0) { revert WitnetBuffer.EmptyBuffer(); } _; @@ -73,281 +73,118 @@ library WitnetCBOR { /// @notice Decode a CBOR structure from raw bytes. /// @dev This is the main factory for CBOR instances, which can be later decoded into native EVM types. - /// @param _cborBytes Raw bytes representing a CBOR-encoded value. + /// @param bytecode Raw bytes representing a CBOR-encoded value. /// @return A `CBOR` instance containing a partially decoded value. - function valueFromBytes(bytes memory _cborBytes) + function fromBytes(bytes memory bytecode) internal pure returns (CBOR memory) { - WitnetBuffer.Buffer memory buffer = WitnetBuffer.Buffer(_cborBytes, 0); - return valueFromBuffer(buffer); + WitnetBuffer.Buffer memory buffer = WitnetBuffer.Buffer(bytecode, 0); + return fromBuffer(buffer); } /// @notice Decode a CBOR structure from raw bytes. /// @dev This is an alternate factory for CBOR instances, which can be later decoded into native EVM types. - /// @param _buffer A Buffer structure representing a CBOR-encoded value. + /// @param buffer A Buffer structure representing a CBOR-encoded value. /// @return A `CBOR` instance containing a partially decoded value. - function valueFromBuffer(WitnetBuffer.Buffer memory _buffer) + function fromBuffer(WitnetBuffer.Buffer memory buffer) internal pure - notEmpty(_buffer) + notEmpty(buffer) returns (CBOR memory) { - uint8 _initialByte; - uint8 _majorType = 255; - uint8 _additionalInformation; - uint64 _tag = UINT64_MAX; - uint256 _len; - - bool _isTagged = true; - while (_isTagged) { + uint8 initialByte; + uint8 majorType = 255; + uint8 additionalInformation; + uint64 tag = UINT64_MAX; + uint256 len; + bool isTagged = true; + while (isTagged) { // Extract basic CBOR properties from input bytes - _initialByte = _buffer.readUint8(); - _len ++; - _majorType = _initialByte >> 5; - _additionalInformation = _initialByte & 0x1f; + initialByte = buffer.readUint8(); + len ++; + majorType = initialByte >> 5; + additionalInformation = initialByte & 0x1f; // Early CBOR tag parsing. - if (_majorType == MAJOR_TYPE_TAG) { - uint _cursor = _buffer.cursor; - _tag = readLength(_buffer, _additionalInformation); - _len += _buffer.cursor - _cursor; + if (majorType == MAJOR_TYPE_TAG) { + uint _cursor = buffer.cursor; + tag = readLength(buffer, additionalInformation); + len += buffer.cursor - _cursor; } else { - _isTagged = false; + isTagged = false; } } - if (_majorType > MAJOR_TYPE_CONTENT_FREE) { - revert UnsupportedMajorType(_majorType); + if (majorType > MAJOR_TYPE_CONTENT_FREE) { + revert UnsupportedMajorType(majorType); } return CBOR( - _buffer, - _initialByte, - _majorType, - _additionalInformation, - uint64(_len), - _tag + buffer, + initialByte, + majorType, + additionalInformation, + uint64(len), + tag ); } - /// @notice Decode a CBOR structure from raw bytes. - /// @dev This is an alternate factory for CBOR instances, which can be later decoded into native EVM types. - /// @param _buffer A Buffer structure representing a CBOR-encoded value. - /// @return _value A `CBOR` instance containing a partially decoded value. - function _valueFromForkedBuffer(WitnetBuffer.Buffer memory _buffer) - private pure - notEmpty(_buffer) - returns (CBOR memory _value) - { - uint8 _initialByte; - uint8 _majorType = 255; - uint8 _additionalInformation; - uint64 _tag = UINT64_MAX; - uint256 _len = 0; - - WitnetBuffer.Buffer memory _newBuffer = _buffer.fork(); - bool _isTagged = true; - while (_isTagged) { - // Extract basic CBOR properties from input bytes - _initialByte = _newBuffer.readUint8(); - _len ++; - _majorType = _initialByte >> 5; - _additionalInformation = _initialByte & 0x1f; - // Early CBOR tag parsing. - if (_majorType == MAJOR_TYPE_TAG) { - uint _cursor = _newBuffer.cursor; - _tag = readLength(_newBuffer, _additionalInformation); - _len += _newBuffer.cursor - _cursor; - - } else { - _isTagged = false; - } - } - if (_majorType > MAJOR_TYPE_CONTENT_FREE) { - revert UnsupportedMajorType(_majorType); - } - _value.buffer = _newBuffer; - _value.initialByte = _initialByte; - _value.majorType = _majorType; - _value.additionalInformation = _additionalInformation; - _value.len = uint64(_len); - _value.tag = _tag; - } - - /// Read the length of a CBOR indifinite-length item (arrays, maps, byte strings and text) from a buffer, consuming - /// as many bytes as specified by the first byte. - function _readIndefiniteStringLength( - WitnetBuffer.Buffer memory _buffer, - uint8 _majorType - ) - private pure - returns (uint64 _length) - { - uint8 _initialByte = _buffer.readUint8(); - if (_initialByte == 0xff) { - return UINT64_MAX; - } - _length = readLength( - _buffer, - _initialByte & 0x1f - ); - if (_length >= UINT64_MAX) { - revert InvalidLengthEncoding(_length); - } else if (_majorType != (_initialByte >> 5)) { - revert UnexpectedMajorType((_initialByte >> 5), _majorType); - } - } - - function _seekNext(WitnetCBOR.CBOR memory _cbor) - private pure - returns (WitnetCBOR.CBOR memory) - { - if (_cbor.majorType == MAJOR_TYPE_INT || _cbor.majorType == MAJOR_TYPE_NEGATIVE_INT) { - readInt(_cbor); - } else if (_cbor.majorType == MAJOR_TYPE_BYTES) { - readBytes(_cbor); - } else if (_cbor.majorType == MAJOR_TYPE_STRING) { - readString(_cbor); - } else if (_cbor.majorType == MAJOR_TYPE_ARRAY) { - CBOR[] memory _items = readArray(_cbor); - _cbor = _items[_items.length - 1]; - } else { - revert UnsupportedMajorType(_cbor.majorType); - } - return _cbor; - } - - function _skipArray(CBOR memory _cbor) - private pure - isMajorType(_cbor, MAJOR_TYPE_ARRAY) - returns (CBOR memory _next) - { - _next = _valueFromForkedBuffer(_cbor.buffer); - CBOR[] memory _items = readArray(_cbor); - if (_items.length == 0) { - revert EmptyArray(); - } - } - - function _skipBytes(CBOR memory _cbor) + function fork(WitnetCBOR.CBOR memory self) internal pure - isMajorType(_cbor, MAJOR_TYPE_BYTES) - returns (CBOR memory) - { - _cbor.len = readLength(_cbor.buffer, _cbor.additionalInformation); - if (_cbor.len < UINT32_MAX) { - _cbor.buffer.seek(_cbor.len); - return _valueFromForkedBuffer(_cbor.buffer); - } - // TODO: support skipping indefitine length bytes array - revert InvalidLengthEncoding(_cbor.len); - } - - function _skipInt(CBOR memory _cbor) - private pure - returns (CBOR memory _next) - { - if (_cbor.majorType == MAJOR_TYPE_INT || _cbor.majorType == MAJOR_TYPE_NEGATIVE_INT) { - _next = _valueFromForkedBuffer(_cbor.buffer); - } else { - revert UnexpectedMajorType(_cbor.majorType, 1); - } - } - - function _skipPrimitive(CBOR memory _cbor) - private pure - isMajorType(_cbor, MAJOR_TYPE_CONTENT_FREE) returns (WitnetCBOR.CBOR memory) { - if (_cbor.additionalInformation == 25) { - _cbor.buffer.seek(2); - - } else if ( - _cbor.additionalInformation != 20 - && _cbor.additionalInformation != 21 - ) { - revert UnsupportedPrimitive(_cbor.additionalInformation); - } - return _valueFromForkedBuffer(_cbor.buffer); - } - - function _skipText(CBOR memory _cbor) - internal pure - isMajorType(_cbor, MAJOR_TYPE_STRING) - returns (CBOR memory) - { - _cbor.len = readLength(_cbor.buffer, _cbor.additionalInformation); - if (_cbor.len < UINT64_MAX) { - _cbor.buffer.seek(_cbor.len); - return valueFromBuffer(_cbor.buffer); - } - // TODO: support skipping indefitine length text array - revert InvalidLengthEncoding(_cbor.len); + return CBOR({ + buffer: self.buffer.fork(), + initialByte: self.initialByte, + majorType: self.majorType, + additionalInformation: self.additionalInformation, + len: self.len, + tag: self.tag + }); } - function next(CBOR memory self) + function settle(CBOR memory self) internal pure + returns (WitnetCBOR.CBOR memory) { - uint8 _initialByte; - uint8 _majorType = 255; - uint8 _additionalInformation; - uint64 _tag = UINT64_MAX; - uint256 _len = 0; - bool _isTagged = true; - while (_isTagged) { - // Extract basic CBOR properties from input bytes - _initialByte = self.buffer.readUint8(); - _len ++; - _majorType = _initialByte >> 5; - _additionalInformation = _initialByte & 0x1f; - // Early CBOR tag parsing. - if (_majorType == MAJOR_TYPE_TAG) { - uint _cursor = self.buffer.cursor; - _tag = readLength(self.buffer, _additionalInformation); - _len += self.buffer.cursor - _cursor; - - } else { - _isTagged = false; - } - } - if (_majorType > MAJOR_TYPE_CONTENT_FREE) { - revert UnsupportedMajorType(_majorType); + if (!self.eof()) { + return fromBuffer(self.buffer); + } else { + return self; } - self.initialByte = _initialByte; - self.majorType = _majorType; - self.additionalInformation = _additionalInformation; - self.len = uint64(_len); - self.tag = _tag; } function skip(CBOR memory self) internal pure + returns (WitnetCBOR.CBOR memory) { if ( self.majorType == MAJOR_TYPE_INT || self.majorType == MAJOR_TYPE_NEGATIVE_INT ) { - self.buffer.cursor += self.length(); + self.buffer.cursor += self.peekLength(); } else if ( self.majorType == MAJOR_TYPE_STRING || self.majorType == MAJOR_TYPE_BYTES ) { - self.buffer.cursor += readLength(self.buffer, self.additionalInformation); + uint64 len = readLength(self.buffer, self.additionalInformation); + self.buffer.cursor += len; } else if ( self.majorType == MAJOR_TYPE_ARRAY ) { - readLength(self.buffer, self.additionalInformation); - } else if ( - self.majorType != MAJOR_TYPE_CONTENT_FREE - ) { + self.len = readLength(self.buffer, self.additionalInformation); + // } else if ( + // self.majorType == MAJOR_TYPE_CONTENT_FREE + // ) { + // TODO + } else { revert UnsupportedMajorType(self.majorType); } - if (!self.eof()) { - self.next(); - } + return self; } - function length(CBOR memory self) + function peekLength(CBOR memory self) internal pure returns (uint64) { + assert(1 << 0 == 1); if (self.additionalInformation < 24) { return self.additionalInformation; } else if (self.additionalInformation > 27) { @@ -357,103 +194,120 @@ library WitnetCBOR { } } + // event Array(uint cursor, uint items); + // event Log2(uint index, bytes data, uint cursor, uint major, uint addinfo, uint len); function readArray(CBOR memory self) internal pure isMajorType(self, MAJOR_TYPE_ARRAY) returns (CBOR[] memory items) { uint64 len = readLength(self.buffer, self.additionalInformation); + // emit Array(self.buffer.cursor, len); items = new CBOR[](len + 1); - for (uint _ix = 0; _ix < len; _ix ++) { - items[_ix] = _valueFromForkedBuffer(self.buffer); - self.buffer.cursor = items[_ix].buffer.cursor; - self.majorType = items[_ix].majorType; - self.additionalInformation = items[_ix].additionalInformation; - self = _seekNext(self); + for (uint ix = 0; ix < len; ix ++) { + items[ix] = self.fork().settle(); + // emit Log2( + // ix, + // items[ix].buffer.data, + // items[ix].buffer.cursor, + // items[ix].majorType, + // items[ix].additionalInformation, + // items[ix].len + // ); + self.buffer.cursor = items[ix].buffer.cursor; + self.majorType = items[ix].majorType; + self.additionalInformation = items[ix].additionalInformation; + self.len = items[ix].len; + if (self.majorType == MAJOR_TYPE_ARRAY) { + CBOR[] memory subitems = self.readArray(); + self = subitems[subitems.length - 1]; + } else { + self.skip(); + } } items[len] = self; } - /// Reads the length of the next CBOR item from a buffer, consuming a different number of bytes depending on the + /// Reads the length of the settle CBOR item from a buffer, consuming a different number of bytes depending on the /// value of the `additionalInformation` argument. function readLength( - WitnetBuffer.Buffer memory _buffer, - uint8 _additionalInformation + WitnetBuffer.Buffer memory buffer, + uint8 additionalInformation ) internal pure returns (uint64) { - if (_additionalInformation < 24) { - return _additionalInformation; + if (additionalInformation < 24) { + return additionalInformation; } - if (_additionalInformation == 24) { - return _buffer.readUint8(); + if (additionalInformation == 24) { + return buffer.readUint8(); } - if (_additionalInformation == 25) { - return _buffer.readUint16(); + if (additionalInformation == 25) { + return buffer.readUint16(); } - if (_additionalInformation == 26) { - return _buffer.readUint32(); + if (additionalInformation == 26) { + return buffer.readUint32(); } - if (_additionalInformation == 27) { - return _buffer.readUint64(); + if (additionalInformation == 27) { + return buffer.readUint64(); } - if (_additionalInformation == 31) { + if (additionalInformation == 31) { return UINT64_MAX; } - revert InvalidLengthEncoding(_additionalInformation); + revert InvalidLengthEncoding(additionalInformation); } /// @notice Read a `CBOR` structure into a native `bool` value. - /// @param _cbor An instance of `CBOR`. + /// @param cbor An instance of `CBOR`. /// @return The value represented by the input, as a `bool` value. - function readBool(CBOR memory _cbor) + function readBool(CBOR memory cbor) internal pure - isMajorType(_cbor, MAJOR_TYPE_CONTENT_FREE) + isMajorType(cbor, MAJOR_TYPE_CONTENT_FREE) returns (bool) { - if (_cbor.additionalInformation == 20) { + if (cbor.additionalInformation == 20) { return false; - } else if (_cbor.additionalInformation == 21) { + } else if (cbor.additionalInformation == 21) { return true; } else { - revert UnsupportedPrimitive(_cbor.additionalInformation); + revert UnsupportedPrimitive(cbor.additionalInformation); } } /// @notice Decode a `CBOR` structure into a native `bytes` value. - /// @param _cbor An instance of `CBOR`. - /// @return _output The value represented by the input, as a `bytes` value. - function readBytes(CBOR memory _cbor) + /// @param cbor An instance of `CBOR`. + /// @return output The value represented by the input, as a `bytes` value. + function readBytes(CBOR memory cbor) internal pure - isMajorType(_cbor, MAJOR_TYPE_BYTES) - returns (bytes memory _output) + isMajorType(cbor, MAJOR_TYPE_BYTES) + returns (bytes memory output) { - _cbor.len = readLength( - _cbor.buffer, - _cbor.additionalInformation + cbor.len = readLength( + cbor.buffer, + cbor.additionalInformation ); - if (_cbor.len == UINT32_MAX) { + if (cbor.len == UINT32_MAX) { // These checks look repetitive but the equivalent loop would be more expensive. - uint32 _length = uint32(_readIndefiniteStringLength( - _cbor.buffer, - _cbor.majorType + uint32 length = uint32(_readIndefiniteStringLength( + cbor.buffer, + cbor.majorType )); - if (_length < UINT32_MAX) { - _output = abi.encodePacked(_cbor.buffer.read(_length)); - _length = uint32(_readIndefiniteStringLength( - _cbor.buffer, - _cbor.majorType + if (length < UINT32_MAX) { + output = abi.encodePacked(cbor.buffer.read(length)); + length = uint32(_readIndefiniteStringLength( + cbor.buffer, + cbor.majorType )); - if (_length < UINT32_MAX) { - _output = abi.encodePacked( - _output, - _cbor.buffer.read(_length) + if (length < UINT32_MAX) { + output = abi.encodePacked( + output, + cbor.buffer.read(length) ); } } } else { - return _cbor.buffer.read(uint32(_cbor.len)); + return cbor.buffer.read(uint32(cbor.len)); } } @@ -461,177 +315,201 @@ library WitnetCBOR { /// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values /// by 5 decimal orders so as to get a fixed precision of 5 decimal positions, which should be OK for most `fixed16` /// use cases. In other words, the output of this method is 10,000 times the actual value, encoded into an `int32`. - /// @param _cbor An instance of `CBOR`. + /// @param cbor An instance of `CBOR`. /// @return The value represented by the input, as an `int128` value. - function readFloat16(CBOR memory _cbor) + function readFloat16(CBOR memory cbor) internal pure - isMajorType(_cbor, MAJOR_TYPE_CONTENT_FREE) + isMajorType(cbor, MAJOR_TYPE_CONTENT_FREE) returns (int32) { - if (_cbor.additionalInformation == 25) { - return _cbor.buffer.readFloat16(); + if (cbor.additionalInformation == 25) { + return cbor.buffer.readFloat16(); } else { - revert UnsupportedPrimitive(_cbor.additionalInformation); + revert UnsupportedPrimitive(cbor.additionalInformation); } } /// @notice Decode a `CBOR` structure into a native `int128[]` value whose inner values follow the same convention /// @notice as explained in `decodeFixed16`. - /// @param _cbor An instance of `CBOR`. - function readFloat16Array(CBOR memory _cbor) + /// @param cbor An instance of `CBOR`. + function readFloat16Array(CBOR memory cbor) internal pure - isMajorType(_cbor, MAJOR_TYPE_ARRAY) - returns (int32[] memory _values) + isMajorType(cbor, MAJOR_TYPE_ARRAY) + returns (int32[] memory values) { - uint64 _length = readLength(_cbor.buffer, _cbor.additionalInformation); - if (_length < UINT64_MAX) { - _values = new int32[](_length); - for (uint64 _i = 0; _i < _length; ) { - CBOR memory _item = valueFromBuffer(_cbor.buffer); - _values[_i] = readFloat16(_item); + uint64 length = readLength(cbor.buffer, cbor.additionalInformation); + if (length < UINT64_MAX) { + values = new int32[](length); + for (uint64 i = 0; i < length; ) { + CBOR memory item = fromBuffer(cbor.buffer); + values[i] = readFloat16(item); unchecked { - _i ++; + i ++; } } } else { - revert InvalidLengthEncoding(_length); + revert InvalidLengthEncoding(length); } } /// @notice Decode a `CBOR` structure into a native `int128` value. - /// @param _cbor An instance of `CBOR`. + /// @param cbor An instance of `CBOR`. /// @return The value represented by the input, as an `int128` value. - function readInt(CBOR memory _cbor) + function readInt(CBOR memory cbor) internal pure returns (int) { - if (_cbor.majorType == 1) { + if (cbor.majorType == 1) { uint64 _value = readLength( - _cbor.buffer, - _cbor.additionalInformation + cbor.buffer, + cbor.additionalInformation ); return int(-1) - int(uint(_value)); - } else if (_cbor.majorType == 0) { + } else if (cbor.majorType == 0) { // Any `uint64` can be safely casted to `int128`, so this method supports majorType 1 as well so as to have offer // a uniform API for positive and negative numbers - return int(readUint(_cbor)); + return int(readUint(cbor)); } else { - revert UnexpectedMajorType(_cbor.majorType, 1); + revert UnexpectedMajorType(cbor.majorType, 1); } } /// @notice Decode a `CBOR` structure into a native `int[]` value. - /// @param _cbor instance of `CBOR`. - /// @return _array The value represented by the input, as an `int[]` value. - function readIntArray(CBOR memory _cbor) + /// @param cbor instance of `CBOR`. + /// @return array The value represented by the input, as an `int[]` value. + function readIntArray(CBOR memory cbor) internal pure - isMajorType(_cbor, MAJOR_TYPE_ARRAY) - returns (int[] memory _array) + isMajorType(cbor, MAJOR_TYPE_ARRAY) + returns (int[] memory array) { - uint64 _length = readLength(_cbor.buffer, _cbor.additionalInformation); - if (_length < UINT64_MAX) { - _array = new int[](_length); - for (uint _i = 0; _i < _length; ) { - CBOR memory _item = valueFromBuffer(_cbor.buffer); - _array[_i] = readInt(_item); + uint64 length = readLength(cbor.buffer, cbor.additionalInformation); + if (length < UINT64_MAX) { + array = new int[](length); + for (uint i = 0; i < length; ) { + CBOR memory item = fromBuffer(cbor.buffer); + array[i] = readInt(item); unchecked { - _i ++; + i ++; } } } else { - revert InvalidLengthEncoding(_length); + revert InvalidLengthEncoding(length); } } /// @notice Decode a `CBOR` structure into a native `string` value. - /// @param _cbor An instance of `CBOR`. - /// @return _text The value represented by the input, as a `string` value. - function readString(CBOR memory _cbor) + /// @param cbor An instance of `CBOR`. + /// @return text The value represented by the input, as a `string` value. + function readString(CBOR memory cbor) internal pure - isMajorType(_cbor, MAJOR_TYPE_STRING) - returns (string memory _text) + isMajorType(cbor, MAJOR_TYPE_STRING) + returns (string memory text) { - _cbor.len = readLength(_cbor.buffer, _cbor.additionalInformation); - if (_cbor.len == UINT64_MAX) { + cbor.len = readLength(cbor.buffer, cbor.additionalInformation); + if (cbor.len == UINT64_MAX) { bool _done; while (!_done) { - uint64 _length = _readIndefiniteStringLength( - _cbor.buffer, - _cbor.majorType + uint64 length = _readIndefiniteStringLength( + cbor.buffer, + cbor.majorType ); - if (_length < UINT64_MAX) { - _text = string(abi.encodePacked( - _text, - _cbor.buffer.readText(_length / 4) + if (length < UINT64_MAX) { + text = string(abi.encodePacked( + text, + cbor.buffer.readText(length / 4) )); } else { _done = true; } } } else { - return string(_cbor.buffer.readText(_cbor.len)); + return string(cbor.buffer.readText(cbor.len)); } } /// @notice Decode a `CBOR` structure into a native `string[]` value. - /// @param _cbor An instance of `CBOR`. - /// @return _strings The value represented by the input, as an `string[]` value. - function readStringArray(CBOR memory _cbor) + /// @param cbor An instance of `CBOR`. + /// @return strings The value represented by the input, as an `string[]` value. + function readStringArray(CBOR memory cbor) internal pure - isMajorType(_cbor, MAJOR_TYPE_ARRAY) - returns (string[] memory _strings) + isMajorType(cbor, MAJOR_TYPE_ARRAY) + returns (string[] memory strings) { - uint _length = readLength(_cbor.buffer, _cbor.additionalInformation); - if (_length < UINT64_MAX) { - _strings = new string[](_length); - for (uint _i = 0; _i < _length; ) { - CBOR memory _item = valueFromBuffer(_cbor.buffer); - _strings[_i] = readString(_item); + uint length = readLength(cbor.buffer, cbor.additionalInformation); + if (length < UINT64_MAX) { + strings = new string[](length); + for (uint i = 0; i < length; ) { + CBOR memory item = fromBuffer(cbor.buffer); + strings[i] = readString(item); unchecked { - _i ++; + i ++; } } } else { - revert InvalidLengthEncoding(_length); + revert InvalidLengthEncoding(length); } } /// @notice Decode a `CBOR` structure into a native `uint64` value. - /// @param _cbor An instance of `CBOR`. + /// @param cbor An instance of `CBOR`. /// @return The value represented by the input, as an `uint64` value. - function readUint(CBOR memory _cbor) + function readUint(CBOR memory cbor) internal pure - isMajorType(_cbor, MAJOR_TYPE_INT) + isMajorType(cbor, MAJOR_TYPE_INT) returns (uint) { return readLength( - _cbor.buffer, - _cbor.additionalInformation + cbor.buffer, + cbor.additionalInformation ); } /// @notice Decode a `CBOR` structure into a native `uint64[]` value. - /// @param _cbor An instance of `CBOR`. - /// @return _values The value represented by the input, as an `uint64[]` value. - function readUintArray(CBOR memory _cbor) + /// @param cbor An instance of `CBOR`. + /// @return values The value represented by the input, as an `uint64[]` value. + function readUintArray(CBOR memory cbor) internal pure - isMajorType(_cbor, MAJOR_TYPE_ARRAY) - returns (uint[] memory _values) + isMajorType(cbor, MAJOR_TYPE_ARRAY) + returns (uint[] memory values) { - uint64 _length = readLength(_cbor.buffer, _cbor.additionalInformation); - if (_length < UINT64_MAX) { - _values = new uint[](_length); - for (uint _ix = 0; _ix < _length; ) { - CBOR memory _item = valueFromBuffer(_cbor.buffer); - _values[_ix] = readUint(_item); + uint64 length = readLength(cbor.buffer, cbor.additionalInformation); + if (length < UINT64_MAX) { + values = new uint[](length); + for (uint ix = 0; ix < length; ) { + CBOR memory item = fromBuffer(cbor.buffer); + values[ix] = readUint(item); unchecked { - _ix ++; + ix ++; } } } else { - revert InvalidLengthEncoding(_length); + revert InvalidLengthEncoding(length); } } + + /// Read the length of a CBOR indifinite-length item (arrays, maps, byte strings and text) from a buffer, consuming + /// as many bytes as specified by the first byte. + function _readIndefiniteStringLength( + WitnetBuffer.Buffer memory buffer, + uint8 majorType + ) + private pure + returns (uint64 len) + { + uint8 initialByte = buffer.readUint8(); + if (initialByte == 0xff) { + return UINT64_MAX; + } + len = readLength( + buffer, + initialByte & 0x1f + ); + if (len >= UINT64_MAX) { + revert InvalidLengthEncoding(len); + } else if (majorType != (initialByte >> 5)) { + revert UnexpectedMajorType((initialByte >> 5), majorType); + } + } } \ No newline at end of file diff --git a/test/TestWitnetCBOR.sol b/test/TestWitnetCBOR.sol index 585cf408d..24fe980c3 100644 --- a/test/TestWitnetCBOR.sol +++ b/test/TestWitnetCBOR.sol @@ -15,8 +15,8 @@ contract TestWitnetCBOR { event Log(string _topic, bytes _value); function testBoolDecode() external { - bool decodedFalse = WitnetCBOR.valueFromBytes(hex"f4").readBool(); - bool decodedTrue = WitnetCBOR.valueFromBytes(hex"f5").readBool(); + bool decodedFalse = WitnetCBOR.fromBytes(hex"f4").readBool(); + bool decodedTrue = WitnetCBOR.fromBytes(hex"f5").readBool(); Assert.equal( decodedFalse, false, @@ -30,7 +30,7 @@ contract TestWitnetCBOR { } function helperDecodeBoolRevert() public pure { - WitnetCBOR.valueFromBytes(hex"f6").readBool(); + WitnetCBOR.fromBytes(hex"f6").readBool(); } function testBoolDecodeRevert() external { @@ -41,12 +41,12 @@ contract TestWitnetCBOR { } function testUint64DecodeDiscriminant() external { - WitnetCBOR.CBOR memory decoded = WitnetCBOR.valueFromBytes(hex"1b0020000000000000"); + WitnetCBOR.CBOR memory decoded = WitnetCBOR.fromBytes(hex"1b0020000000000000"); Assert.equal(uint(decoded.majorType), 0, "CBOR-encoded Uint64 value should be decoded into a WitnetCBOR.CBOR with major type 0"); } function testUint64DecodeValue() external { - uint64 decoded = uint64(WitnetCBOR.valueFromBytes(hex"1b0020000000000000").readUint()); + uint64 decoded = uint64(WitnetCBOR.fromBytes(hex"1b0020000000000000").readUint()); Assert.equal( uint(decoded), 9007199254740992, @@ -55,12 +55,12 @@ contract TestWitnetCBOR { } function testInt128DecodeDiscriminant() external { - WitnetCBOR.CBOR memory decoded = WitnetCBOR.valueFromBytes(hex"3bfffffffffffffffe"); + WitnetCBOR.CBOR memory decoded = WitnetCBOR.fromBytes(hex"3bfffffffffffffffe"); Assert.equal(uint(decoded.majorType), 1, "CBOR-encoded Int128 value should be decoded into a WitnetCBOR.CBOR with major type 1"); } function testInt128DecodeValue() external { - int128 decoded = int128(WitnetCBOR.valueFromBytes(hex"3bfffffffffffffffe").readInt()); + int128 decoded = int128(WitnetCBOR.fromBytes(hex"3bfffffffffffffffe").readInt()); Assert.equal( int(decoded), -18446744073709551615, @@ -69,29 +69,29 @@ contract TestWitnetCBOR { } function testInt128DecodeZeroValue() external { - int128 decoded = int128(WitnetCBOR.valueFromBytes(hex"00").readInt()); + int128 decoded = int128(WitnetCBOR.fromBytes(hex"00").readInt()); Assert.equal(int(decoded), 0, "CBOR-encoded Int128 value should be decoded into a WitnetCBOR.CBOR containing the correct Uint64 value"); } function testBytes0DecodeDiscriminant() external { - WitnetCBOR.CBOR memory decoded = WitnetCBOR.valueFromBytes(hex"40"); + WitnetCBOR.CBOR memory decoded = WitnetCBOR.fromBytes(hex"40"); Assert.equal(uint(decoded.majorType), 2, "Empty CBOR-encoded Bytes value should be decoded into a WitnetCBOR.CBOR with major type 2"); } function testBytes0DecodeValue() external { bytes memory encoded = hex"40"; - bytes memory decoded = WitnetCBOR.valueFromBytes(encoded).readBytes(); + bytes memory decoded = WitnetCBOR.fromBytes(encoded).readBytes(); Assert.equal(decoded.length, 0, "Empty CBOR-encoded Bytes value should be decoded into an empty WitnetCBOR.CBOR containing an empty bytes value"); } function testBytes4BDecodeDiscriminant() external { - WitnetCBOR.CBOR memory decoded = WitnetCBOR.valueFromBytes(hex"4401020304"); + WitnetCBOR.CBOR memory decoded = WitnetCBOR.fromBytes(hex"4401020304"); Assert.equal(uint(decoded.majorType), 2, "CBOR-encoded Bytes value should be decoded into a WitnetCBOR.CBOR with major type 2"); } function testBytes4DecodeValue() external { bytes memory encoded = hex"4401020304"; - bytes memory decoded = WitnetCBOR.valueFromBytes(encoded).readBytes(); + bytes memory decoded = WitnetCBOR.fromBytes(encoded).readBytes(); bytes memory expected = abi.encodePacked( uint8(1), uint8(2), @@ -123,7 +123,7 @@ contract TestWitnetCBOR { function testBytes32DecodeValueFrom31bytes() external { bytes memory encoded = hex"581f01020304050607080910111213141516171819202122232425262728293031"; - bytes32 decoded = WitnetLib.toBytes32(WitnetCBOR.readBytes(WitnetCBOR.valueFromBytes(encoded))); + bytes32 decoded = WitnetLib.toBytes32(WitnetCBOR.readBytes(WitnetCBOR.fromBytes(encoded))); bytes32 expected = 0x0102030405060708091011121314151617181920212223242526272829303100; Assert.equal( decoded, @@ -134,7 +134,7 @@ contract TestWitnetCBOR { function testBytes32DecodeValueFrom32bytes() external { bytes memory encoded = hex"58200102030405060708091011121314151617181920212223242526272829303132"; - bytes32 decoded = WitnetLib.toBytes32(WitnetCBOR.readBytes(WitnetCBOR.valueFromBytes(encoded))); + bytes32 decoded = WitnetLib.toBytes32(WitnetCBOR.readBytes(WitnetCBOR.fromBytes(encoded))); bytes32 expected = 0x0102030405060708091011121314151617181920212223242526272829303132; Assert.equal( decoded, @@ -145,7 +145,7 @@ contract TestWitnetCBOR { function testBytes32DecodeValueFrom33bytes() external { bytes memory encoded = hex"5821010203040506070809101112131415161718192021222324252627282930313233"; - bytes32 decoded = WitnetLib.toBytes32(WitnetCBOR.readBytes(WitnetCBOR.valueFromBytes(encoded))); + bytes32 decoded = WitnetLib.toBytes32(WitnetCBOR.readBytes(WitnetCBOR.fromBytes(encoded))); bytes32 expected = 0x0102030405060708091011121314151617181920212223242526272829303132; Assert.equal( decoded, @@ -155,7 +155,7 @@ contract TestWitnetCBOR { } function testStringDecodeDiscriminant() external { - WitnetCBOR.CBOR memory decoded = WitnetCBOR.valueFromBytes(hex"6449455446"); + WitnetCBOR.CBOR memory decoded = WitnetCBOR.fromBytes(hex"6449455446"); Assert.equal( uint(decoded.majorType), 3, @@ -165,7 +165,7 @@ contract TestWitnetCBOR { function testStringDecodeValue() external { bytes memory encoded = hex"6449455446"; - string memory decoded = WitnetCBOR.valueFromBytes(encoded).readString(); + string memory decoded = WitnetCBOR.fromBytes(encoded).readString(); string memory expected = "IETF"; Assert.equal( decoded, @@ -175,7 +175,7 @@ contract TestWitnetCBOR { } function testFloatDecodeDiscriminant() external { - WitnetCBOR.CBOR memory decoded = WitnetCBOR.valueFromBytes(hex"f90001"); + WitnetCBOR.CBOR memory decoded = WitnetCBOR.fromBytes(hex"f90001"); Assert.equal( uint(decoded.majorType), 7, @@ -185,7 +185,7 @@ contract TestWitnetCBOR { function testFloatDecodeSmallestSubnormal() external { bytes memory encoded = hex"f90001"; - int32 decoded = WitnetCBOR.valueFromBytes(encoded).readFloat16(); + int32 decoded = WitnetCBOR.fromBytes(encoded).readFloat16(); int32 expected = 0; Assert.equal( decoded, @@ -196,7 +196,7 @@ contract TestWitnetCBOR { function testFloatDecodeLargestSubnormal() external { bytes memory encoded = hex"f903ff"; - int32 decoded = WitnetCBOR.valueFromBytes(encoded).readFloat16(); + int32 decoded = WitnetCBOR.fromBytes(encoded).readFloat16(); int32 expected = 0; Assert.equal( decoded, @@ -207,7 +207,7 @@ contract TestWitnetCBOR { function testFloatDecodeSmallestPositiveNormal() external { bytes memory encoded = hex"f90400"; - int32 decoded = WitnetCBOR.valueFromBytes(encoded).readFloat16(); + int32 decoded = WitnetCBOR.fromBytes(encoded).readFloat16(); int32 expected = 0; Assert.equal( decoded, @@ -218,7 +218,7 @@ contract TestWitnetCBOR { function testFloatDecodeLargestNormal() external { bytes memory encoded = hex"f97bff"; - int32 decoded = WitnetCBOR.valueFromBytes(encoded).readFloat16(); + int32 decoded = WitnetCBOR.fromBytes(encoded).readFloat16(); int32 expected = 655040000; Assert.equal( decoded, @@ -229,7 +229,7 @@ contract TestWitnetCBOR { function testFloatDecodeLargestLessThanOne() external { bytes memory encoded = hex"f93bff"; - int32 decoded = WitnetCBOR.valueFromBytes(encoded).readFloat16(); + int32 decoded = WitnetCBOR.fromBytes(encoded).readFloat16(); int32 expected = 9995; Assert.equal( decoded, @@ -240,7 +240,7 @@ contract TestWitnetCBOR { function testFloatDecodeOne() external { bytes memory encoded = hex"f93c00"; - int32 decoded = WitnetCBOR.valueFromBytes(encoded).readFloat16(); + int32 decoded = WitnetCBOR.fromBytes(encoded).readFloat16(); int32 expected = 10000; Assert.equal( decoded, @@ -251,7 +251,7 @@ contract TestWitnetCBOR { function testFloatDecodeSmallestGreaterThanOne() external { bytes memory encoded = hex"f93c01"; - int32 decoded = WitnetCBOR.valueFromBytes(encoded).readFloat16(); + int32 decoded = WitnetCBOR.fromBytes(encoded).readFloat16(); int32 expected = 10009; Assert.equal( decoded, @@ -262,7 +262,7 @@ contract TestWitnetCBOR { function testFloatDecodeOneThird() external { bytes memory encoded = hex"f93555"; - int32 decoded = WitnetCBOR.valueFromBytes(encoded).readFloat16(); + int32 decoded = WitnetCBOR.fromBytes(encoded).readFloat16(); int32 expected = 3332; Assert.equal( decoded, @@ -273,7 +273,7 @@ contract TestWitnetCBOR { function testFloatDecodeMinusTwo() external { bytes memory encoded = hex"f9c000"; - int32 decoded = WitnetCBOR.valueFromBytes(encoded).readFloat16(); + int32 decoded = WitnetCBOR.fromBytes(encoded).readFloat16(); int32 expected = -20000; Assert.equal( decoded, @@ -284,7 +284,7 @@ contract TestWitnetCBOR { function testFloatDecodeZero() external { bytes memory encoded = hex"f90000"; - int32 decoded = WitnetCBOR.valueFromBytes(encoded).readFloat16(); + int32 decoded = WitnetCBOR.fromBytes(encoded).readFloat16(); int32 expected = 0; Assert.equal( decoded, @@ -295,7 +295,7 @@ contract TestWitnetCBOR { function testFloatDecodeMinusZero() external { bytes memory encoded = hex"f98000"; - int32 decoded = WitnetCBOR.valueFromBytes(encoded).readFloat16(); + int32 decoded = WitnetCBOR.fromBytes(encoded).readFloat16(); int32 expected = 0; Assert.equal( decoded, @@ -306,7 +306,7 @@ contract TestWitnetCBOR { function testUintArrayDecode() external { bytes memory encoded = hex"840102031a002fefd8"; - uint[] memory decoded = WitnetCBOR.valueFromBytes(encoded).readUintArray(); + uint[] memory decoded = WitnetCBOR.fromBytes(encoded).readUintArray(); uint[4] memory expected = [ uint(1), uint(2), @@ -337,7 +337,7 @@ contract TestWitnetCBOR { function testIntArrayDecode() external { bytes memory encoded = hex"840121033a002fefd7"; - int[] memory decoded = WitnetCBOR.valueFromBytes(encoded).readIntArray(); + int[] memory decoded = WitnetCBOR.fromBytes(encoded).readIntArray(); int[4] memory expected = [ int(1), int(-2), @@ -368,7 +368,7 @@ contract TestWitnetCBOR { function testFixed16ArrayDecode() external { bytes memory encoded = hex"84f93c80f9c080f94290f9C249"; - int32[] memory decoded = WitnetCBOR.valueFromBytes(encoded).readFloat16Array(); + int32[] memory decoded = WitnetCBOR.fromBytes(encoded).readFloat16Array(); int32[4] memory expected = [ int32(11250), int32(-22500), @@ -399,7 +399,7 @@ contract TestWitnetCBOR { function testStringArrayDecode() external { bytes memory encoded = hex"846548656c6c6f6d646563656e7472616c697a656465776f726c646121"; - string[] memory decoded = WitnetCBOR.valueFromBytes(encoded).readStringArray(); + string[] memory decoded = WitnetCBOR.fromBytes(encoded).readStringArray(); string[4] memory expected = [ "Hello", "decentralized", From ab6cd8b57545edc0f5535271916cbcd03fe19972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 7 Dec 2022 09:13:52 +0100 Subject: [PATCH 028/119] feat(libs): move encoding methods to new deployable lib --- contracts/libs/WitnetEncodingLib.sol | 607 +++++++++++++++++++++++++++ contracts/libs/WitnetLib.sol | 228 +--------- test/TestWitnetLib.sol | 14 - 3 files changed, 625 insertions(+), 224 deletions(-) create mode 100644 contracts/libs/WitnetEncodingLib.sol diff --git a/contracts/libs/WitnetEncodingLib.sol b/contracts/libs/WitnetEncodingLib.sol new file mode 100644 index 000000000..6e8fc1d1d --- /dev/null +++ b/contracts/libs/WitnetEncodingLib.sol @@ -0,0 +1,607 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.0 <0.9.0; + +import "./WitnetLib.sol"; +import "solidity-stringutils/src/strings.sol"; + +/// @title A library for decoding Witnet request results +/// @notice The library exposes functions to check the Witnet request success. +/// and retrieve Witnet results from CBOR values into solidity types. +/// @author The Witnet Foundation. +library WitnetEncodingLib { + + using strings for string; + using strings for strings.slice; + using WitnetBuffer for WitnetBuffer.Buffer; + using WitnetCBOR for WitnetCBOR.CBOR; + using WitnetCBOR for WitnetCBOR.CBOR[]; + + bytes internal constant WITNET_RADON_OPCODES_RESULT_TYPES = + hex"10ffffffffffffffffffffffffffffff0401ff010203050406071311ff01ffff07ff02ffffffffffffffffffffffffff0703ffffffffffffffffffffffffffff0405070202ff04040404ffffffffffff05070402040205050505ff04ff04ffffff010203050406070101ffffffffffff02ff050404000106060707ffffffffff"; + // 10ffffffffffffffffffffffffffffff + // 0401ff000203050406070100ff01ffff + // 07ff02ffffffffffffffffffffffffff + // 0703ffffffffffffffffffffffffffff + // 0405070202ff04040404ffffffffffff + // 05070402040205050505ff04ff04ffff + // ff010203050406070101ffffffffffff + // 02ff050404000106060707ffffffffff + + bytes internal constant URL_HOST_XALPHAS_CHARS = + hex"000000000000000000000000000000000000000000000000000000000000000000ffff00ffffffffffffffff00ff0000ffffffffffffffffffff00ff00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff0000ff00ffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + bytes internal constant URL_PATH_XALPHAS_CHARS = + hex"000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff0000ff00ffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + bytes internal constant URL_QUERY_XALPHAS_CHARS = + hex"000000000000000000000000000000000000000000000000000000000000000000ffff00ffffffffffffffffffffffffffffffffffffffffffffffff00ff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff0000ff00ffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + error UrlBadHostIpv4(string fqdn, string part); + error UrlBadHostPort(string fqdn, string port); + error UrlBadHostXalphas(string fqdn, string part); + error UrlBadPathXalphas(string path, uint pos); + error UrlBadQueryXalphas(string query, uint pos); + + /// =============================================================================================================== + /// --- WitnetLib internal methods -------------------------------------------------------------------------------- + + function size(WitnetV2.RadonDataTypes _type) internal pure returns (uint) { + if (_type == WitnetV2.RadonDataTypes.Integer + || _type == WitnetV2.RadonDataTypes.Float + ) { + return 9; + } else if (_type == WitnetV2.RadonDataTypes.Bool) { + return 1; + } else { + // undetermined + return 0; + } + } + + + /// =============================================================================================================== + /// --- WitnetLib public methods (if used library will have to linked to calling contracts) ----------------------- + + /// @notice Encode bytes array into given major type (UTF-8 not yet supported) + /// @param buf Bytes array + /// @return Marshaled bytes + function encode(bytes memory buf, uint majorType) + public pure + returns (bytes memory) + { + uint len = buf.length; + if (len < 23) { + return abi.encodePacked( + uint8((majorType << 5) | uint8(len)), + buf + ); + } else { + uint8 buf0 = uint8((majorType << 5)); + bytes memory buf1; + if (len <= 0xff) { + buf0 |= 24; + buf1 = abi.encodePacked(uint8(len)); + } else if (len <= 0xffff) { + buf0 |= 25; + buf1 = abi.encodePacked(uint16(len)); + } else if (len <= 0xffffffff) { + buf0 |= 26; + buf1 = abi.encodePacked(uint32(len)); + } else { + buf0 |= 27; + buf1 = abi.encodePacked(uint64(len)); + } + return abi.encodePacked( + buf0, + buf1, + buf + ); + } + } + + /// @notice Encode bytes array. + /// @param buf Bytes array + /// @return Mashaled bytes + function encode(bytes memory buf) + public pure + returns (bytes memory) + { + return encode(buf, WitnetCBOR.MAJOR_TYPE_BYTES); + } + + /// @notice Encode string array (UTF-8 not yet supported). + /// @param str String bytes. + /// @return Mashaled bytes + function encode(string memory str) + public pure + returns (bytes memory) + { + return encode(bytes(str), WitnetCBOR.MAJOR_TYPE_STRING); + } + + /// @dev Encode uint64 into tagged varint. + /// @dev See https://developers.google.com/protocol-buffers/docs/encoding#varints. + /// @param n Number + /// @param t Tag + /// @return buf Marshaled bytes + function encode(uint64 n, bytes1 t) + public pure + returns (bytes memory buf) + { + unchecked { + // Count the number of groups of 7 bits + // We need this pre-processing step since Solidity doesn't allow dynamic memory resizing + uint64 tmp = n; + uint64 numBytes = 2; + while (tmp > 0x7F) { + tmp = tmp >> 7; + numBytes += 1; + } + buf = new bytes(numBytes); + tmp = n; + buf[0] = t; + for (uint64 i = 1; i < numBytes; i++) { + // Set the first bit in the byte for each group of 7 bits + buf[i] = bytes1(0x80 | uint8(tmp & 0x7F)); + tmp = tmp >> 7; + } + // Unset the first bit of the last byte + buf[numBytes - 1] &= 0x7F; + } + } + + function encode(WitnetV2.DataSource memory source) + public pure + returns (bytes memory) + { + bytes memory _encodedMethod = encode(uint64(source.method), bytes1(0x08)); + bytes memory _encodedUrl; + if (bytes(source.url).length > 0) { + _encodedUrl = abi.encodePacked( + encode(uint64(bytes(source.url).length), bytes1(0x12)), + bytes(source.url) + ); + } + bytes memory _encodedScript; + if (source.script.length > 0) { + _encodedScript = abi.encodePacked( + encode(uint64(source.script.length), bytes1(0x1a)), + source.script + ); + } + bytes memory _encodedBody; + if (bytes(source.body).length > 0) { + _encodedBody = abi.encodePacked( + encode(uint64(bytes(source.body).length), bytes1(0x22)), + bytes(source.body) + ); + } + bytes memory _encodedHeaders; + if (source.headers.length > 0) { + for (uint _ix = 0; _ix < source.headers.length; _ix ++) { + bytes memory _headers = abi.encodePacked( + encode(uint64(bytes(source.headers[_ix][0]).length), bytes1(0x0a)), + bytes(source.headers[_ix][0]), + encode(uint64(bytes(source.headers[_ix][1]).length), bytes1(0x12)), + bytes(source.headers[_ix][1]) + ); + _encodedHeaders = abi.encodePacked( + _encodedHeaders, + encode(uint64(_headers.length), bytes1(0x2a)), + _headers + ); + } + } + uint _innerSize = ( + _encodedMethod.length + + _encodedUrl.length + + _encodedScript.length + + _encodedBody.length + + _encodedHeaders.length + ); + return abi.encodePacked( + encode(uint64(_innerSize), bytes1(0x12)), + _encodedMethod, + _encodedUrl, + _encodedScript, + _encodedBody, + _encodedHeaders + ); + } + + function encode( + WitnetV2.DataSource[] memory sources, + string[][] memory args, + WitnetV2.RadonReducer memory aggregator, + WitnetV2.RadonReducer memory tally, + uint16 resultMaxSize + ) + public pure + returns (bytes memory bytecode) + { + bytes[] memory encodedSources; + for (uint ix = 0; ix < sources.length; ix ++) { + replaceWildcards(sources[ix], args[ix]); + encodedSources[ix] = encode(sources[ix]); + } + bytecode = abi.encodePacked( + encode(aggregator), + encode(tally), + (resultMaxSize > 0 + ? encode(uint64(resultMaxSize), 0x28) + : bytes("") + ) + ); + + } + + function encode(WitnetV2.RadonReducer memory reducer) + public pure + returns (bytes memory bytecode) + { + for (uint ix = 0; ix < reducer.filters.length; ix ++) { + bytecode = abi.encodePacked( + bytecode, + encode(reducer.filters[ix]) + ); + } + bytecode = abi.encodePacked( + bytecode, + encode(reducer.opcode) + ); + } + + function encode(WitnetV2.RadonFilter memory filter) + public pure + returns (bytes memory bytecode) + { + + bytecode = abi.encodePacked( + encode(uint64(filter.opcode), bytes1(0x08)), + filter.args.length > 0 + ? abi.encodePacked( + encode(uint64(filter.args.length), bytes1(0x12)), + filter.args + ) : bytes("") + ); + return abi.encodePacked( + encode(uint64(bytecode.length), bytes1(0x0a)), + bytecode + ); + } + + function encode(WitnetV2.RadonReducerOpcodes opcode) + public pure + returns (bytes memory) + { + + return encode(uint64(opcode), bytes1(0x10)); + } + + function encode(WitnetV2.RadonSLA memory sla) + public pure + returns (bytes memory) + { + return abi.encodePacked( + encode(uint64(sla.witnessReward), bytes1(0x10)), + encode(uint64(sla.numWitnesses), bytes1(0x18)), + encode(uint64(sla.commitRevealFee), bytes1(0x20)), + encode(uint64(sla.minConsensusPercentage), bytes1(0x28)), + encode(uint64(sla.collateral), bytes1(0x30)) + ); + } + + function replaceCborStringsFromBytes( + bytes memory data, + string[] memory args + ) + public pure + returns (bytes memory) + { + WitnetCBOR.CBOR memory cbor = WitnetCBOR.fromBytes(data); + while (!cbor.eof()) { + if (cbor.majorType == WitnetCBOR.MAJOR_TYPE_STRING) { + _replaceCborWildcards(cbor, args); + } + cbor = cbor.skip().settle(); + } + return cbor.buffer.data; + } + + function replaceWildcards(WitnetV2.DataSource memory self, string[] memory args) + public pure + { + self.url = string (WitnetBuffer.replace(bytes(self.url), args)); + self.body = string(WitnetBuffer.replace(bytes(self.body), args)); + self.script = replaceCborStringsFromBytes(self.script, args); + } + + function validate( + WitnetV2.DataRequestMethods method, + string memory schema, + string memory host, + string memory path, + string memory query, + string memory body, + string[2][] memory headers, + bytes memory script + ) + public pure + { + if ( + method == WitnetV2.DataRequestMethods.Rng + && ( + bytes(schema).length != 0 + || headers.length != 0 + || bytes(body).length != 0 + || script.length != 0 + ) + || ( + method == WitnetV2.DataRequestMethods.HttpGet + || method == WitnetV2.DataRequestMethods.HttpPost + ) && ( + headers[0].length != headers[1].length + || ( + keccak256(bytes(schema)) != keccak256(bytes("https://")) + && keccak256(bytes(schema)) != keccak256(bytes("http://")) + ) + ) + || method == WitnetV2.DataRequestMethods.Unknown + || uint8(method) > uint8(WitnetV2.DataRequestMethods.HttpPost) + ) { + revert WitnetV2.UnsupportedDataRequestMethod( + uint8(method), + schema, + body, + headers + ); + } + validateUrlHost(host); + validateUrlPath(path); + validateUrlQuery(query); + } + + function validate( + WitnetV2.RadonDataTypes dataType, + uint16 maxDataSize + ) + public pure + { + if ( + dataType == WitnetV2.RadonDataTypes.Array + || dataType == WitnetV2.RadonDataTypes.Bytes + || dataType == WitnetV2.RadonDataTypes.Map + || dataType == WitnetV2.RadonDataTypes.String + ) { + if (maxDataSize == 0 || maxDataSize > 2048) { + revert WitnetV2.UnsupportedRadonDataType( + uint8(dataType), + maxDataSize + ); + } + } else if ( + dataType != WitnetV2.RadonDataTypes.Integer + || dataType != WitnetV2.RadonDataTypes.Float + || dataType != WitnetV2.RadonDataTypes.Bool + ) { + revert WitnetV2.UnsupportedRadonDataType( + uint8(dataType), + 0 + ); + } + } + + function validate(WitnetV2.RadonFilter memory filter) + public pure + { + if ( + filter.opcode == WitnetV2.RadonFilterOpcodes.StandardDeviation + && filter.args.length == 0 + || filter.opcode != WitnetV2.RadonFilterOpcodes.Mode + ) { + revert WitnetV2.UnsupportedRadonFilter( + uint8(filter.opcode), filter.args + ); + } + } + + function validate(WitnetV2.RadonReducer memory reducer) + public pure + { + if (!( + reducer.opcode == WitnetV2.RadonReducerOpcodes.AverageMean + || reducer.opcode == WitnetV2.RadonReducerOpcodes.StandardDeviation + || reducer.opcode == WitnetV2.RadonReducerOpcodes.Mode + || reducer.opcode == WitnetV2.RadonReducerOpcodes.ConcatenateAndHash + || reducer.opcode == WitnetV2.RadonReducerOpcodes.AverageMedian + )) { + revert WitnetV2.UnsupportedRadonReducer(uint8(reducer.opcode)); + } + for (uint ix = 0; ix < reducer.filters.length; ix ++) { + validate(reducer.filters[ix]); + } + } + + function validate(WitnetV2.RadonSLA memory sla) + public pure + { + if (sla.witnessReward == 0) { + revert WitnetV2.RadonSlaNoReward(); + } + if (sla.numWitnesses == 0) { + revert WitnetV2.RadonSlaNoWitnesses(); + } else if (sla.numWitnesses > 127) { + revert WitnetV2.RadonSlaTooManyWitnesses(sla.numWitnesses); + } + if ( + sla.minConsensusPercentage < 51 + || sla.minConsensusPercentage > 99 + ) { + revert WitnetV2.RadonSlaConsensusOutOfRange(sla.minConsensusPercentage); + } + if (sla.collateral < 10 ** 9) { + revert WitnetV2.RadonSlaLowCollateral(sla.collateral); + } + } + + function validateUrlHost(string memory fqdn) + public pure + { + strings.slice memory slice = fqdn.toSlice(); + strings.slice memory host = slice.split(string(":").toSlice()); + if (!_checkUrlHostPort(slice.toString())) { + revert UrlBadHostPort(fqdn, slice.toString()); + } + strings.slice memory delim = string(".").toSlice(); + string[] memory parts = new string[](host.count(delim) + 1); + if (parts.length == 1) { + revert UrlBadHostXalphas(fqdn, fqdn); + } + for (uint ix = 0; ix < parts.length; ix ++) { + parts[ix] = host.split(delim).toString(); + if (!_checkUrlHostXalphas(bytes(parts[ix]))) { + revert UrlBadHostXalphas(fqdn, parts[ix]); + } + } + if (parts.length == 4) { + bool _prevDigits = false; + for (uint ix = 4; ix > 0; ix --) { + if (_checkUrlHostIpv4(parts[ix - 1])) { + _prevDigits = true; + } else { + if (_prevDigits) { + revert UrlBadHostIpv4(fqdn, parts[ix - 1]); + } else { + break; + } + } + } + } + } + + function validateUrlPath(string memory path) public pure { + unchecked { + if (bytes(path).length > 0) { + if (bytes(path)[0] == bytes1("/")) { + revert UrlBadPathXalphas(path, 0); + } + for (uint ix = 0; ix < bytes(path).length; ix ++) { + if (URL_PATH_XALPHAS_CHARS[uint8(bytes(path)[ix])] == 0x0) { + revert UrlBadPathXalphas(path, ix); + } + } + } + } + } + + function validateUrlQuery(string memory query) public pure { + unchecked { + if (bytes(query).length > 0) { + for (uint ix = 0; ix < bytes(query).length; ix ++) { + if (URL_QUERY_XALPHAS_CHARS[uint8(bytes(query)[ix])] == 0x0) { + revert UrlBadQueryXalphas(query, ix); + } + } + } + } + } + + function verifyRadonScript(bytes memory script) + public pure + returns (WitnetV2.RadonDataTypes) + { + return _verifyRadonScript( + WitnetCBOR.fromBytes(script) + ); + } + + + /// =============================================================================================================== + /// --- WitnetLib private methods --------------------------------------------------------------------------------- + + function _checkUrlHostIpv4(string memory ipv4) + private pure + returns (bool) + { + (uint res, bool ok) = WitnetLib.tryUint(ipv4); + return (ok && res <= 255); + } + + function _checkUrlHostPort(string memory hostPort) + private pure + returns (bool) + { + (uint res, bool ok) = WitnetLib.tryUint(hostPort); + return (ok && res <= 65536); + } + + function _checkUrlHostXalphas(bytes memory xalphas) + private pure + returns (bool) + { + unchecked { + for (uint ix = 0; ix < xalphas.length; ix ++) { + if (URL_HOST_XALPHAS_CHARS[uint8(xalphas[ix])] == 0x00) { + return false; + } + } + return true; + } + } + + function _replaceCborWildcards( + WitnetCBOR.CBOR memory self, + string[] memory args + ) private pure + { + uint _rewind = self.len; + uint _start = self.buffer.cursor; + bytes memory _peeks = bytes(self.readString()); + uint _dataLength = _peeks.length + _rewind; + bytes memory _pokes = WitnetBuffer.replace(_peeks, args); + if (keccak256(_pokes) != keccak256(bytes(_peeks))) { + self.buffer.cursor = _start - _rewind; + self.buffer.mutate( + _dataLength, + encode(string(_pokes)) + ); + } + self.buffer.cursor = _start; + } + + function _verifyRadonScript(WitnetCBOR.CBOR memory self) + private pure + returns (WitnetV2.RadonDataTypes) + { + if (self.majorType == WitnetCBOR.MAJOR_TYPE_ARRAY) { + WitnetCBOR.CBOR[] memory items = self.readArray(); + if (items.length > 1) { + return _verifyRadonScript(items[items.length - 2]); + } else { + return WitnetV2.RadonDataTypes.Any; + } + } else if (self.majorType == WitnetCBOR.MAJOR_TYPE_INT) { + uint cursor = self.buffer.cursor; + uint opcode = self.readUint(); + uint8 dataType = (opcode > WITNET_RADON_OPCODES_RESULT_TYPES.length + ? 0xff + : uint8(WITNET_RADON_OPCODES_RESULT_TYPES[opcode]) + ); + if (dataType > uint8(type(WitnetV2.RadonDataTypes).max)) { + revert WitnetV2.UnsupportedRadonScriptOpcode( + self.buffer.data, + cursor, + uint8(opcode) + ); + } + return WitnetV2.RadonDataTypes(dataType); + } else { + revert WitnetCBOR.UnexpectedMajorType( + WitnetCBOR.MAJOR_TYPE_INT, + self.majorType + ); + } + } + +} \ No newline at end of file diff --git a/contracts/libs/WitnetLib.sol b/contracts/libs/WitnetLib.sol index e2fe8fe45..08fdb1cc4 100644 --- a/contracts/libs/WitnetLib.sol +++ b/contracts/libs/WitnetLib.sol @@ -18,19 +18,6 @@ library WitnetLib { /// =============================================================================================================== /// --- WitnetLib internal methods -------------------------------------------------------------------------------- - function size(WitnetV2.RadonDataTypes _type) internal pure returns (uint) { - if (_type == WitnetV2.RadonDataTypes.Integer - || _type == WitnetV2.RadonDataTypes.Float - ) { - return 9; - } else if (_type == WitnetV2.RadonDataTypes.Bool) { - return 1; - } else { - // undetermined - return 0; - } - } - function toAddress(bytes memory _value) internal pure returns (address) { return address(toBytes20(_value)); } @@ -105,6 +92,24 @@ library WitnetLib { } } + function tryUint(string memory str) + internal pure + returns (uint res, bool) + { + unchecked { + for (uint256 i = 0; i < bytes(str).length; i++) { + if ( + (uint8(bytes(str)[i]) - 48) < 0 + || (uint8(bytes(str)[i]) - 48) > 9 + ) { + return (0, false); + } + res += (uint8(bytes(str)[i]) - 48) * 10 ** (bytes(str).length - i - 1); + } + return (res, true); + } + } + /// @notice Returns true if Witnet.Result contains an error. /// @param _result An instance of Witnet.Result. /// @return `true` if errored, `false` if successful. @@ -555,201 +560,4 @@ library WitnetLib { return _resultFromCborValue(cborValue); } - /// ----------------------------- public encoding methods --------------------------------------------------------- - - /// @notice Encode bytes array into given major type (UTF-8 not yet supported) - /// @param buf Bytes array - /// @return Marshaled bytes - function encode(bytes memory buf, uint majorType) - public pure - returns (bytes memory) - { - uint len = buf.length; - if (len < 23) { - return abi.encodePacked( - uint8((majorType << 5) | uint8(len)), - buf - ); - } else { - uint8 buf0 = uint8((majorType << 5)); - bytes memory buf1; - if (len <= 0xff) { - buf0 |= 24; - buf1 = abi.encodePacked(uint8(len)); - } else if (len <= 0xffff) { - buf0 |= 25; - buf1 = abi.encodePacked(uint16(len)); - } else if (len <= 0xffffffff) { - buf0 |= 26; - buf1 = abi.encodePacked(uint32(len)); - } else { - buf0 |= 27; - buf1 = abi.encodePacked(uint64(len)); - } - return abi.encodePacked( - buf0, - buf1, - buf - ); - } - } - - /// @notice Encode bytes array. - /// @param buf Bytes array - /// @return Mashaled bytes - function encode(bytes memory buf) - public pure - returns (bytes memory) - { - return encode(buf, WitnetCBOR.MAJOR_TYPE_BYTES); - } - - /// @notice Encode string array (UTF-8 not yet supported). - /// @param str String bytes. - /// @return Mashaled bytes - function encode(string memory str) - public pure - returns (bytes memory) - { - return encode(bytes(str), WitnetCBOR.MAJOR_TYPE_STRING); - } - - /// @dev Encode uint64 into tagged varint. - /// @dev See https://developers.google.com/protocol-buffers/docs/encoding#varints. - /// @param n Number - /// @param t Tag - /// @return buf Marshaled bytes - function encode(uint64 n, bytes1 t) - public pure - returns (bytes memory buf) - { - unchecked { - // Count the number of groups of 7 bits - // We need this pre-processing step since Solidity doesn't allow dynamic memory resizing - uint64 tmp = n; - uint64 numBytes = 2; - while (tmp > 0x7F) { - tmp = tmp >> 7; - numBytes += 1; - } - buf = new bytes(numBytes); - tmp = n; - buf[0] = t; - for (uint64 i = 1; i < numBytes; i++) { - // Set the first bit in the byte for each group of 7 bits - buf[i] = bytes1(0x80 | uint8(tmp & 0x7F)); - tmp = tmp >> 7; - } - // Unset the first bit of the last byte - buf[numBytes - 1] &= 0x7F; - } - } - - function encode(WitnetV2.DataSource memory _dds) - public pure - returns (bytes memory) - { - assert(_dds.headers[0].length == _dds.headers[1].length); - bytes memory _encodedMethod = encode(uint64(_dds.method), bytes1(0x08)); - bytes memory _encodedUrl; - if (bytes(_dds.url).length > 0) { - _encodedUrl = abi.encodePacked( - encode(uint64(bytes(_dds.url).length), bytes1(0x12)), - bytes(_dds.url) - ); - } - bytes memory _encodedScript; - if (_dds.script.length > 0) { - _encodedScript = abi.encodePacked( - encode(uint64(_dds.script.length), bytes1(0x1a)), - _dds.script - ); - } - bytes memory _encodedBody; - if (bytes(_dds.body).length > 0) { - _encodedBody = abi.encodePacked( - encode(uint64(bytes(_dds.body).length), bytes1(0x22)) - ); - } - bytes memory _encodedHeaders; - if (_dds.headers[0].length > 0) { - bytes memory _partials; - for (uint _ix = 0; _ix < _dds.headers[0].length; ) { - _partials = abi.encodePacked( - _partials, - encode(uint64(bytes(_dds.headers[0][_ix]).length), bytes1(0x0a)), - bytes(_dds.headers[0][_ix]), - encode(uint64(bytes(_dds.headers[1][_ix]).length), bytes1(0x12)), - bytes(_dds.headers[1][_ix]) - ); - } - _encodedHeaders = abi.encodePacked( - encode(uint64(_partials.length), bytes1(0x2a)), - _partials - ); - } - uint _innerSize = ( - _encodedMethod.length - + _encodedUrl.length - + _encodedScript.length - + _encodedBody.length - + _encodedHeaders.length - ); - return abi.encodePacked( - encode(uint64(_innerSize), bytes1(0x12)), - _encodedMethod, - _encodedUrl, - _encodedScript, - _encodedBody, - _encodedHeaders - ); - } - - function encode(WitnetV2.RadonSLA memory _sla) - public pure - returns (bytes memory) - { - return abi.encodePacked( - encode(uint64(_sla.witnessReward), bytes1(0x10)), - encode(uint64(_sla.numWitnesses), bytes1(0x18)), - encode(uint64(_sla.commitRevealFee), bytes1(0x20)), - encode(uint64(_sla.minConsensusPercentage), bytes1(0x28)), - encode(uint64(_sla.collateral), bytes1(0x30)) - ); - } - - function replaceCborStringsFromBytes( - bytes memory data, - string[] memory args - ) - public pure - returns (WitnetCBOR.CBOR memory cbor) - { - cbor = WitnetCBOR.valueFromBytes(data); - while (!cbor.eof()) { - if (cbor.majorType == WitnetCBOR.MAJOR_TYPE_STRING) { - _replaceWildcards(cbor, args); - } else { - cbor.skip(); - } - } - } - - function _replaceWildcards(WitnetCBOR.CBOR memory self, string[] memory args) - private pure - { - uint _rewind = self.len; - uint _start = self.buffer.cursor; - bytes memory _currentText = bytes(self.readString()); - uint _currentCborLength = _currentText.length + _rewind; - bytes memory _newText = WitnetBuffer.replace(bytes(_currentText), args); - if (keccak256(_newText) != keccak256(bytes(_currentText))) { - bytes memory _newCborPokes = encode(string(_newText)); - self.buffer.cursor = _start - _rewind; - self.buffer.mutate(_currentCborLength, _newCborPokes); - } - self.buffer.cursor = _start; - self.skip(); - } - } \ No newline at end of file diff --git a/test/TestWitnetLib.sol b/test/TestWitnetLib.sol index 1290462c3..7def99302 100644 --- a/test/TestWitnetLib.sol +++ b/test/TestWitnetLib.sol @@ -12,20 +12,6 @@ contract TestWitnetLib { event Log(bytes data, uint256 length); - function testReplaceCborStringsFromBytes() external { - bytes memory radon = hex"861877821866646461746182186664706F6F6C821864635C305C8218571A000F4240185B"; - emit Log(radon, radon.length); - string[] memory args = new string[](1); - args[0] = "token1Price"; - WitnetCBOR.CBOR memory cbor = WitnetLib.replaceCborStringsFromBytes(radon, args); - emit Log(cbor.buffer.data, cbor.buffer.data.length); - Assert.equal( - keccak256(cbor.buffer.data), - keccak256(hex'861877821866646461746182186664706F6F6C8218646B746F6B656E3150726963658218571A000F4240185B'), - "not good :/" - ); - } - // Test decoding of `RadonError` error codes function testErrorCodes1() external { Witnet.ErrorCodes errorCodeEmpty = WitnetLib.resultFromCborBytes(hex"D82780").asErrorCode(); From 3ccc82c4f4ec0e341602c9cfe25bf1a7fe1cbb1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 7 Dec 2022 09:14:14 +0100 Subject: [PATCH 029/119] refactor(libs): no underscored vars in libs --- contracts/libs/WitnetLib.sol | 314 +++++++++++++++++------------------ 1 file changed, 156 insertions(+), 158 deletions(-) diff --git a/contracts/libs/WitnetLib.sol b/contracts/libs/WitnetLib.sol index 08fdb1cc4..f42db0cb6 100644 --- a/contracts/libs/WitnetLib.sol +++ b/contracts/libs/WitnetLib.sol @@ -111,67 +111,67 @@ library WitnetLib { } /// @notice Returns true if Witnet.Result contains an error. - /// @param _result An instance of Witnet.Result. + /// @param result An instance of Witnet.Result. /// @return `true` if errored, `false` if successful. - function failed(Witnet.Result memory _result) + function failed(Witnet.Result memory result) internal pure returns (bool) { - return !_result.success; + return !result.success; } /// @notice Returns true if Witnet.Result contains valid result. - /// @param _result An instance of Witnet.Result. + /// @param result An instance of Witnet.Result. /// @return `true` if errored, `false` if successful. - function succeeded(Witnet.Result memory _result) + function succeeded(Witnet.Result memory result) internal pure returns (bool) { - return _result.success; + return result.success; } /// =============================================================================================================== /// --- WitnetLib private methods --------------------------------------------------------------------------------- /// @notice Decode an errored `Witnet.Result` as a `uint[]`. - /// @param _result An instance of `Witnet.Result`. + /// @param result An instance of `Witnet.Result`. /// @return The `uint[]` error parameters as decoded from the `Witnet.Result`. - function _errorsFromResult(Witnet.Result memory _result) + function _errorsFromResult(Witnet.Result memory result) private pure returns(uint[] memory) { require( - failed(_result), + failed(result), "WitnetLib: no actual error" ); - return _result.value.readUintArray(); + return result.value.readUintArray(); } /// @notice Decode a CBOR value into a Witnet.Result instance. - /// @param _cborValue An instance of `Witnet.Value`. + /// @param cbor An instance of `Witnet.Value`. /// @return A `Witnet.Result` instance. - function _resultFromCborValue(WitnetCBOR.CBOR memory _cborValue) + function _resultFromCborValue(WitnetCBOR.CBOR memory cbor) private pure returns (Witnet.Result memory) { // Witnet uses CBOR tag 39 to represent RADON error code identifiers. // [CBOR tag 39] Identifiers for CBOR: https://github.com/lucas-clemente/cbor-specs/blob/master/id.md - bool success = _cborValue.tag != 39; - return Witnet.Result(success, _cborValue); + bool success = cbor.tag != 39; + return Witnet.Result(success, cbor); } /// @notice Convert a stage index number into the name of the matching Witnet request stage. - /// @param _stageIndex A `uint64` identifying the index of one of the Witnet request stages. + /// @param stageIndex A `uint64` identifying the index of one of the Witnet request stages. /// @return The name of the matching stage. - function _stageName(uint64 _stageIndex) + function _stageName(uint64 stageIndex) private pure returns (string memory) { - if (_stageIndex == 0) { + if (stageIndex == 0) { return "retrieval"; - } else if (_stageIndex == 1) { + } else if (stageIndex == 1) { return "aggregation"; - } else if (_stageIndex == 2) { + } else if (stageIndex == 2) { return "tally"; } else { return "unknown"; @@ -182,256 +182,254 @@ library WitnetLib { /// =============================================================================================================== /// --- WitnetLib public methods (if used library will have to linked to calling contracts) ----------------------- - /// ----------------------------- public decoding methods --------------------------------------------------------- - - function asAddress(Witnet.Result memory _result) + function asAddress(Witnet.Result memory result) public pure returns (address) { require( - _result.success, + result.success, "WitnetLib: tried to read `address` from errored result." ); - if (_result.value.majorType == uint8(WitnetCBOR.MAJOR_TYPE_BYTES)) { - return _result.value.readBytes().toAddress(); + if (result.value.majorType == uint8(WitnetCBOR.MAJOR_TYPE_BYTES)) { + return result.value.readBytes().toAddress(); } else { revert("WitnetLib: reading address from string not yet supported."); } } /// @notice Decode a boolean value from a Witnet.Result as an `bool` value. - /// @param _result An instance of Witnet.Result. + /// @param result An instance of Witnet.Result. /// @return The `bool` decoded from the Witnet.Result. - function asBool(Witnet.Result memory _result) + function asBool(Witnet.Result memory result) public pure returns (bool) { require( - _result.success, + result.success, "WitnetLib: tried to read `bool` value from errored result." ); - return _result.value.readBool(); + return result.value.readBool(); } /// @notice Decode a bytes value from a Witnet.Result as a `bytes` value. - /// @param _result An instance of Witnet.Result. + /// @param result An instance of Witnet.Result. /// @return The `bytes` decoded from the Witnet.Result. - function asBytes(Witnet.Result memory _result) + function asBytes(Witnet.Result memory result) public pure returns(bytes memory) { require( - _result.success, + result.success, "WitnetLib: Tried to read bytes value from errored Witnet.Result" ); - return _result.value.readBytes(); + return result.value.readBytes(); } - function asBytes4(Witnet.Result memory _result) + function asBytes4(Witnet.Result memory result) public pure returns (bytes4) { - return asBytes(_result).toBytes4(); + return asBytes(result).toBytes4(); } /// @notice Decode a bytes value from a Witnet.Result as a `bytes32` value. - /// @param _result An instance of Witnet.Result. + /// @param result An instance of Witnet.Result. /// @return The `bytes32` decoded from the Witnet.Result. - function asBytes32(Witnet.Result memory _result) + function asBytes32(Witnet.Result memory result) public pure returns (bytes32) { - return asBytes(_result).toBytes32(); + return asBytes(result).toBytes32(); } /// @notice Decode an error code from a Witnet.Result as a member of `Witnet.ErrorCodes`. - /// @param _result An instance of `Witnet.Result`. - function asErrorCode(Witnet.Result memory _result) + /// @param result An instance of `Witnet.Result`. + function asErrorCode(Witnet.Result memory result) public pure returns (Witnet.ErrorCodes) { - uint[] memory _errors = _errorsFromResult(_result); - if (_errors.length == 0) { + uint[] memory errors = _errorsFromResult(result); + if (errors.length == 0) { return Witnet.ErrorCodes.Unknown; } else { - return Witnet.ErrorCodes(_errors[0]); + return Witnet.ErrorCodes(errors[0]); } } /// @notice Generate a suitable error message for a member of `Witnet.ErrorCodes` and its corresponding arguments. /// @dev WARN: Note that client contracts should wrap this function into a try-catch foreseing potential errors generated in this function - /// @param _result An instance of `Witnet.Result`. - /// @return _errorCode Decoded error code. - /// @return _errorString Decoded error message. - function asErrorMessage(Witnet.Result memory _result) + /// @param result An instance of `Witnet.Result`. + /// @return errorCode Decoded error code. + /// @return errorString Decoded error message. + function asErrorMessage(Witnet.Result memory result) public pure returns ( - Witnet.ErrorCodes _errorCode, - string memory _errorString + Witnet.ErrorCodes errorCode, + string memory errorString ) { - uint[] memory _errors = _errorsFromResult(_result); - if (_errors.length == 0) { + uint[] memory errors = _errorsFromResult(result); + if (errors.length == 0) { return ( Witnet.ErrorCodes.Unknown, "Unknown error: no error code." ); } else { - _errorCode = Witnet.ErrorCodes(_errors[0]); + errorCode = Witnet.ErrorCodes(errors[0]); } if ( - _errorCode == Witnet.ErrorCodes.SourceScriptNotCBOR - && _errors.length >= 2 + errorCode == Witnet.ErrorCodes.SourceScriptNotCBOR + && errors.length >= 2 ) { - _errorString = string(abi.encodePacked( + errorString = string(abi.encodePacked( "Source script #", - toString(uint8(_errors[1])), + toString(uint8(errors[1])), " was not a valid CBOR value" )); } else if ( - _errorCode == Witnet.ErrorCodes.SourceScriptNotArray - && _errors.length >= 2 + errorCode == Witnet.ErrorCodes.SourceScriptNotArray + && errors.length >= 2 ) { - _errorString = string(abi.encodePacked( + errorString = string(abi.encodePacked( "The CBOR value in script #", - toString(uint8(_errors[1])), + toString(uint8(errors[1])), " was not an Array of calls" )); } else if ( - _errorCode == Witnet.ErrorCodes.SourceScriptNotRADON - && _errors.length >= 2 + errorCode == Witnet.ErrorCodes.SourceScriptNotRADON + && errors.length >= 2 ) { - _errorString = string(abi.encodePacked( + errorString = string(abi.encodePacked( "The CBOR value in script #", - toString(uint8(_errors[1])), + toString(uint8(errors[1])), " was not a valid Data Request" )); } else if ( - _errorCode == Witnet.ErrorCodes.RequestTooManySources - && _errors.length >= 2 + errorCode == Witnet.ErrorCodes.RequestTooManySources + && errors.length >= 2 ) { - _errorString = string(abi.encodePacked( + errorString = string(abi.encodePacked( "The request contained too many sources (", - toString(uint8(_errors[1])), + toString(uint8(errors[1])), ")" )); } else if ( - _errorCode == Witnet.ErrorCodes.ScriptTooManyCalls - && _errors.length >= 4 + errorCode == Witnet.ErrorCodes.ScriptTooManyCalls + && errors.length >= 4 ) { - _errorString = string(abi.encodePacked( + errorString = string(abi.encodePacked( "Script #", - toString(uint8(_errors[2])), + toString(uint8(errors[2])), " from the ", - _stageName(uint8(_errors[1])), + _stageName(uint8(errors[1])), " stage contained too many calls (", - toString(uint8(_errors[3])), + toString(uint8(errors[3])), ")" )); } else if ( - _errorCode == Witnet.ErrorCodes.UnsupportedOperator - && _errors.length >= 5 + errorCode == Witnet.ErrorCodes.UnsupportedOperator + && errors.length >= 5 ) { - _errorString = string(abi.encodePacked( + errorString = string(abi.encodePacked( "Operator code 0x", - toHexString(uint8(_errors[4])), + toHexString(uint8(errors[4])), " found at call #", - toString(uint8(_errors[3])), + toString(uint8(errors[3])), " in script #", - toString(uint8(_errors[2])), + toString(uint8(errors[2])), " from ", - _stageName(uint8(_errors[1])), + _stageName(uint8(errors[1])), " stage is not supported" )); } else if ( - _errorCode == Witnet.ErrorCodes.HTTP - && _errors.length >= 3 + errorCode == Witnet.ErrorCodes.HTTP + && errors.length >= 3 ) { - _errorString = string(abi.encodePacked( + errorString = string(abi.encodePacked( "Source #", - toString(uint8(_errors[1])), + toString(uint8(errors[1])), " could not be retrieved. Failed with HTTP error code: ", - toString(uint8(_errors[2] / 100)), - toString(uint8(_errors[2] % 100 / 10)), - toString(uint8(_errors[2] % 10)) + toString(uint8(errors[2] / 100)), + toString(uint8(errors[2] % 100 / 10)), + toString(uint8(errors[2] % 10)) )); } else if ( - _errorCode == Witnet.ErrorCodes.RetrievalTimeout - && _errors.length >= 2 + errorCode == Witnet.ErrorCodes.RetrievalTimeout + && errors.length >= 2 ) { - _errorString = string(abi.encodePacked( + errorString = string(abi.encodePacked( "Source #", - toString(uint8(_errors[1])), + toString(uint8(errors[1])), " could not be retrieved because of a timeout" )); } else if ( - _errorCode == Witnet.ErrorCodes.Underflow - && _errors.length >= 5 + errorCode == Witnet.ErrorCodes.Underflow + && errors.length >= 5 ) { - _errorString = string(abi.encodePacked( + errorString = string(abi.encodePacked( "Underflow at operator code 0x", - toHexString(uint8(_errors[4])), + toHexString(uint8(errors[4])), " found at call #", - toString(uint8(_errors[3])), + toString(uint8(errors[3])), " in script #", - toString(uint8(_errors[2])), + toString(uint8(errors[2])), " from ", - _stageName(uint8(_errors[1])), + _stageName(uint8(errors[1])), " stage" )); } else if ( - _errorCode == Witnet.ErrorCodes.Overflow - && _errors.length >= 5 + errorCode == Witnet.ErrorCodes.Overflow + && errors.length >= 5 ) { - _errorString = string(abi.encodePacked( + errorString = string(abi.encodePacked( "Overflow at operator code 0x", - toHexString(uint8(_errors[4])), + toHexString(uint8(errors[4])), " found at call #", - toString(uint8(_errors[3])), + toString(uint8(errors[3])), " in script #", - toString(uint8(_errors[2])), + toString(uint8(errors[2])), " from ", - _stageName(uint8(_errors[1])), + _stageName(uint8(errors[1])), " stage" )); } else if ( - _errorCode == Witnet.ErrorCodes.DivisionByZero - && _errors.length >= 5 + errorCode == Witnet.ErrorCodes.DivisionByZero + && errors.length >= 5 ) { - _errorString = string(abi.encodePacked( + errorString = string(abi.encodePacked( "Division by zero at operator code 0x", - toHexString(uint8(_errors[4])), + toHexString(uint8(errors[4])), " found at call #", - toString(uint8(_errors[3])), + toString(uint8(errors[3])), " in script #", - toString(uint8(_errors[2])), + toString(uint8(errors[2])), " from ", - _stageName(uint8(_errors[1])), + _stageName(uint8(errors[1])), " stage" )); } else if ( - _errorCode == Witnet.ErrorCodes.BridgeMalformedRequest + errorCode == Witnet.ErrorCodes.BridgeMalformedRequest ) { - _errorString = "The structure of the request is invalid and it cannot be parsed"; + errorString = "The structure of the request is invalid and it cannot be parsed"; } else if ( - _errorCode == Witnet.ErrorCodes.BridgePoorIncentives + errorCode == Witnet.ErrorCodes.BridgePoorIncentives ) { - _errorString = "The request has been rejected by the bridge node due to poor incentives"; + errorString = "The request has been rejected by the bridge node due to poor incentives"; } else if ( - _errorCode == Witnet.ErrorCodes.BridgeOversizedResult + errorCode == Witnet.ErrorCodes.BridgeOversizedResult ) { - _errorString = "The request result length exceeds a bridge contract defined limit"; + errorString = "The request result length exceeds a bridge contract defined limit"; } else { - _errorString = string(abi.encodePacked( + errorString = string(abi.encodePacked( "Unknown error (0x", - toHexString(uint8(_errors[0])), + toHexString(uint8(errors[0])), ")" )); } return ( - _errorCode, - _errorString + errorCode, + errorString ); } @@ -439,124 +437,124 @@ library WitnetLib { /// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values. /// by 5 decimal orders so as to get a fixed precision of 5 decimal positions, which should be OK for most `fixed16`. /// use cases. In other words, the output of this method is 10,000 times the actual value, encoded into an `int32`. - /// @param _result An instance of Witnet.Result. + /// @param result An instance of Witnet.Result. /// @return The `int128` decoded from the Witnet.Result. - function asFixed16(Witnet.Result memory _result) + function asFixed16(Witnet.Result memory result) public pure returns (int32) { require( - _result.success, + result.success, "WitnetLib: tried to read `fixed16` value from errored result." ); - return _result.value.readFloat16(); + return result.value.readFloat16(); } /// @notice Decode an array of fixed16 values from a Witnet.Result as an `int32[]` array. - /// @param _result An instance of Witnet.Result. + /// @param result An instance of Witnet.Result. /// @return The `int128[]` decoded from the Witnet.Result. - function asFixed16Array(Witnet.Result memory _result) + function asFixed16Array(Witnet.Result memory result) public pure returns (int32[] memory) { require( - _result.success, + result.success, "WitnetLib: tried to read `fixed16[]` value from errored result." ); - return _result.value.readFloat16Array(); + return result.value.readFloat16Array(); } /// @notice Decode a integer numeric value from a Witnet.Result as an `int128` value. - /// @param _result An instance of Witnet.Result. + /// @param result An instance of Witnet.Result. /// @return The `int` decoded from the Witnet.Result. - function asInt(Witnet.Result memory _result) + function asInt(Witnet.Result memory result) public pure returns (int) { require( - _result.success, + result.success, "WitnetLib: tried to read `int` value from errored result." ); - return _result.value.readInt(); + return result.value.readInt(); } /// @notice Decode an array of integer numeric values from a Witnet.Result as an `int[]` array. - /// @param _result An instance of Witnet.Result. + /// @param result An instance of Witnet.Result. /// @return The `int[]` decoded from the Witnet.Result. - function asIntArray(Witnet.Result memory _result) + function asIntArray(Witnet.Result memory result) public pure returns (int[] memory) { require( - _result.success, + result.success, "WitnetLib: tried to read `int[]` value from errored result." ); - return _result.value.readIntArray(); + return result.value.readIntArray(); } /// @notice Decode a string value from a Witnet.Result as a `string` value. - /// @param _result An instance of Witnet.Result. + /// @param result An instance of Witnet.Result. /// @return The `string` decoded from the Witnet.Result. - function asString(Witnet.Result memory _result) + function asString(Witnet.Result memory result) public pure returns(string memory) { require( - _result.success, + result.success, "WitnetLib: tried to read `string` value from errored result." ); - return _result.value.readString(); + return result.value.readString(); } /// @notice Decode an array of string values from a Witnet.Result as a `string[]` value. - /// @param _result An instance of Witnet.Result. + /// @param result An instance of Witnet.Result. /// @return The `string[]` decoded from the Witnet.Result. - function asStringArray(Witnet.Result memory _result) + function asStringArray(Witnet.Result memory result) public pure returns (string[] memory) { require( - _result.success, + result.success, "WitnetLib: tried to read `string[]` value from errored result."); - return _result.value.readStringArray(); + return result.value.readStringArray(); } /// @notice Decode a natural numeric value from a Witnet.Result as a `uint` value. - /// @param _result An instance of Witnet.Result. + /// @param result An instance of Witnet.Result. /// @return The `uint` decoded from the Witnet.Result. - function asUint(Witnet.Result memory _result) + function asUint(Witnet.Result memory result) public pure returns(uint) { require( - _result.success, + result.success, "WitnetLib: tried to read `uint64` value from errored result" ); - return _result.value.readUint(); + return result.value.readUint(); } /// @notice Decode an array of natural numeric values from a Witnet.Result as a `uint[]` value. - /// @param _result An instance of Witnet.Result. + /// @param result An instance of Witnet.Result. /// @return The `uint[]` decoded from the Witnet.Result. - function asUintArray(Witnet.Result memory _result) + function asUintArray(Witnet.Result memory result) public pure returns (uint[] memory) { require( - _result.success, + result.success, "WitnetLib: tried to read `uint[]` value from errored result." ); - return _result.value.readUintArray(); + return result.value.readUintArray(); } /// @notice Decode raw CBOR bytes into a Witnet.Result instance. - /// @param _cborBytes Raw bytes representing a CBOR-encoded value. + /// @param bytecode Raw bytes representing a CBOR-encoded value. /// @return A `Witnet.Result` instance. - function resultFromCborBytes(bytes memory _cborBytes) + function resultFromCborBytes(bytes memory bytecode) public pure returns (Witnet.Result memory) { - WitnetCBOR.CBOR memory cborValue = WitnetCBOR.valueFromBytes(_cborBytes); + WitnetCBOR.CBOR memory cborValue = WitnetCBOR.fromBytes(bytecode); return _resultFromCborValue(cborValue); } From 0b93560fd3505e5e4bd15aabd314f9a4ee51a94d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 7 Dec 2022 09:15:33 +0100 Subject: [PATCH 030/119] test(libs): add unitary tests for WitnetEncodingLib --- test/TestWitnetEncodingLib.sol | 300 +++++++++++++++++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 test/TestWitnetEncodingLib.sol diff --git a/test/TestWitnetEncodingLib.sol b/test/TestWitnetEncodingLib.sol new file mode 100644 index 000000000..25206fe27 --- /dev/null +++ b/test/TestWitnetEncodingLib.sol @@ -0,0 +1,300 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.7.0 <0.9.0; +pragma experimental ABIEncoderV2; + +import "truffle/Assert.sol"; +import "../contracts/libs/WitnetEncodingLib.sol"; + +contract TestWitnetEncodingLib { + + using WitnetEncodingLib for string; + + // event Log(bytes data); + // event Log(bytes data, uint256 length); + + function testEncodeTaggedVarint() external { + bytes memory bytecode = WitnetEncodingLib.encode(10 ** 6, bytes1(0x10)); + Assert.equal( + keccak256(bytecode), + keccak256(hex"10c0843d"), + "bad encode(uint64,bytes1)" + ); + } + + function testEncodeString() external { + bytes memory bytecode = WitnetEncodingLib.encode(string("witnet")); + Assert.equal( + keccak256(bytecode), + keccak256(hex"667769746E6574"), + "bad encode(string)" + ); + } + + function testEncodeBytes() external { + bytes memory bytecode = WitnetEncodingLib.encode(bytes("witnet")); + Assert.equal( + keccak256(bytecode), + keccak256(hex"467769746E6574"), + "bad encode(bytes)" + ); + } + + function testEncodeRadonReducerOpcodes() external { + bytes memory bytecode = WitnetEncodingLib.encode( + WitnetV2.RadonReducerOpcodes.StandardDeviation + ); + Assert.equal( + keccak256(bytecode), + keccak256(hex"1007"), + "bad encode(WitnetV2.RadonReducerOpcodes)" + ); + } + + function testEncodeRadonSLA() external { + bytes memory bytecode = WitnetEncodingLib.encode( + WitnetV2.RadonSLA({ + witnessReward: 1000000, + numWitnesses: 10, + commitRevealFee: 1000000, + minConsensusPercentage: 51, + collateral: 5000000 + }) + ); + // emit Log(bytecode); + Assert.equal( + keccak256(bytecode), + keccak256(hex"10c0843d180a20c0843d283330c096b102"), + "bad encode(WitnetV2.RadonSLA)" + ); + } + + function testEncodeRadonReducer1Filter() external { + WitnetV2.RadonReducer memory reducer; + reducer.opcode = WitnetV2.RadonReducerOpcodes.Mode; + reducer.filters = new WitnetV2.RadonFilter[](1); + reducer.filters[0].opcode = WitnetV2.RadonFilterOpcodes.StandardDeviation; + reducer.filters[0].args = hex"fa40200000"; + bytes memory bytecode = WitnetEncodingLib.encode(reducer); + // emit Log(bytecode); + Assert.equal( + keccak256(bytecode), + keccak256(hex"0a0908051205fa402000001002"), + "bad encode(WitnetV2.RadonReducer)" + ); + } + + function testEncodeDataSourceUrlOnly() external { + WitnetV2.DataSource memory source; + source.method = WitnetV2.DataRequestMethods.HttpGet; + source.url = "https://data.messar.io/api/v1/assets/\\0\\/metrics/market-data?fields=market_data/price_\\1\\"; + source.script = hex"861877821866646461746182186664706f6f6c8218646b746f6b656e3150726963658218571a000f4240185b"; + bytes memory bytecode = WitnetEncodingLib.encode(source); + // emit Log(bytecode); + Assert.equal( + keccak256(bytecode), + keccak256(hex"128b010801125968747470733a2f2f646174612e6d65737361722e696f2f6170692f76312f6173736574732f5c305c2f6d6574726963732f6d61726b65742d646174613f6669656c64733d6d61726b65745f646174612f70726963655f5c315c1a2c861877821866646461746182186664706f6f6c8218646b746f6b656e3150726963658218571a000f4240185b"), + "bad encode(WitnetV2.DataSource)" + ); + } + + function testEncodeDataSourceUrlBodyHeaders() external { + WitnetV2.DataSource memory source; + source.method = WitnetV2.DataRequestMethods.HttpPost; + source.url = "https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3"; + source.body = "{\"query\":\"{pool(id:\\\"0xc2a856c3aff2110c1171b8f942256d40e980c726\\\"){token1Price}}\"}"; + source.headers = new string[2][](2); + source.headers[0] = [ "user-agent", "witnet-rust" ]; + source.headers[1] = [ "content-type", "text/html; charset=utf-8" ]; + source.script = hex"861877821866646461746182186664706f6f6c8218646b746f6b656e3150726963658218571a000f4240185b"; + bytes memory bytecode = WitnetEncodingLib.encode(source); + // emit Log(bytecode); + Assert.equal( + keccak256(bytecode), + keccak256( + hex"1285020803123a68747470733a2f2f6170692e74686567726170682e636f6d2f7375626772617068732f6e616d652f756e69737761702f756e69737761702d76331a2c861877821866646461746182186664706f6f6c8218646b746f6b656e3150726963658218571a000f4240185b22527b227175657279223a227b706f6f6c2869643a5c223078633261383536633361666632313130633131373162386639343232353664343065393830633732365c22297b746f6b656e3150726963657d7d227d2a190a0a757365722d6167656e74120b7769746e65742d727573742a280a0c636f6e74656e742d747970651218746578742f68746d6c3b20636861727365743d7574662d38"), + "bad encode(WitnetV2.DataSource)" + ); + } + + function testReplaceCborStringsFromBytes() external { + bytes memory radon = hex"861877821866646461746182186664706F6F6C821864635C305C8218571A000F4240185B"; + // emit Log(radon, radon.length); + string[] memory args = new string[](1); + args[0] = "token1Price"; + bytes memory newradon = WitnetEncodingLib.replaceCborStringsFromBytes(radon, args); + // emit Log(newradon, newradon.length); + Assert.equal( + keccak256(newradon), + keccak256(hex'861877821866646461746182186664706F6F6C8218646B746F6B656E3150726963658218571A000F4240185B'), + "not good :/" + ); + } + + function testValidateUrlHostOk1() external { + WitnetEncodingLib.validateUrlHost("witnet.io"); + } + + function testValidateUrlHostOk2() external { + WitnetEncodingLib.validateUrlHost("api.coinone.co.kr"); + } + + function testValidateUrlHostOk3() external { + WitnetEncodingLib.validateUrlHost("api.coinone.co.kr:123"); + } + + function testValidateUrlHostOk4() external { + WitnetEncodingLib.validateUrlHost("123.coinone.co.kr:123"); + } + + function testValidateUrlHostOk5() external { + WitnetEncodingLib.validateUrlHost("8.8.8.255:123"); + } + + function testValidateUrlHostNok1() external { + try WitnetEncodingLib.validateUrlHost("witnet") { + revert ("'witnet' should not be valid"); + } + catch (bytes memory reason) { + if (bytes4(reason) != bytes4(keccak256(bytes("UrlBadHostXalphas(string,string)")))) { + revert ("unexpected revert reason"); + } + } + } + + function testValidateUrlHostNok2() external { + try WitnetEncodingLib.validateUrlHost("api/coinone/co/kr") { + revert ("'api/coinone/co/kr' should not be valid"); + } + catch (bytes memory reason) { + if (bytes4(reason) != bytes4(keccak256(bytes("UrlBadHostXalphas(string,string)")))) { + revert ("unexpected revert reason"); + } + } + } + + function testValidateUrlHostNok3() external { + try WitnetEncodingLib.validateUrlHost("api.coinone.co.kr:65537") { + revert ("'api.coinone.co.kr:65537' should not be valid"); + } + catch (bytes memory reason) { + if (bytes4(reason) != bytes4(keccak256(bytes("UrlBadHostPort(string,string)")))) { + revert ("unexpected revert reason"); + } + } + } + + function testValidateUrlHostNok4() external { + try WitnetEncodingLib.validateUrlHost("api.coinone.co.kr:123:65536") { + revert ("'api.coinone.co.kr:123:65536' should not be valid"); + } + catch (bytes memory reason) { + if (bytes4(reason) != bytes4(keccak256(bytes("UrlBadHostPort(string,string)")))) { + revert ("unexpected revert reason"); + } + } + } + + function testValidateUrlHostNok5() external { + try WitnetEncodingLib.validateUrlHost("256.8.8.8:123") { + revert("'256.8.8.8:123' should not be valid"); + } catch (bytes memory reason) { + if (bytes4(reason) != bytes4(keccak256(bytes("UrlBadHostIpv4(string,string)")))) { + revert ("unexpected revert reason"); + } + } + } + + function testValidateUrlPathOk1() external { + WitnetEncodingLib.validateUrlPath(""); + } + + function testValidateUrlPathOk2() external { + WitnetEncodingLib.validateUrlPath("open/api/v2/market/ticker"); + } + + function testValidateUrlPathOk3() external { + WitnetEncodingLib.validateUrlPath("api/spot/v3/instruments/\\0\\-\\1\\/ticker"); + } + + function testValidateUrlPathOk4() external { + WitnetEncodingLib.validateUrlPath("api/spot/v3/instruments/\\0\\-\\1\\/ticker#tag"); + } + + function testValidateUrlPathOk5() external { + WitnetEncodingLib.validateUrlPath("gh/fawazahmed0/currency-api@1/latest/currencies/\\0\\.json"); + } + + function testValidateUrlPathNok1() external { + try WitnetEncodingLib.validateUrlPath("/") { + revert("'/' should not be valid"); + } catch (bytes memory reason) { + if (bytes4(reason) != bytes4(keccak256(bytes("UrlBadPathXalphas(string,uint256)")))) { + revert ("unexpected revert reason"); + } + } + } + + function testValidateUrlPathNok2() external { + try WitnetEncodingLib.validateUrlPath("?") { + revert("'?' should not be valid"); + } catch (bytes memory reason) { + if (bytes4(reason) != bytes4(keccak256(bytes("UrlBadPathXalphas(string,uint256)")))) { + revert ("unexpected revert reason"); + } + } + } + + function testValidateUrlQueryOk1() external { + WitnetEncodingLib.validateUrlQuery("chain=56"); + } + + function testValidateUrlQueryOk2() external { + WitnetEncodingLib.validateUrlQuery("symbol=\\0\\\\1\\"); + } + + function testValidateUrlQueryOk3() external { + WitnetEncodingLib.validateUrlQuery("fields=market_data/price_\\1\\"); + } + + function testValidateUrlQueryOk4() external { + WitnetEncodingLib.validateUrlQuery("from=\\0\\&to=\\1\\&lang=es&format=json"); + } + + function testValidateUrlQueryNok1() external { + try WitnetEncodingLib.validateUrlQuery("/?") { + revert("'/?' should not be valid"); + } catch (bytes memory reason) { + if (bytes4(reason) != bytes4(keccak256(bytes("UrlBadQueryXalphas(string,uint256)")))) { + revert ("unexpected revert reason"); + } + } + } + + function testValidateUrlQueryNok2() external { + try WitnetEncodingLib.validateUrlQuery("?") { + revert("'?' should not be valid"); + } catch (bytes memory reason) { + if (bytes4(reason) != bytes4(keccak256(bytes("UrlBadQueryXalphas(string,uint256)")))) { + revert ("unexpected revert reason"); + } + } + } + + function testVerifyRadonScriptOk1() external { + Assert.equal( + uint(WitnetEncodingLib.verifyRadonScript(hex"861877821866646461746182186664706f6f6c8218646b746f6b656e3150726963658218571a000f4240185b")), + uint(WitnetV2.RadonDataTypes.Integer), + "unexpected result data type" + ); + } + + function testVerifyRadonScriptOk2() external { + Assert.equal( + uint(WitnetEncodingLib.verifyRadonScript(hex"80")), + uint(WitnetV2.RadonDataTypes.Any), + "unexpected result data type" + ); + } + +} \ No newline at end of file From 94673dbf9988b2eccf2847c7e149f1ecf488e38a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 7 Dec 2022 09:16:13 +0100 Subject: [PATCH 031/119] test(migrations): avoid deploying whole stack when running tests --- migrations/scripts/1_deploy_wrb.js | 14 ++++++++++++++ migrations/scripts/3_deploy_wpr.js | 7 ++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/migrations/scripts/1_deploy_wrb.js b/migrations/scripts/1_deploy_wrb.js index bb27b0cec..dd80e55d7 100644 --- a/migrations/scripts/1_deploy_wrb.js +++ b/migrations/scripts/1_deploy_wrb.js @@ -3,6 +3,20 @@ const settings = require("../witnet.settings") const utils = require("../../scripts/utils") module.exports = async function (deployer, network, accounts) { + + if (network === "test") { + const WitnetRequestBoardTrustlessReporting1 = artifacts.require("WitnetRequestBoardTrustlessReporting1") + await deployer.deploy(WitnetRequestBoardTrustlessReporting1, true, utils.fromAscii("testing")) + const WitnetLib = artifacts.require("WitnetLib") + await deployer.deploy(WitnetLib); + const WitnetEncodingLib = artifacts.require("WitnetEncodingLib") + await deployer.deploy(WitnetEncodingLib) + const WitnetBytecodes = artifacts.require("WitnetBytecodes") + await deployer.link(WitnetEncodingLib, WitnetBytecodes) + await deployer.deploy(WitnetBytecodes, true, utils.fromAscii("testing")) + return + } + const realm = network === "test" ? "default" : utils.getRealmNetworkFromArgs()[0] diff --git a/migrations/scripts/3_deploy_wpr.js b/migrations/scripts/3_deploy_wpr.js index bbd36c3c3..d2b8b9095 100644 --- a/migrations/scripts/3_deploy_wpr.js +++ b/migrations/scripts/3_deploy_wpr.js @@ -4,9 +4,10 @@ const settings = require("../witnet.settings") const utils = require("../../scripts/utils") module.exports = async function (deployer, network, accounts) { - const realm = network === "test" - ? "default" - : utils.getRealmNetworkFromArgs()[0] + + if (network === "test") return + + const realm = utils.getRealmNetworkFromArgs()[0] const addresses = require("../witnet.addresses")[realm][network = network.split("-")[0]] const artifactsName = merge(settings.artifacts.default, settings.artifacts[realm]) From 652b13ca3d32b3ef9555cab669a702d57e377f52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 7 Dec 2022 09:17:23 +0100 Subject: [PATCH 032/119] feat(bytecodes): polish and refactor to latest libs --- contracts/data/WitnetBytecodesData.sol | 41 +- contracts/impls/bytecodes/WitnetBytecodes.sol | 567 +++++++----------- contracts/interfaces/V2/IWitnetBytecodes.sol | 89 ++- contracts/libs/WitnetV2.sol | 51 +- 4 files changed, 293 insertions(+), 455 deletions(-) diff --git a/contracts/data/WitnetBytecodesData.sol b/contracts/data/WitnetBytecodesData.sol index 7565e9060..99a49225d 100644 --- a/contracts/data/WitnetBytecodesData.sol +++ b/contracts/data/WitnetBytecodesData.sol @@ -13,13 +13,10 @@ abstract contract WitnetBytecodesData IWitnetBytecodes { - bytes32 internal constant _WITNET_BYTECODES_DATA_SLOTHASH = + bytes32 private constant _WITNET_BYTECODES_DATA_SLOTHASH = /* keccak256("io.witnet.bytecodes.data") */ 0x673359bdfd0124f9962355e7aed2d07d989b0d4bc4cbe2c94c295e0f81427dec; - bytes internal constant _WITNET_BYTECODES_RADON_OPCODES_RESULT_TYPES = - hex"00ffffffffffffffffffffffffffffff0401ff010203050406070101ff01ffff07ff02ffffffffffffffffffffffffff0703ffffffffffffffffffffffffffff05070404020205050505ff04ff04ffff0405070202ff04040404ffffffffffff010203050406070101ffffffffffffff02ff050404000106060707ffffffffff"; - struct Bytecodes { address base; address owner; @@ -30,41 +27,25 @@ abstract contract WitnetBytecodesData // ... } + struct RadonRetrieval { + WitnetV2.RadonDataTypes dataType; + uint16 dataMaxSize; + string[][] args; + bytes32[] sources; + bytes32 aggregator; + bytes32 tally; + } + struct Database { mapping (uint256 => WitnetV2.DataProvider) providers; - mapping (uint256 => mapping (uint256 => bytes32[])) providersSources; mapping (bytes32 => uint256) providersIndex; - mapping (bytes32 => bytes) reducersBytecode; - mapping (bytes32 => bytes) retrievalsBytecode; - mapping (bytes32 => bytes) slasBytecode; - - mapping (bytes32 => IWitnetBytecodes.RadonRetrieval) retrievals; mapping (bytes32 => WitnetV2.RadonReducer) reducers; + mapping (bytes32 => RadonRetrieval) retrievals; mapping (bytes32 => WitnetV2.RadonSLA) slas; mapping (bytes32 => WitnetV2.DataSource) sources; } - function _lookupOpcodeResultType(uint8 _opcode) - internal pure - returns (WitnetV2.RadonDataTypes) - { - if (_opcode >= _WITNET_BYTECODES_RADON_OPCODES_RESULT_TYPES.length) { - revert IWitnetBytecodes.UnsupportedRadonScriptOpcode(_opcode); - } else { - uint8 _resultType = uint8( - _WITNET_BYTECODES_RADON_OPCODES_RESULT_TYPES[_opcode] - ); - if (_resultType == 0xff) { - revert IWitnetBytecodes.UnsupportedRadonScriptOpcode(_opcode); - } else if (_resultType > uint8(type(WitnetV2.RadonDataTypes).max)) { - revert IWitnetBytecodes.UnsupportedRadonDataType(_resultType, 0); - } else { - return WitnetV2.RadonDataTypes(_resultType); - } - } - } - // ================================================================================================================ // --- Internal state-modifying functions ------------------------------------------------------------------------- diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol index 91461fe1e..23b5cf882 100644 --- a/contracts/impls/bytecodes/WitnetBytecodes.sol +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -7,7 +7,7 @@ import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; import "../WitnetUpgradableBase.sol"; import "../../data/WitnetBytecodesData.sol"; -import "../../libs/WitnetLib.sol"; +import "../../libs/WitnetEncodingLib.sol"; /// @title Witnet Request Board "trustless" base implementation contract. /// @notice Contract to bridge requests to Witnet Decentralized Oracle Network. @@ -20,12 +20,14 @@ contract WitnetBytecodes WitnetBytecodesData { using ERC165Checker for address; - using Witnet for bytes; - using WitnetCBOR for WitnetCBOR.CBOR; - using WitnetLib for uint64; - using WitnetLib for WitnetV2.DataSource; - using WitnetLib for WitnetV2.RadonSLA; - using WitnetLib for WitnetV2.RadonDataTypes; + + using Witnet for bytes; + using WitnetEncodingLib for WitnetV2.DataRequestMethods; + using WitnetEncodingLib for WitnetV2.DataSource; + using WitnetEncodingLib for WitnetV2.DataSource[]; + using WitnetEncodingLib for WitnetV2.RadonReducer; + using WitnetEncodingLib for WitnetV2.RadonSLA; + using WitnetEncodingLib for WitnetV2.RadonDataTypes; constructor( bool _upgradable, @@ -42,6 +44,7 @@ contract WitnetBytecodes revert("WitnetBytecodes: no transfers"); } + // ================================================================================================================ // --- Overrides IERC165 interface -------------------------------------------------------------------------------- @@ -145,21 +148,31 @@ contract WitnetBytecodes // ================================================================================================================ // --- Implementation of 'IWitnetBytecodes' ----------------------------------------------------------------------- - function bytecodeOf(bytes32 _drRetrievalHash) - external view + function bytecodeOf(bytes32 _hash) + public view override returns (bytes memory) { - return __database().retrievalsBytecode[_drRetrievalHash]; + RadonRetrieval memory _retrieval = __retrieval(_hash); + WitnetV2.DataSource[] memory _sources = new WitnetV2.DataSource[](_retrieval.sources.length); + for (uint _ix = 0; _ix < _retrieval.sources.length; _ix ++) { + _sources[_ix] = __database().sources[_retrieval.sources[_ix]]; + } + return _sources.encode( + _retrieval.args, + __database().reducers[_retrieval.aggregator], + __database().reducers[_retrieval.tally], + _retrieval.dataMaxSize + ); } - function bytecodeOf(bytes32 _drRetrievalHash, bytes32 _drSlaHash) + function bytecodeOf(bytes32 _retrievalHash, bytes32 _slaHash) external view returns (bytes memory) { return abi.encodePacked( - __database().retrievalsBytecode[_drRetrievalHash], - __database().slasBytecode[_drSlaHash] + bytecodeOf(_retrievalHash), + __database().slas[_slaHash].encode() ); } @@ -177,9 +190,15 @@ contract WitnetBytecodes function lookupDataProvider(uint256 _index) external view override - returns (WitnetV2.DataProvider memory) + returns ( + string memory _fqdn, + uint256 _totalSources + ) { - return __database().providers[_index]; + return ( + __database().providers[_index].fqdn, + __database().providers[_index].totalSources + ); } function lookupDataProviderIndex(string calldata _fqdn) @@ -190,15 +209,41 @@ contract WitnetBytecodes return __database().providersIndex[keccak256(abi.encodePacked(_fqdn))]; } - function lookupDataSource(bytes32 _drDataSourceHash) + function lookupDataProviderSources( + uint256 _index, + uint256 _offset, + uint256 _length + ) + external view + returns (bytes32[] memory _sources) + { + WitnetV2.DataProvider storage __provider = __database().providers[_index]; + if (_offset + _length > __provider.totalSources) { + revert IWitnetBytecodes.IndexOutOfBounds(_offset + _length, __provider.totalSources); + } + _sources = new bytes32[](_length); + for (uint _ix = 0; _ix < _sources.length; _ix ++) { + _sources[_ix] = __provider.sources[_ix + _offset]; + } + } + + function lookupDataSource(bytes32 _hash) external view override returns (WitnetV2.DataSource memory) { - return __database().sources[_drDataSourceHash]; + return __database().sources[_hash]; + } + + function lookupRadonReducer(bytes32 _hash) + external view + override + returns (WitnetV2.RadonReducer memory) + { + return __database().reducers[_hash]; } - function lookupRadonRetrievalAggregatorHash(bytes32 _drRetrievalHash) + function lookupRadonRetrievalAggregator(bytes32 _drRetrievalHash) external view override returns (WitnetV2.RadonReducer memory) @@ -208,27 +253,23 @@ contract WitnetBytecodes ]; } - function lookupRadonRetrievalResultMaxSize(bytes32 _drRetrievalHash) + function lookupRadonRetrievalDataMaxSize(bytes32 _drRetrievalHash) external view override returns (uint256) { - IWitnetBytecodes.RadonRetrieval storage __rr = __retrieval(_drRetrievalHash); - return (__rr.resultMaxSize > 0 - ? __rr.resultMaxSize - : __rr.resultType.size() - ); + return __retrieval(_drRetrievalHash).dataMaxSize; } - function lookupRadonRetrievalResultType(bytes32 _drRetrievalHash) + function lookupRadonRetrievalDataType(bytes32 _drRetrievalHash) external view override returns (WitnetV2.RadonDataTypes) { - return __retrieval(_drRetrievalHash).resultType; + return __retrieval(_drRetrievalHash).dataType; } - function lookupRadonRetrievalSourceHashes(bytes32 _drRetrievalHash) + function lookupRadonRetrievalSources(bytes32 _drRetrievalHash) external view override returns (bytes32[] memory) @@ -244,7 +285,7 @@ contract WitnetBytecodes return __retrieval(_drRetrievalHash).sources.length; } - function lookupRadonRetrievalTallyHash(bytes32 _drRetrievalHash) + function lookupRadonRetrievalTally(bytes32 _drRetrievalHash) external view override returns (WitnetV2.RadonReducer memory) @@ -271,174 +312,152 @@ contract WitnetBytecodes } function verifyDataSource( - WitnetV2.DataRequestMethods _method, - string memory _schema, - string memory _fqdn, - string memory _pathQuery, - string memory _body, - string[2][] memory _headers, - bytes memory _script + WitnetV2.DataRequestMethods _requestMethod, + string memory _requestSchema, + string memory _requestFQDN, + string memory _requestPath, + string memory _requestQuery, + string memory _requestBody, + string[2][] memory _requestHeaders, + bytes memory _requestRadonScript ) - public + external virtual override - returns (bytes32 _dataSourceHash) + returns (bytes32 _hash) { - if (_headers[0].length != _headers[1].length) { - revert UnsupportedDataRequestHeaders(_headers); - } - if (bytes(_fqdn).length > 0 ){ - __pushDataProvider(_fqdn); - } - WitnetV2.DataSource memory _dds = WitnetV2.DataSource({ - method: _verifyDataSourceMethod(_method, _schema), - resultType: _verifyDataSourceScript(_script), + _requestMethod.validate( + _requestSchema, + _requestFQDN, + _requestPath, + _requestQuery, + _requestBody, + _requestHeaders, + _requestRadonScript + ); + WitnetV2.DataSource memory _source = WitnetV2.DataSource({ + method: _requestMethod, + resultDataType: WitnetEncodingLib.verifyRadonScript(_requestRadonScript), url: string(abi.encodePacked( - _schema, - _fqdn, - bytes("/"), - _pathQuery - )), - body: _body, - headers: _headers, - script: _script + _requestSchema, + _requestFQDN, + bytes(_requestPath).length > 0 + ? abi.encodePacked(bytes("/"), _requestPath) + : bytes(""), + bytes(_requestQuery).length > 0 + ? abi.encodePacked("?", _requestQuery) + : bytes("") + )), + body: _requestBody, + headers: _requestHeaders, + script: _requestRadonScript }); - _dataSourceHash = keccak256(abi.encode(_dds)); - if (__database().sources[_dataSourceHash].method == WitnetV2.DataRequestMethods.Unknown) { - __database().sources[_dataSourceHash] = _dds; - emit NewDataSourceHash(_dataSourceHash, _dds.url); + _hash = keccak256(_source.encode()); + if (__database().sources[_hash].method == WitnetV2.DataRequestMethods.Unknown) { + __database().sources[_hash] = _source; + __pushDataProviderSource(_requestFQDN, _hash); + emit NewDataSourceHash(_hash); } } - function verifyRadonReducer(WitnetV2.RadonReducer calldata _ddr) - public - virtual override - returns (bytes32 _ddrHash) + function verifyRadonReducer(WitnetV2.RadonReducer memory _reducer) + external returns (bytes32 _hash) { - bytes memory _ddrBytes; - for (uint _ix = 0; _ix < _ddr.filters.length; ) { - _ddrBytes = abi.encodePacked( - _ddrBytes, - _verifyDataFilter(_ddr.filters[_ix]) - ); - unchecked { - _ix ++; - } - } - _ddrBytes = abi.encodePacked( - _ddrBytes, - _verifyRadonReducerOps(_ddr.op) - ); - _ddrHash = _ddrBytes.hash(); - if (__database().reducersBytecode[_ddrHash].length == 0) { - __database().reducersBytecode[_ddrHash] = _ddrBytes; - __database().reducers[_ddrHash] = _ddr; - emit NewDataReducerHash(_ddrHash); - } + _reducer.validate(); + bytes memory _bytecode = _reducer.encode(); + _hash = _bytecode.hash(); + WitnetV2.RadonReducer storage __reducer = __database().reducers[_hash]; + if (uint8(__reducer.opcode) == 0 && _reducer.filters.length == 0) { + __reducer.opcode = _reducer.opcode; + __pushRadonReducerFilters(__reducer, _reducer.filters); + emit NewRadonReducerHash(_hash, _bytecode); + } } - function verifyRadonRetrieval(RadonRetrieval memory _retrieval) - public + function verifyRadonRetrieval( + WitnetV2.RadonDataTypes _resultDataType, + uint16 _resultMaxVariableSize, + bytes32[] memory _sourcesHashes, + string[][] memory _sourcesArgs, + bytes32 _aggregatorHash, + bytes32 _tallyHash + ) + external virtual override - returns (bytes32 _drRetrievalHash) + returns (bytes32 _hash) { - // Check number of provided sources and template args: - if (_retrieval.sources.length == 0) { - revert RadonRetrievalNoSources(); - } - if ( _retrieval.sources.length != _retrieval.args.length) { - revert RadonRetrievalArgsMismatch(_retrieval.args); - } // Check provided result type and result max size: - if (!_verifyDataTypeMaxSize(_retrieval.resultType, _retrieval.resultMaxSize)) { - revert UnsupportedRadonDataType( - uint8(_retrieval.resultType), - _retrieval.resultMaxSize - ); + _resultDataType.validate(_resultMaxVariableSize); + + // Check that at least one source is provided; + if (_sourcesHashes.length == 0) { + revert WitnetV2.RadonRetrievalNoSources(); + } + + // Check that number of args arrays matches the number of sources: + if ( _sourcesHashes.length != _sourcesArgs.length) { + revert WitnetV2.RadonRetrievalArgsMismatch(_sourcesArgs); } - // Build data source bytecode in memory: - bytes[] memory _sourcesBytecodes = new bytes[](_retrieval.sources.length); - for (uint _ix = 0; _ix < _sourcesBytecodes.length; ) { - WitnetV2.DataSource memory _ds = __database().sources[ - _retrieval.sources[_ix] - ]; - // Check all result types of provided sources match w/ `_retrieval.resultType` - if (_ds.resultType != _retrieval.resultType) { - revert RadonRetrievalResultsMismatch( - uint8(_ds.resultType), - uint8(_retrieval.resultType) + + // Check sources and tally reducers, as those unsupported in the past + // may eventually become so: + WitnetV2.RadonReducer memory _aggregator = __database().reducers[_aggregatorHash]; + WitnetV2.RadonReducer memory _tally = __database().reducers[_tallyHash]; + _aggregator.validate(); + _tally.validate(); + + // Check result type consistency among all sources: + WitnetV2.DataSource[] memory _sources = new WitnetV2.DataSource[](_sourcesHashes.length); + for (uint _ix = 0; _ix < _sources.length; _ix ++) { + _sources[_ix] = __database().sources[_sourcesHashes[_ix]]; + // Check all sources return same Radon data type: + if (_sources[_ix].resultDataType != _resultDataType) { + revert WitnetV2.RadonRetrievalResultsMismatch( + _ix, + uint8(_sources[_ix].resultDataType), + uint8(_resultDataType) ); } - // Replace wildcards where needed: - if (_retrieval.args[_ix].length > 0) { - _ds = _replaceWildcards(_ds, _retrieval.args[_ix]); - } - // Encode data source: - _sourcesBytecodes[_ix] = _ds.encode(); - unchecked { - _ix ++; - } - } - // Get pointer to aggregator bytecode in storage: - bytes storage __aggregatorBytes = __database().reducersBytecode[_retrieval.aggregator]; - // Get pointer to tally bytecode in storage: - bytes storage __tallyBytes; - if (_retrieval.aggregator == _retrieval.tally) { - __tallyBytes = __aggregatorBytes; - } else { - __tallyBytes = __database().reducersBytecode[_retrieval.tally]; - require( - __tallyBytes.length > 0, - "WitnetBytecodes: no tally" - ); } - require( - __aggregatorBytes.length > 0, - "WitnetBytecodes: no aggregator" - ); - // Build retrieval bytecode: - bytes memory _retrievalBytes = _encodeRadonRetrieval( - WitnetBuffer.concat(_sourcesBytecodes), - __aggregatorBytes, - __tallyBytes, - _retrieval.resultMaxSize + + // Build RadonRetrieval bytecode: + bytes memory _bytecode = _sources.encode( + _sourcesArgs, + _aggregator, + _tally, + _resultMaxVariableSize ); + // Calculate hash and add to storage if new: - _drRetrievalHash = _retrievalBytes.hash(); - if (__database().retrievalsBytecode[_drRetrievalHash].length == 0) { - __database().retrievalsBytecode[_drRetrievalHash] = _retrievalBytes; - __database().retrievals[_drRetrievalHash] = _retrieval; - emit NewRadonRetrievalHash(_drRetrievalHash); + _hash = _bytecode.hash(); + if (__database().retrievals[_hash].sources.length == 0) { + __database().retrievals[_hash] = RadonRetrieval({ + dataType: _resultDataType, + dataMaxSize: _resultMaxVariableSize, + args: _sourcesArgs, + sources: _sourcesHashes, + aggregator: _aggregatorHash, + tally: _tallyHash + }); + emit NewRadonRetrievalHash(_hash, _bytecode); } } - function verifyRadonSLA(WitnetV2.RadonSLA memory _drSla) + function verifyRadonSLA(WitnetV2.RadonSLA calldata _sla) external virtual override - returns (bytes32 _drSlaHash) + returns (bytes32 _hash) { - if (_drSla.witnessReward == 0) { - revert RadonRetrievalNoSources(); - } - if (_drSla.numWitnesses == 0) { - revert RadonSlaNoWitnesses(); - } else if (_drSla.numWitnesses > 127) { - revert RadonSlaTooManyWitnesses(_drSla.numWitnesses); - } - if ( - _drSla.minConsensusPercentage < 51 - || _drSla.minConsensusPercentage > 99 - ) { - revert RadonSlaConsensusOutOfRange(_drSla.minConsensusPercentage); - } - if (_drSla.collateral < 10 ** 9) { - revert RadonSlaLowCollateral(_drSla.collateral); - } - bytes memory _drSlaBytecode = _drSla.encode(); - _drSlaHash = _drSlaBytecode.hash(); - if (__database().slasBytecode[_drSlaHash].length == 0) { - __database().slasBytecode[_drSlaHash] = _drSlaBytecode; - __database().slas[_drSlaHash] = _drSla; - emit NewDrSlaHash(_drSlaHash); + // Validate SLA params: + _sla.validate(); + + // Build RadonSLA bytecode: + bytes memory _bytecode = _sla.encode(); + + // Calculate hash and add to storage if new: + _hash = _bytecode.hash(); + if (__database().slas[_hash].numWitnesses == 0) { + __database().slas[_hash] = _sla; + emit NewRadonSLAHash(_hash, _bytecode); } } @@ -450,191 +469,11 @@ contract WitnetBytecodes return __bytecodes().totalDataProviders; } - - // ================================================================================================================ - // --- Internal view/pure methods --------------------------------------------------------------------------------- - - function _encodeRadonRetrieval( - bytes memory _encodedSources, - bytes memory _encodedAggregator, - bytes memory _encodedTally, - uint16 _resultMaxSize - ) - virtual - internal pure - returns (bytes memory _encodedRetrieval) - { - _encodedRetrieval = abi.encodePacked( - uint64(_encodedAggregator.length).encode(bytes1(0x1a)), - uint64(_encodedTally.length).encode(bytes1(0x2a)), - _encodeRadonRetrievalResultMaxSize(_resultMaxSize) - ); - uint64 _retrievalSize = uint64( - _encodedSources.length - + _encodedRetrieval.length - ); - return abi.encodePacked( - _retrievalSize.encode(bytes1(0x0a)), - _encodedSources, - _encodedRetrieval - ); - } - - function _encodeRadonRetrievalResultMaxSize(uint16 _maxsize) - virtual - internal pure - returns (bytes memory _encodedMaxSize) - { - if (_maxsize > 0) { - _encodedMaxSize = uint64(_maxsize).encode(0x28); - } - } - - function _replaceWildcards( - WitnetV2.DataSource memory _ds, - string[] memory _args - ) - internal pure - virtual - returns (WitnetV2.DataSource memory) - { - _ds.url = string(WitnetBuffer.replace(bytes(_ds.url), _args)); - _ds.body = string(WitnetBuffer.replace(bytes(_ds.url), _args)); - WitnetCBOR.CBOR memory _cborScript = WitnetLib.replaceCborStringsFromBytes(_ds.script, _args); - _ds.script = _cborScript.buffer.data; - return _ds; - } - - function _uint16InRange(uint16 _value, uint16 _min, uint16 _max) - internal pure - returns (bool) - { - return _value >= _min && _value <= _max; - } - - function _verifyDataFilter(WitnetV2.RadonFilter memory _filter) - internal pure - virtual - returns (bytes memory _filterBytes) - { - _filterBytes = _verifyDataFilterOps(_filter.op, _filter.cborArgs); - if (_filter.cborArgs.length > 0) { - _filterBytes = abi.encodePacked( - _filterBytes, - uint64(_filter.cborArgs.length).encode(bytes1(0x12)), - _filter.cborArgs - ); - } - return abi.encodePacked( - uint64(_filterBytes.length).encode(bytes1(0x0a)), - _filterBytes - ); - } - - function _verifyDataFilterOps(WitnetV2.RadonFilterOpcodes _opcode, bytes memory _args) - virtual - internal pure - returns (bytes memory) - { - if ( - _opcode == WitnetV2.RadonFilterOpcodes.StandardDeviation - && _args.length == 0 - || _opcode != WitnetV2.RadonFilterOpcodes.Mode - ) { - revert UnsupportedRadonFilter(uint8(_opcode), _args); - } - return uint64(_opcode).encode(bytes1(0x08)); - } - - function _verifyRadonReducerOps(WitnetV2.RadonReducerOpcodes _reducer) - virtual - internal view - returns (bytes memory) - { - if (!( - _reducer == WitnetV2.RadonReducerOpcodes.AverageMean - || _reducer == WitnetV2.RadonReducerOpcodes.StandardDeviation - || _reducer == WitnetV2.RadonReducerOpcodes.Mode - || _reducer == WitnetV2.RadonReducerOpcodes.ConcatenateAndHash - || _reducer == WitnetV2.RadonReducerOpcodes.AverageMedian - )) { - revert UnsupportedRadonReducer(uint8(_reducer)); - } - return uint64(_reducer).encode(bytes1(0x10)); - } - - function _verifyDataSourceMethod( - WitnetV2.DataRequestMethods _method, - string memory _schema - ) - virtual - internal pure - returns (WitnetV2.DataRequestMethods) - { - if ( - _method == WitnetV2.DataRequestMethods.Rng - && keccak256(bytes(_schema)) != keccak256(bytes("https://")) - && keccak256(bytes(_schema)) != keccak256(bytes("http://")) - || !_uint16InRange(uint16(_method), uint16(1), uint16(3)) - ) { - revert UnsupportedDataRequestMethod(uint8(_method), _schema); - } - return _method; - } - - function _verifyDataSourceCborScript(WitnetCBOR.CBOR memory _cbor) - virtual - internal view - returns (WitnetV2.RadonDataTypes _resultType) - { - if (_cbor.majorType == 4) { - WitnetCBOR.CBOR[] memory _items = _cbor.readArray(); - if (_items.length > 1) { - return _verifyDataSourceCborScript( - _items[_items.length - 2] - ); - } else { - return WitnetV2.RadonDataTypes.Any; - } - } else if (_cbor.majorType == 0) { - return _lookupOpcodeResultType(uint8(_cbor.readUint())); - } else { - revert WitnetCBOR.UnexpectedMajorType(0, _cbor.majorType); - } - } - - function _verifyDataSourceScript(bytes memory _script) - virtual - internal view - returns (WitnetV2.RadonDataTypes _resultType) - { - return _verifyDataSourceCborScript( - WitnetCBOR.valueFromBytes(_script) - ); - } - - function _verifyDataTypeMaxSize(WitnetV2.RadonDataTypes _dt, uint16 _maxsize) - virtual - internal pure - returns (bool) - { - if ( - _dt == WitnetV2.RadonDataTypes.Array - || _dt == WitnetV2.RadonDataTypes.Bytes - || _dt == WitnetV2.RadonDataTypes.Map - || _dt == WitnetV2.RadonDataTypes.String - ) { - return _uint16InRange(_maxsize, uint16(1), uint16(2048)); - } else { - return _uint16InRange(uint16(_dt), uint16(1), uint16(type(WitnetV2.RadonDataTypes).max)); - } - } - // ================================================================================================================ // --- Internal state-modifying methods --------------------------------------------------------------------------- - function __pushDataProvider(string memory _fqdn) + function __pushDataProviderSource(string memory _fqdn, bytes32 _sourceHash) internal virtual returns (bytes32 _hash) @@ -643,14 +482,24 @@ contract WitnetBytecodes uint _index = __database().providersIndex[_hash]; if (_index == 0) { _index = ++ __bytecodes().totalDataProviders; - __database().providers[_index] = WitnetV2.DataProvider({ - fqdn: _fqdn, - totalSources: 1, - totalRetrievals: 0 - }); - emit NewDataProvider(_fqdn, _index); - } else { - __database().providers[_index].totalSources ++; + __database().providersIndex[keccak256(bytes(_fqdn))] = _index; + __database().providers[_index].fqdn = _fqdn; + emit NewDataProvider(_index); + } + __database().providers[_index].sources[ + __database().providers[_index].totalSources ++ + ] = _sourceHash; + } + + function __pushRadonReducerFilters( + WitnetV2.RadonReducer storage __reducer, + WitnetV2.RadonFilter[] memory _filters + ) + internal + virtual + { + for (uint _ix = 0; _ix < _filters.length; _ix ++) { + __reducer.filters.push(_filters[_ix]); } } diff --git a/contracts/interfaces/V2/IWitnetBytecodes.sol b/contracts/interfaces/V2/IWitnetBytecodes.sol index 34937641b..71c47dd37 100644 --- a/contracts/interfaces/V2/IWitnetBytecodes.sol +++ b/contracts/interfaces/V2/IWitnetBytecodes.sol @@ -6,68 +6,53 @@ import "../../libs/WitnetV2.sol"; interface IWitnetBytecodes { - struct RadonRetrieval { - WitnetV2.RadonDataTypes resultType; - uint16 resultMaxSize; - string[][] args; - bytes32[] sources; - bytes32 aggregator; - bytes32 tally; - } + error IndexOutOfBounds(uint256 index, uint256 range); - error RadonRetrievalNoSources(); - error RadonRetrievalArgsMismatch(string[][] args); - error RadonRetrievalResultsMismatch(uint8 read, uint8 expected); - - error RadonSlaNoReward(); - error RadonSlaNoWitnesses(); - error RadonSlaTooManyWitnesses(uint256 numWitnesses); - error RadonSlaConsensusOutOfRange(uint256 percentage); - error RadonSlaLowCollateral(uint256 collateral); - - error UnsupportedDataRequestMethod(uint8 method, string schema); - error UnsupportedDataRequestHeaders(string[2][] headers); - error UnsupportedRadonDataType(uint8 datatype, uint256 maxlength); - error UnsupportedRadonFilter(uint8 filter, bytes args); - error UnsupportedRadonReducer(uint8 reducer); - error UnsupportedRadonScript(bytes script, uint256 offset); - error UnsupportedRadonScriptOpcode(uint8 opcode); - - event NewDataProvider(string fqdn, uint256 index); - event NewDataReducerHash(bytes32 hash); - event NewDataSourceHash(bytes32 hash, string url); - event NewRadonRetrievalHash(bytes32 hash); - event NewDrSlaHash(bytes32 hash); + event NewDataProvider(uint256 index); + event NewDataSourceHash(bytes32 hash); + event NewRadonReducerHash(bytes32 hash, bytes bytecode); + event NewRadonRetrievalHash(bytes32 hash, bytes bytecode); + event NewRadonSLAHash(bytes32 hash, bytes bytecode); function bytecodeOf(bytes32 _drRetrievalHash) external view returns (bytes memory); function bytecodeOf(bytes32 _drRetrievalHash, bytes32 _drSlaHash) external view returns (bytes memory); function hashOf(bytes32 _drRetrievalHash, bytes32 _drSlaHash) external pure returns (bytes32 _drQueryHash); - function lookupDataProvider(uint256) external view returns (WitnetV2.DataProvider memory); - function lookupDataProviderIndex(string calldata) external view returns (uint); - function lookupDataSource(bytes32 _drDataSourceHash) external view returns (WitnetV2.DataSource memory); - function lookupRadonRetrievalAggregatorHash(bytes32 _drRetrievalHash) external view returns (WitnetV2.RadonReducer memory); - function lookupRadonRetrievalResultMaxSize(bytes32 _drRetrievalHash) external view returns (uint256); - function lookupRadonRetrievalResultType(bytes32 _drRetrievalHash) external view returns (WitnetV2.RadonDataTypes); - function lookupRadonRetrievalSourceHashes(bytes32 _drRetrievalHash) external view returns (bytes32[] memory); - function lookupRadonRetrievalSourcesCount(bytes32 _drRetrievalHash) external view returns (uint); - function lookupRadonRetrievalTallyHash(bytes32 _drRetrievalHash) external view returns (WitnetV2.RadonReducer memory); - function lookupRadonSLA(bytes32 _drSlaHash) external view returns (WitnetV2.RadonSLA memory); - function lookupRadonSLAReward(bytes32 _drSlaHash) external view returns (uint64); + function lookupDataProvider(uint256 index) external view returns (string memory, uint); + function lookupDataProviderIndex(string calldata fqdn) external view returns (uint); + function lookupDataProviderSources(uint256 index, uint256 offset, uint256 length) external view returns (bytes32[] memory); + function lookupDataSource(bytes32 hash) external view returns (WitnetV2.DataSource memory); + function lookupRadonReducer(bytes32 hash) external view returns (WitnetV2.RadonReducer memory); + function lookupRadonRetrievalAggregator(bytes32 hash) external view returns (WitnetV2.RadonReducer memory); + function lookupRadonRetrievalDataMaxSize(bytes32 hash) external view returns (uint256); + function lookupRadonRetrievalDataType(bytes32 hash) external view returns (WitnetV2.RadonDataTypes); + function lookupRadonRetrievalSources(bytes32 hash) external view returns (bytes32[] memory); + function lookupRadonRetrievalSourcesCount(bytes32 hash) external view returns (uint); + function lookupRadonRetrievalTally(bytes32 hash) external view returns (WitnetV2.RadonReducer memory); + function lookupRadonSLA(bytes32 hash) external view returns (WitnetV2.RadonSLA memory); + function lookupRadonSLAReward(bytes32 hash) external view returns (uint64); function verifyDataSource( - WitnetV2.DataRequestMethods _requestMethod, - string calldata _requestSchema, - string calldata _requestFQDN, - string calldata _requestPathQuery, - string calldata _requestBody, - string[2][] calldata _requestHeaders, - bytes calldata _witnetScript + WitnetV2.DataRequestMethods requestMethod, + string calldata requestSchema, + string calldata requestFQDN, + string calldata requestPath, + string calldata requestQuery, + string calldata requestBody, + string[2][] calldata requestHeaders, + bytes calldata requestRadonScript ) external returns (bytes32); - function verifyRadonReducer(WitnetV2.RadonReducer calldata) external returns (bytes32); - function verifyRadonRetrieval(RadonRetrieval calldata) external returns (bytes32); - function verifyRadonSLA(WitnetV2.RadonSLA calldata _drSla) external returns (bytes32); + function verifyRadonReducer(WitnetV2.RadonReducer calldata reducer) external returns (bytes32); + function verifyRadonRetrieval( + WitnetV2.RadonDataTypes resultDataType, + uint16 resultMaxVariableSize, + bytes32[] calldata sources, + string[][] calldata sourcesArgs, + bytes32 aggregatorHash, + bytes32 tallyHash + ) external returns (bytes32); + function verifyRadonSLA(WitnetV2.RadonSLA calldata drSLA) external returns (bytes32); function totalDataProviders() external view returns (uint); diff --git a/contracts/libs/WitnetV2.sol b/contracts/libs/WitnetV2.sol index 2d1e9047f..0e5ce99c1 100644 --- a/contracts/libs/WitnetV2.sol +++ b/contracts/libs/WitnetV2.sol @@ -9,7 +9,24 @@ library WitnetV2 { error IndexOutOfBounds(uint256 index, uint256 range); error InsufficientBalance(uint256 weiBalance, uint256 weiExpected); error InsufficientFee(uint256 weiProvided, uint256 weiExpected); - error Unauthorized(address violator); + error Unauthorized(address violator); + + error RadonRetrievalNoSources(); + error RadonRetrievalArgsMismatch(string[][] args); + error RadonRetrievalResultsMismatch(uint index, uint8 read, uint8 expected); + + error RadonSlaNoReward(); + error RadonSlaNoWitnesses(); + error RadonSlaTooManyWitnesses(uint256 numWitnesses); + error RadonSlaConsensusOutOfRange(uint256 percentage); + error RadonSlaLowCollateral(uint256 collateral); + + error UnsupportedDataRequestMethod(uint8 method, string schema, string body, string[2][] headers); + error UnsupportedRadonDataType(uint8 datatype, uint256 maxlength); + error UnsupportedRadonFilter(uint8 filter, bytes args); + error UnsupportedRadonReducer(uint8 reducer); + error UnsupportedRadonScript(bytes script, uint256 offset); + error UnsupportedRadonScriptOpcode(bytes script, uint256 cursor, uint8 opcode); function toEpoch(uint _timestamp) internal pure returns (uint) { return 1 + (_timestamp - 11111) / 15; @@ -86,7 +103,7 @@ library WitnetV2 { struct DataProvider { string fqdn; uint256 totalSources; - uint256 totalRetrievals; + mapping (uint256 => bytes32) sources; } enum DataRequestMethods { @@ -98,7 +115,7 @@ library WitnetV2 { struct DataSource { DataRequestMethods method; - RadonDataTypes resultType; + RadonDataTypes resultDataType; string url; string body; string[2][] headers; @@ -106,19 +123,25 @@ library WitnetV2 { } enum RadonDataTypes { - /* 0x0 */ Any, - /* 0x1 */ Array, - /* 0x2 */ Bool, - /* 0x3 */ Bytes, - /* 0x4 */ Integer, - /* 0x5 */ Float, - /* 0x6 */ Map, - /* 0x7 */ String + /* 0x00 */ Any, + /* 0x01 */ Array, + /* 0x02 */ Bool, + /* 0x03 */ Bytes, + /* 0x04 */ Integer, + /* 0x05 */ Float, + /* 0x06 */ Map, + /* 0x07 */ String, + Unused0x08, Unused0x09, Unused0x0A, Unused0x0B, + Unused0x0C, Unused0x0D, Unused0x0E, Unused0x0F, + /* 0x10 */ Same, + /* 0x11 */ Inner, + /* 0x12 */ Match, + /* 0x13 */ Subscript } struct RadonFilter { - RadonFilterOpcodes op; - bytes cborArgs; + RadonFilterOpcodes opcode; + bytes args; } enum RadonFilterOpcodes { @@ -135,7 +158,7 @@ library WitnetV2 { } struct RadonReducer { - RadonReducerOpcodes op; + RadonReducerOpcodes opcode; RadonFilter[] filters; } From 6b8a062647ad232ee71a0443d02d809c7afd5064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 7 Dec 2022 09:18:11 +0100 Subject: [PATCH 033/119] chore: add missing dev dependencies --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index aa876fd07..6eb1ba6bc 100644 --- a/package.json +++ b/package.json @@ -41,15 +41,16 @@ ], "license": "MIT", "dependencies": { - "ado-contracts": "1.0.0", "@eth-optimism/solc": "0.7.6-alpha.1", "@openzeppelin/contracts": "~4.8.0-rc.2", + "ado-contracts": "1.0.0", "lodash": "^4.17.21" }, "devDependencies": { "@openzeppelin/test-helpers": "0.5.5", "@witnet/truffle-flattener-single-experimental": "^0.1.0", "chai": "4.3.6", + "custom-error-test-helper": "^1.0.6", "dotenv": "8.2.0", "eslint": "^8.26.0", "eslint-config-standard": "^17.0.0", @@ -60,6 +61,7 @@ "js-sha256": "0.9.0", "solhint": "3.3.7", "solidity-coverage": "0.7.16", + "solidity-stringutils": "https://github.com/Arachnid/solidity-stringutils/", "truffle": "5.6.2", "truffle-assertions": "0.9.2" } From d8cdba7f61f37f31d2f214e54ebb1ee06a3e6c63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Mon, 19 Dec 2022 17:18:10 +0100 Subject: [PATCH 034/119] test(bytecodes): add WitnetBytecodes unitary tests --- test/witnet_bytecodes.test.js | 749 ++++++++++++++++++++++++++++++++++ 1 file changed, 749 insertions(+) create mode 100644 test/witnet_bytecodes.test.js diff --git a/test/witnet_bytecodes.test.js b/test/witnet_bytecodes.test.js new file mode 100644 index 000000000..193e0fd18 --- /dev/null +++ b/test/witnet_bytecodes.test.js @@ -0,0 +1,749 @@ +const utils = require("../scripts/utils") +const { expectEvent, expectRevert } = require("@openzeppelin/test-helpers"); +const { assert } = require("chai"); +const { expectRevertCustomError } = require("custom-error-test-helper"); + +const WitnetBuffer = artifacts.require("WitnetBuffer") +const WitnetBytecodes = artifacts.require("WitnetBytecodes"); +const WitnetV2 = artifacts.require("WitnetV2") + +contract('WitnetBytecodes', (accounts) => { + var creatorAddress = accounts[0]; + var firstOwnerAddress = accounts[1]; + var secondOwnerAddress = accounts[2]; + var externalAddress = accounts[3]; + var unprivilegedAddress = accounts[4] + + var bytecodes + + before(async () => { + bytecodes = await WitnetBytecodes.new( + true, + utils.fromAscii("testing") + ) + }) + + beforeEach(async () => { + /* before each context */ + }) + + context("Ownable2Step", async () => { + it('should revert if transferring ownership from stranger', async () => { + await expectRevert( + bytecodes.transferOwnership(unprivilegedAddress, { from: unprivilegedAddress }), + "not the owner" + ) + }) + it('owner can start transferring ownership', async () => { + const tx = await bytecodes.transferOwnership(firstOwnerAddress, { from: creatorAddress }) + expectEvent( + tx.receipt, + "OwnershipTransferStarted", + { newOwner: firstOwnerAddress } + ) + }) + it('stranger cannot accept transferring ownership', async () => { + await expectRevert( + bytecodes.acceptOwnership({ from: unprivilegedAddress }), + "not the new owner" + ) + }) + it('ownership is fully transferred upon acceptance', async () => { + const tx = await bytecodes.acceptOwnership({ from: firstOwnerAddress }) + expectEvent( + tx.receipt, + "OwnershipTransferred", + { + previousOwner: creatorAddress, + newOwner: firstOwnerAddress + } + ) + assert.equal(firstOwnerAddress, await bytecodes.owner()) + }) + }) + + context("Upgradable", async () => { + it('should manifest to be upgradable from actual owner', async () => { + assert.equal( + await bytecodes.isUpgradableFrom(firstOwnerAddress), + true + ) + }) + it('should manifest to not be upgradable from anybody else', async () => { + assert.equal( + await bytecodes.isUpgradableFrom(unprivilegedAddress), + false + ) + }) + it('cannot be initialized more than once', async () => { + await expectRevertCustomError( + WitnetBytecodes, + bytecodes.initialize("0x", { from: firstOwnerAddress }), + "AlreadyInitialized" + ) + await expectRevertCustomError( + WitnetBytecodes, + bytecodes.initialize("0x", { from: unprivilegedAddress }), + "OnlyOwner" + ) + }) + }) + + context("IWitnetBytecodes", async () => { + + var slaHash + var slaBytecode + + var concathashReducerHash + var concathashReducerBytecode + var modeNoFiltersReducerHash + var modeNoFitlersReducerBytecode + var stdev15ReducerHash + var stdev25ReducerHash + + var rngSourceHash + var binanceTickerHash + var uniswapToken0PriceHash + var uniswapToken1PriceHash + + var rngHash + var rngBytecode + + var btcUsdPriceFeedHash + var btcUsdPriceFeedBytecode + var fraxUsdtPriceFeedHash + var fraxUsdtPriceFeedBytecode + + context("verifyDataSource(..)", async() => { + context("WitnetV2.DataRequestMethods.Rng", async () => { + it('emits appropiate single event when verifying randomness data source for the first time', async () => { + const tx = await bytecodes.verifyDataSource( + 2, // requestMethod + 0, // resultMinRank + 0, // resultMaxRank + "", // requestSchema + "", // requestFQDN + "", // requestPath + "", // requestQuery + "", // requestBody + [], // requestHeaders + "0x80", // requestRadonScript + ) + expectEvent( + tx.receipt, + "NewDataSourceHash" + ) + rngSourceHash = tx.logs[0].args.hash + }) + it('emits no event when verifying already existing randomness data source', async () => { + const tx = await bytecodes.verifyDataSource( + 2, // requestMethod + 0, // resultMinRank + 0, // resultMaxRank + "", // requestSchema + "", // requestFQDN + "", // requestPath + "", // requestQuery + "", // requestBody + [], // requestHeaders + "0x80", // requestRadonScript + ) + 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 bytecodes.verifyDataSource.call( + 2, // requestMethod + 0, // resultMinRank + 0, // resultMaxRank + "", // requestSchema + "", // requestFQDN + "", // requestPath + "", // requestQuery + "", // requestBody + [], // requestHeaders + "0x80", // requestRadonScript + ) + assert.equal(hash, rngSourceHash); + }) + // ... reverts + }) + context("WitnetV2.DataRequestMethods.HttpGet", async () => { + it('emits new data provider and source events when verifying a new http-get source for the first time', async () => { + const tx = await bytecodes.verifyDataSource( + 1, // requestMethod + 0, // resultMinRank + 0, // resultMaxRank + "HTTPs://", // requestSchema + "api.binance.US", // requestFQDN + "api/v3/ticker/price", // requestPath + "symbol=\\0\\\\1\\", // requestQuery + "", // requestBody + [], // requestHeaders + "0x841877821864696c61737450726963658218571a000f4240185b", // requestRadonScript + ) + expectEvent( + tx.receipt, + "NewDataProvider" + ) + assert.equal(tx.logs[0].args.index, 1) + expectEvent( + tx.receipt, + "NewDataSourceHash" + ) + binanceTickerHash = tx.logs[1].args.hash + }) + it('data source metadata gets stored as expected', async () => { + const ds = await bytecodes.lookupDataSource(binanceTickerHash) + assert.equal(ds.method, 1) // HTTP-GET + assert.equal(ds.resultDataType, 4) // Integer + assert.equal(ds.url, "https://api.binance.us/api/v3/ticker/price?symbol=\\0\\\\1\\") + assert.equal(ds.body, "") + assert(ds.headers.length == 0) + assert.equal(ds.script, "0x841877821864696c61737450726963658218571a000f4240185b") + }) + it('emits one single event when verifying new http-get endpoint to already existing provider', async () => { + const tx = await bytecodes.verifyDataSource( + 1, // requestMethod + 0, // resultMinRank + 0, // resultMaxRank + "http://", // requestSchema + "api.binance.us", // requestFQDN + "api/v3/ticker/24hr", // requestPath + "symbol=\\0\\\\1\\", // requestQuery + "", // requestBody + [], // requestHeaders + "0x841877821864696c61737450726963658218571a000f4240185b", // requestRadonScript + ) + assert.equal(tx.logs.length, 1) + expectEvent( + tx.receipt, + "NewDataSourceHash" + ) + }) + }) + context("WitnetV2.DataRequestMethods.HttpPost", async () => { + it('emits new data provider and source events when verifying a new http-post source for the first time', async () => { + const tx = await bytecodes.verifyDataSource( + 3, // requestMethod + 0, // resultMinRank + 0, // resultMaxRank + "HTTPs://", // requestSchema + "api.thegraph.com", // requestFQDN + "subgraphs/name/uniswap/uniswap-v3", // requestPath + "", // requestQuery + `{"query":"{pool(id:\"\\0\\\"){token1Price}}"}`, // requestBody + [ + ["user-agent", "witnet-rust"], + ["content-type", "text/html; charset=utf-8"], + ], // requestHeaders + "0x861877821866646461746182186664706f6f6c8218646b746f6b656e3150726963658218571a000f4240185b", // requestRadonScript + ) + expectEvent( + tx.receipt, + "NewDataProvider" + ) + assert.equal(tx.logs[0].args.index, 2) + expectEvent( + tx.receipt, + "NewDataSourceHash" + ) + uniswapToken1PriceHash = tx.logs[1].args.hash + }) + it('data source metadata gets stored as expected', async () => { + const ds = await bytecodes.lookupDataSource(uniswapToken1PriceHash) + assert.equal(ds.method, 3) // HTTP-GET + assert.equal(ds.resultDataType, 4) // Integer + assert.equal(ds.url, "https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3") + assert.equal(ds.body, `{"query":"{pool(id:\"\\0\\\"){token1Price}}"}`) + assert(ds.headers.length == 2) + assert.equal(ds.headers[0][0], "user-agent") + assert.equal(ds.headers[0][1], "witnet-rust") + assert.equal(ds.headers[1][0], "content-type") + assert.equal(ds.headers[1][1], "text/html; charset=utf-8") + assert.equal(ds.script, "0x861877821866646461746182186664706f6f6c8218646b746f6b656e3150726963658218571a000f4240185b") + }) + }) + }) + + context("verifyRadonReducer(..)", async() => { + it('emits event when verifying new radon reducer with no filter', async () => { + const tx = await bytecodes.verifyRadonReducer([ + 11, // opcode: ConcatenateAndHash + [], // filters + "0x" // script + ]) + expectEvent( + tx.receipt, + "NewRadonReducerHash" + ) + concathashReducerHash = tx.logs[0].args.hash + concathashReducerBytecode = tx.logs[0].args.bytecode + }) + it('emits no event when verifying an already verified radon sla with no filter', async () => { + const tx = await bytecodes.verifyRadonReducer([ + 11, // ConcatenateAndHash + [], // filters + "0x" // script + ]) + assert.equal( + tx.logs.length, + 0, + "some unexpected event was emitted" + ) + }) + it('generates proper hash upon offchain call', async () => { + const hash = await bytecodes.verifyRadonReducer.call([ + 11, // ConcatenateAndHash + [], // filters + "0x" // script + ]) + assert.equal(hash, concathashReducerHash) + }) + it('reverts custom error if verifying radon reducer with unsupported opcode', async () => { + await expectRevertCustomError( + WitnetV2, + bytecodes.verifyRadonReducer([ + 0, // Minimum + [], // filters + "0x" // script + ]), + "UnsupportedRadonReducerOpcode" + ) + }) + it('reverts custom error if verifying radon reducer with at least one unsupported filter', async () => { + await expectRevertCustomError( + WitnetV2, + bytecodes.verifyRadonReducer([ + 5, // AverageMedian + [ + [ 8, "0x" ], // Mode: supported + [ 0, "0x" ], // Greater than: not yet supported + ], + "0x" // script + ]), + "UnsupportedRadonFilterOpcode" + ) + }) + it('reverts custom error if verifying radon reducer with stdev filter but no args', async () => { + await expectRevertCustomError( + WitnetV2, + bytecodes.verifyRadonReducer([ + 2, // Mode + [ + [ 5, "0x" ], // Standard deviation filter + ], + "0x" // script + ]), + "RadonFilterMissingArgs" + ) + }) + it('verifying radon reducer with stdev filter and args works', async () => { + var tx = await bytecodes.verifyRadonReducer([ + 3, // AverageMean + [ + [ 5, "0xF93E00" ], // StdDev(1.5) filter + ], + "0x" // script + ]) + expectEvent( + tx.receipt, + "NewRadonReducerHash" + ) + stdev15ReducerHash = tx.logs[0].args.hash + tx = await bytecodes.verifyRadonReducer([ + 2, // Mode + [ + [ 5, "0xF94100" ], // StdDev(2.5) filter + ], + "0x" // script + ]) + stdev25ReducerHash = tx.logs[0].args.hash + }) + }) + + context("verifyRadonRetrieval(..)", async () => { + context("Use case: Randomness", async () => { + it('emits single event when verifying new radomness retrieval', async () => { + var tx = await bytecodes.verifyRadonReducer([ + 2, // Mode + [], // no filters + "0x" // script + ]); + expectEvent( + tx.receipt, + "NewRadonReducerHash" + ) + modeNoFiltersReducerHash = tx.logs[0].args.hash + modeNoFiltersReducerBytecode = tx.logs[0].args.bytecode + tx = await bytecodes.verifyRadonRetrieval( + 0, // resultDataType + 0, // resultMaxVariableSize + [ // sources + rngSourceHash + ], + [[]], // sourcesArgs + modeNoFiltersReducerHash, // aggregator + concathashReducerHash, // tally + ) + assert(tx.logs.length == 1) + expectEvent( + tx.receipt, + "NewRadonRetrievalHash" + ) + rngHash = tx.logs[0].args.hash + rngBytecode = tx.logs[0].args.bytecode + }) + it('emits no event when verifying same randomness retrieval', async () => { + const tx = await bytecodes.verifyRadonRetrieval( + 0, // resultDataType + 0, // resultMaxVariableSize + [ // sources + rngSourceHash + ], + [[]], // sourcesArgs + modeNoFiltersReducerHash, // aggregator + concathashReducerHash, // tally + ) + assert(tx.logs.length == 0) + }) + it('generates same hash when verifying same randomness retrieval offchain', async () => { + const hash = await bytecodes.verifyRadonRetrieval.call( + 0, // resultDataType + 0, // resultMaxVariableSize + [ // sources + rngSourceHash + ], + [[]], // sourcesArgs + modeNoFiltersReducerHash, // aggregator + concathashReducerHash, // tally + ) + assert.equal(hash, rngHash) + }) + }) + context("Use case: Price feeds", async () => { + it('reverts custom error if trying to verify retrieval w/ templated source and 0 args out of 2', async () => { + await expectRevertCustomError( + WitnetBuffer, + bytecodes.verifyRadonRetrieval( + 4, // resultDataType + 0, // resultMaxVariableSize + [ // sources + binanceTickerHash, + ], + [ // sourcesArgs + [] + ], + stdev15ReducerHash, // aggregator + stdev25ReducerHash, // tally + ), + "MissingArgs", [ + 1, // expected + 0, // given + ] + ) + }) + it('reverts custom error if trying to verify retrieval w/ templated source and 1 args out of 2', async () => { + await expectRevertCustomError( + WitnetBuffer, + bytecodes.verifyRadonRetrieval( + 4, // resultDataType + 0, // resultMaxVariableSize + [ // sources + binanceTickerHash, + ], + [ // sourcesArgs + [ "BTC" ] + ], + stdev15ReducerHash, // aggregator + stdev25ReducerHash, // tally + ), + "MissingArgs", [ + 2, // expected + 1, // given + ] + ) + }) + it('emits single event when verifying new price feed retrieval for the first time', async () => { + const tx = await bytecodes.verifyRadonRetrieval( + 4, // resultDataType + 0, // resultMaxVariableSize, + [ // sources + binanceTickerHash, + ], + [ + [ "BTC", "USD" ] // binance ticker args + ], + stdev15ReducerHash, // aggregator + stdev25ReducerHash, // tally + ) + assert(tx.logs.length == 1) + expectEvent( + tx.receipt, + "NewRadonRetrievalHash" + ) + btcUsdPriceFeedHash = tx.logs[0].args.hash + btcUsdPriceFeedBytecode = tx.logs[0].args.bytecode + }) + it('verifying radon retrieval with repeated sources works', async () => { + const tx = await bytecodes.verifyRadonRetrieval( + 4, // resultDataType + 0, // resultMaxVariableSize, + [ // sources + binanceTickerHash, + binanceTickerHash, + // binanceTickerHash, + ], + [ + [ "BTC", "USD" ], // binance ticker args + [ "BTC", "USD" ], // binance ticker args + // [ "BTC", "USD" ], // binance ticker args + ], + stdev15ReducerHash, // aggregator + stdev25ReducerHash, // tally + ) + assert(tx.logs.length == 1) + expectEvent( + tx.receipt, + "NewRadonRetrievalHash" + ) + }) + it('reverts if trying to verify radon retrieval w/ incompatible sources', async () => { + await expectRevertCustomError( + WitnetV2, + bytecodes.verifyRadonRetrieval( + 4, // resultDataType + 0, // resultMaxVariableSize, + [ // sources + binanceTickerHash, + rngSourceHash, + ], + [ + [ "BTC", "USD" ], // binance ticker args + [], + ], + stdev15ReducerHash, // aggregator + stdev25ReducerHash, // tally + ), + "RadonRetrievalResultsMismatch", [ + 1, // index + 0, // read + 4, // expected + ] + ) + }) + it('emits single event when verifying new radon retrieval w/ http-post source', async () => { + const tx = await bytecodes.verifyRadonRetrieval( + 4, // resultDataType + 0, // resultMaxVariableSize, + [ // sources + uniswapToken1PriceHash, + ], + [ + [ "0xc2a856c3aff2110c1171b8f942256d40e980c726" ] // pair id + ], + stdev15ReducerHash, // aggregator + stdev25ReducerHash, // tally + ) + assert(tx.logs.length == 1) + expectEvent( + tx.receipt, + "NewRadonRetrievalHash" + ) + console.log(tx.logs[0].args) + fraxUsdtPriceFeedHash = tx.logs[0].args.hash + fraxUsdtPriceFeedBytecode = tx.logs[0].args.bytecode + }) + it('emits single event when verifying new radon retrieval w/ repeated http-post sources', async () => { + const tx = await bytecodes.verifyRadonRetrieval( + 4, // resultDataType + 0, // resultMaxVariableSize, + [ // sources + uniswapToken1PriceHash, + uniswapToken1PriceHash, + uniswapToken1PriceHash, + uniswapToken1PriceHash, + uniswapToken1PriceHash, + uniswapToken1PriceHash, + ], + [ + [ "0xc2a856c3aff2110c1171b8f942256d40e980c726" ], // pair id + [ "0xc2a856c3aff2110c1171b8f942256d40e980c726" ], // pair id + [ "0xc2a856c3aff2110c1171b8f942256d40e980c726" ], // pair id + [ "0xc2a856c3aff2110c1171b8f942256d40e980c726" ], // pair id + [ "0xc2a856c3aff2110c1171b8f942256d40e980c726" ], // pair id + [ "0xc2a856c3aff2110c1171b8f942256d40e980c726" ], // pair id + ], + stdev15ReducerHash, // aggregator + stdev25ReducerHash, // tally + ) + assert(tx.logs.length == 1) + expectEvent( + tx.receipt, + "NewRadonRetrievalHash" + ) + console.log(tx.logs[0].args) + }) + }) + + }) + + context("verifyRadonSLA(..)", async () => { + + it('emits event when verifying new radon sla', async () => { + const tx = await bytecodes.verifyRadonSLA([ + 10 ** 6, + 10, + 10 ** 6, + 51, + 5 * 10 ** 9 + ]) + expectEvent( + tx.receipt, + "NewRadonSLAHash" + ) + slaHash = tx.logs[0].args.hash + slaBytecode = tx.logs[0].args.bytecode + }) + it('emits no event when verifying an already verified radon sla', async () => { + const tx = await bytecodes.verifyRadonSLA([ + 10 ** 6, + 10, + 10 ** 6, + 51, + 5 * 10 ** 9 + ]) + assert.equal( + tx.logs.length, + 0, + "some unexpected event was emitted" + ) + }) + it('generates proper hash upon offchain call', async() => { + const hash = await bytecodes.verifyRadonSLA.call([ + 10 ** 6, + 10, + 10 ** 6, + 51, + 5 * 10 ** 9 + ]) + assert.equal(hash, slaHash); + }) + it('reverts custom error if verifying radon sla with no reward', async () => { + await expectRevertCustomError( + WitnetV2, + bytecodes.verifyRadonSLA([ + 0, + 10, + 10 ** 6, + 51, + 5 * 10 ** 9 + ]), + "RadonSlaNoReward" + ) + }) + it('reverts custom error if verifying radon sla with no witnesses', async () => { + await expectRevertCustomError( + WitnetV2, + bytecodes.verifyRadonSLA([ + 10 ** 6, + 0, + 10 ** 6, + 51, + 5 * 10 ** 9 + ]), + "RadonSlaNoWitnesses" + ) + }) + it('reverts custom error if verifying radon sla with too many witnesses', async () => { + await expectRevertCustomError( + WitnetV2, + bytecodes.verifyRadonSLA([ + 10 ** 6, + 500, + 10 ** 6, + 51, + 5 * 10 ** 9 + ]), + "RadonSlaTooManyWitnesses" + ) + }) + it('reverts custom error if verifying radon sla with quorum out of range', async () => { + await expectRevertCustomError( + WitnetV2, + bytecodes.verifyRadonSLA([ + 10 ** 6, + 10, + 10 ** 6, + 50, + 5 * 10 ** 9 + ]), + "RadonSlaConsensusOutOfRange" + ) + await expectRevertCustomError( + WitnetV2, + bytecodes.verifyRadonSLA([ + 10 ** 6, + 10, + 10 ** 6, + 100, + 5 * 10 ** 9 + ]), + "RadonSlaConsensusOutOfRange" + ) + }) + it('reverts custom error if verifying radon sla with too low collateral', async () => { + await expectRevertCustomError( + WitnetV2, + bytecodes.verifyRadonSLA([ + 10 ** 6, + 10, + 10 ** 6, + 51, + 10 ** 6 + ]), + "RadonSlaLowCollateral" + ) + }) + }) + + context("bytecodeOf(..)", async () => { + context("radon retrievals", async () => { + it('reverts if trying to get bytecode from unknown radon retrieval', async () => { + await expectRevertCustomError( + WitnetBytecodes, + bytecodes.bytecodeOf("0x0"), + "UnknownRadonRetrieval" + ) + }) + it('works if trying to get bytecode onchain from known radon retrieval', async () => { + await bytecodes.bytecodeOf(btcUsdPriceFeedHash); + }) + it('returns expected bytecode if getting it offchain from known radon retrieval', async () =>{ + const bytecode = await bytecodes.bytecodeOf(btcUsdPriceFeedHash) + assert.equal(bytecode, btcUsdPriceFeedBytecode) + }) + }) + context("radon slas", async () => { + it('reverts if trying to get bytecode from unknown radon sla', async () => { + await expectRevertCustomError( + WitnetBytecodes, + bytecodes.bytecodeOf(btcUsdPriceFeedHash, "0x0"), + "UnknownRadonSLA" + ) + }) + it('works if trying to get bytecode onchain from known radon retrieval and sla', async () => { + await bytecodes.bytecodeOf(btcUsdPriceFeedHash, slaHash) + }) + it('returns expected bytecode if getting it offchain from known radon retrieval and sla', async () => { + const bytecode = await bytecodes.bytecodeOf.call(btcUsdPriceFeedHash, slaHash) + assert.equal( + btcUsdPriceFeedBytecode + slaBytecode.slice(2), + bytecode + ) + }) + }) + }) + + }) + +}); From 45b1661f82ab802877e28e507a9cf4436681f34b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Mon, 19 Dec 2022 17:19:59 +0100 Subject: [PATCH 035/119] fix(libs): as to pass WitnetBytecodes tests --- contracts/libs/WitnetBuffer.sol | 10 +- contracts/libs/WitnetEncodingLib.sol | 239 ++++++++++++++++----------- contracts/libs/WitnetLib.sol | 18 ++ contracts/libs/WitnetV2.sol | 27 +-- 4 files changed, 185 insertions(+), 109 deletions(-) diff --git a/contracts/libs/WitnetBuffer.sol b/contracts/libs/WitnetBuffer.sol index f0dd791a6..0296e1d94 100644 --- a/contracts/libs/WitnetBuffer.sol +++ b/contracts/libs/WitnetBuffer.sol @@ -12,7 +12,7 @@ library WitnetBuffer { error EmptyBuffer(); error IndexOutOfBounds(uint index, uint range); - error MissingArgs(uint index, string[] args); + error MissingArgs(uint expected, uint given); /// Iterable bytes buffer. struct Buffer { @@ -398,6 +398,10 @@ library WitnetBuffer { uint source; uint sourceLength; uint sourcePointer; + + if (input.length < 3) { + return input; + } assembly { // set starting input pointer @@ -417,8 +421,8 @@ library WitnetBuffer { && input[ix + 1] >= bytes1("0") && input[ix + 1] <= bytes1("9") ) { + inputLength = (ix - lix); if (ix > lix) { - inputLength = (ix - lix); _memcpy( outputPointer, inputPointer, @@ -429,7 +433,7 @@ library WitnetBuffer { } uint ax = uint(uint8(input[ix + 1]) - uint8(bytes1("0"))); if (ax >= args.length) { - revert MissingArgs(ax, args); + revert MissingArgs(ax + 1, args.length); } assembly { source := mload(add(args, mul(32, add(ax, 1)))) diff --git a/contracts/libs/WitnetEncodingLib.sol b/contracts/libs/WitnetEncodingLib.sol index 6e8fc1d1d..05bb51b74 100644 --- a/contracts/libs/WitnetEncodingLib.sol +++ b/contracts/libs/WitnetEncodingLib.sol @@ -46,7 +46,7 @@ library WitnetEncodingLib { /// =============================================================================================================== /// --- WitnetLib internal methods -------------------------------------------------------------------------------- - function size(WitnetV2.RadonDataTypes _type) internal pure returns (uint) { + function size(WitnetV2.RadonDataTypes _type) internal pure returns (uint16) { if (_type == WitnetV2.RadonDataTypes.Integer || _type == WitnetV2.RadonDataTypes.Float ) { @@ -206,57 +206,74 @@ library WitnetEncodingLib { _encodedUrl, _encodedScript, _encodedBody, - _encodedHeaders + _encodedHeaders, + source.resultMinRank > 0 || source.resultMaxRank > 0 + ? abi.encodePacked( + encode(uint64(source.resultMinRank), bytes1(0x30)), + encode(uint64(source.resultMaxRank), bytes1(0x38)) + ) : bytes("") ); } function encode( WitnetV2.DataSource[] memory sources, string[][] memory args, - WitnetV2.RadonReducer memory aggregator, - WitnetV2.RadonReducer memory tally, + bytes memory aggregatorInnerBytecode, + bytes memory tallyInnerBytecode, uint16 resultMaxSize ) public pure returns (bytes memory bytecode) { - bytes[] memory encodedSources; + bytes[] memory encodedSources = new bytes[](sources.length); for (uint ix = 0; ix < sources.length; ix ++) { replaceWildcards(sources[ix], args[ix]); encodedSources[ix] = encode(sources[ix]); } bytecode = abi.encodePacked( - encode(aggregator), - encode(tally), + WitnetBuffer.concat(encodedSources), + encode(uint64(aggregatorInnerBytecode.length), bytes1(0x1a)), + aggregatorInnerBytecode, + encode(uint64(tallyInnerBytecode.length), bytes1(0x22)), + tallyInnerBytecode, (resultMaxSize > 0 ? encode(uint64(resultMaxSize), 0x28) : bytes("") ) ); - + return abi.encodePacked( + encode(uint64(bytecode.length), bytes1(0x0a)), + bytecode + ); } function encode(WitnetV2.RadonReducer memory reducer) public pure returns (bytes memory bytecode) { - for (uint ix = 0; ix < reducer.filters.length; ix ++) { + if (reducer.script.length == 0) { + for (uint ix = 0; ix < reducer.filters.length; ix ++) { + bytecode = abi.encodePacked( + bytecode, + encode(reducer.filters[ix]) + ); + } bytecode = abi.encodePacked( bytecode, - encode(reducer.filters[ix]) + encode(reducer.opcode) + ); + } else { + return abi.encodePacked( + encode(uint64(reducer.script.length), bytes1(0x18)), + reducer.script ); } - bytecode = abi.encodePacked( - bytecode, - encode(reducer.opcode) - ); } function encode(WitnetV2.RadonFilter memory filter) public pure returns (bytes memory bytecode) - { - + { bytecode = abi.encodePacked( encode(uint64(filter.opcode), bytes1(0x08)), filter.args.length > 0 @@ -319,6 +336,8 @@ library WitnetEncodingLib { function validate( WitnetV2.DataRequestMethods method, + uint16 resultMinRank, + uint16 resultMaxRank, string memory schema, string memory host, string memory path, @@ -329,27 +348,22 @@ library WitnetEncodingLib { ) public pure { - if ( - method == WitnetV2.DataRequestMethods.Rng + if (!( + (method == WitnetV2.DataRequestMethods.HttpGet || method == WitnetV2.DataRequestMethods.HttpPost) + && bytes(host).length > 0 && ( - bytes(schema).length != 0 - || headers.length != 0 - || bytes(body).length != 0 - || script.length != 0 + keccak256(bytes(schema)) == keccak256(bytes("https://")) + || keccak256(bytes(schema)) == keccak256(bytes("http://")) ) - || ( - method == WitnetV2.DataRequestMethods.HttpGet - || method == WitnetV2.DataRequestMethods.HttpPost - ) && ( - headers[0].length != headers[1].length - || ( - keccak256(bytes(schema)) != keccak256(bytes("https://")) - && keccak256(bytes(schema)) != keccak256(bytes("http://")) - ) - ) - || method == WitnetV2.DataRequestMethods.Unknown - || uint8(method) > uint8(WitnetV2.DataRequestMethods.HttpPost) - ) { + || method == WitnetV2.DataRequestMethods.Rng + && bytes(schema).length == 0 + && bytes(host).length == 0 + && bytes(path).length == 0 + && bytes(query).length == 0 + && bytes(body).length == 0 + && headers.length == 0 + && script.length >= 1 + )) { revert WitnetV2.UnsupportedDataRequestMethod( uint8(method), schema, @@ -357,6 +371,13 @@ library WitnetEncodingLib { headers ); } + if (resultMinRank > resultMaxRank) { + revert WitnetV2.UnsupportedDataRequestMinMaxRanks( + uint8(method), + resultMinRank, + resultMaxRank + ); + } validateUrlHost(host); validateUrlPath(path); validateUrlQuery(query); @@ -367,27 +388,31 @@ library WitnetEncodingLib { uint16 maxDataSize ) public pure + returns (uint16) { if ( - dataType == WitnetV2.RadonDataTypes.Array - || dataType == WitnetV2.RadonDataTypes.Bytes - || dataType == WitnetV2.RadonDataTypes.Map - || dataType == WitnetV2.RadonDataTypes.String + dataType == WitnetV2.RadonDataTypes.Any + || dataType == WitnetV2.RadonDataTypes.String + || dataType == WitnetV2.RadonDataTypes.Bytes + || dataType == WitnetV2.RadonDataTypes.Array ) { - if (maxDataSize == 0 || maxDataSize > 2048) { + if (/*maxDataSize == 0 ||*/maxDataSize > 2048) { revert WitnetV2.UnsupportedRadonDataType( uint8(dataType), maxDataSize ); } + return maxDataSize; } else if ( - dataType != WitnetV2.RadonDataTypes.Integer - || dataType != WitnetV2.RadonDataTypes.Float - || dataType != WitnetV2.RadonDataTypes.Bool + dataType == WitnetV2.RadonDataTypes.Integer + || dataType == WitnetV2.RadonDataTypes.Float + || dataType == WitnetV2.RadonDataTypes.Bool ) { + return 0; // TBD: size(dataType); + } else { revert WitnetV2.UnsupportedRadonDataType( uint8(dataType), - 0 + size(dataType) ); } } @@ -397,29 +422,48 @@ library WitnetEncodingLib { { if ( filter.opcode == WitnetV2.RadonFilterOpcodes.StandardDeviation - && filter.args.length == 0 - || filter.opcode != WitnetV2.RadonFilterOpcodes.Mode ) { - revert WitnetV2.UnsupportedRadonFilter( - uint8(filter.opcode), filter.args - ); + // check filters that require arguments + if (filter.args.length == 0) { + revert WitnetV2.RadonFilterMissingArgs(uint8(filter.opcode)); + } + } else if ( + filter.opcode == WitnetV2.RadonFilterOpcodes.Mode + ) { + // check filters that don't require any arguments + if (filter.args.length > 0) { + revert WitnetV2.UnsupportedRadonFilterArgs(uint8(filter.opcode), filter.args); + } + } else { + // reject unsupported opcodes + revert WitnetV2.UnsupportedRadonFilterOpcode(uint8(filter.opcode)); } } function validate(WitnetV2.RadonReducer memory reducer) public pure { - if (!( - reducer.opcode == WitnetV2.RadonReducerOpcodes.AverageMean - || reducer.opcode == WitnetV2.RadonReducerOpcodes.StandardDeviation - || reducer.opcode == WitnetV2.RadonReducerOpcodes.Mode - || reducer.opcode == WitnetV2.RadonReducerOpcodes.ConcatenateAndHash - || reducer.opcode == WitnetV2.RadonReducerOpcodes.AverageMedian - )) { - revert WitnetV2.UnsupportedRadonReducer(uint8(reducer.opcode)); - } - for (uint ix = 0; ix < reducer.filters.length; ix ++) { - validate(reducer.filters[ix]); + if (reducer.script.length == 0) { + if (!( + reducer.opcode == WitnetV2.RadonReducerOpcodes.AverageMean + || reducer.opcode == WitnetV2.RadonReducerOpcodes.StandardDeviation + || reducer.opcode == WitnetV2.RadonReducerOpcodes.Mode + || reducer.opcode == WitnetV2.RadonReducerOpcodes.ConcatenateAndHash + || reducer.opcode == WitnetV2.RadonReducerOpcodes.AverageMedian + )) { + revert WitnetV2.UnsupportedRadonReducerOpcode(uint8(reducer.opcode)); + } + for (uint ix = 0; ix < reducer.filters.length; ix ++) { + validate(reducer.filters[ix]); + } + } else { + if (uint8(reducer.opcode) != 0xff || reducer.filters.length > 0) { + revert WitnetV2.UnsupportedRadonReducerScript( + uint8(reducer.opcode), + reducer.script, + 0 + ); + } } } @@ -448,39 +492,45 @@ library WitnetEncodingLib { function validateUrlHost(string memory fqdn) public pure { - strings.slice memory slice = fqdn.toSlice(); - strings.slice memory host = slice.split(string(":").toSlice()); - if (!_checkUrlHostPort(slice.toString())) { - revert UrlBadHostPort(fqdn, slice.toString()); - } - strings.slice memory delim = string(".").toSlice(); - string[] memory parts = new string[](host.count(delim) + 1); - if (parts.length == 1) { - revert UrlBadHostXalphas(fqdn, fqdn); - } - for (uint ix = 0; ix < parts.length; ix ++) { - parts[ix] = host.split(delim).toString(); - if (!_checkUrlHostXalphas(bytes(parts[ix]))) { - revert UrlBadHostXalphas(fqdn, parts[ix]); - } - } - if (parts.length == 4) { - bool _prevDigits = false; - for (uint ix = 4; ix > 0; ix --) { - if (_checkUrlHostIpv4(parts[ix - 1])) { - _prevDigits = true; - } else { - if (_prevDigits) { - revert UrlBadHostIpv4(fqdn, parts[ix - 1]); - } else { - break; + unchecked { + if (bytes(fqdn).length > 0) { + strings.slice memory slice = fqdn.toSlice(); + strings.slice memory host = slice.split(string(":").toSlice()); + if (!_checkUrlHostPort(slice.toString())) { + revert UrlBadHostPort(fqdn, slice.toString()); + } + strings.slice memory delim = string(".").toSlice(); + string[] memory parts = new string[](host.count(delim) + 1); + if (parts.length == 1) { + revert UrlBadHostXalphas(fqdn, fqdn); + } + for (uint ix = 0; ix < parts.length; ix ++) { + parts[ix] = host.split(delim).toString(); + if (!_checkUrlHostXalphas(bytes(parts[ix]))) { + revert UrlBadHostXalphas(fqdn, parts[ix]); } - } + } + if (parts.length == 4) { + bool _prevDigits = false; + for (uint ix = 4; ix > 0; ix --) { + if (_checkUrlHostIpv4(parts[ix - 1])) { + _prevDigits = true; + } else { + if (_prevDigits) { + revert UrlBadHostIpv4(fqdn, parts[ix - 1]); + } else { + break; + } + } + } + } } } } - function validateUrlPath(string memory path) public pure { + function validateUrlPath(string memory path) + public pure + { unchecked { if (bytes(path).length > 0) { if (bytes(path)[0] == bytes1("/")) { @@ -495,7 +545,9 @@ library WitnetEncodingLib { } } - function validateUrlQuery(string memory query) public pure { + function validateUrlQuery(string memory query) + public pure + { unchecked { if (bytes(query).length > 0) { for (uint ix = 0; ix < bytes(query).length; ix ++) { @@ -507,11 +559,12 @@ library WitnetEncodingLib { } } - function verifyRadonScript(bytes memory script) + function verifyRadonRequestScript(bytes memory script) public pure returns (WitnetV2.RadonDataTypes) { - return _verifyRadonScript( + // TODO: formal validation of radon script + return _verifyRadonScriptResultDataType( WitnetCBOR.fromBytes(script) ); } @@ -570,14 +623,14 @@ library WitnetEncodingLib { self.buffer.cursor = _start; } - function _verifyRadonScript(WitnetCBOR.CBOR memory self) + function _verifyRadonScriptResultDataType(WitnetCBOR.CBOR memory self) private pure returns (WitnetV2.RadonDataTypes) { if (self.majorType == WitnetCBOR.MAJOR_TYPE_ARRAY) { WitnetCBOR.CBOR[] memory items = self.readArray(); if (items.length > 1) { - return _verifyRadonScript(items[items.length - 2]); + return _verifyRadonScriptResultDataType(items[items.length - 2]); } else { return WitnetV2.RadonDataTypes.Any; } diff --git a/contracts/libs/WitnetLib.sol b/contracts/libs/WitnetLib.sol index f42db0cb6..86fe7faa5 100644 --- a/contracts/libs/WitnetLib.sol +++ b/contracts/libs/WitnetLib.sol @@ -47,6 +47,24 @@ library WitnetLib { } } + function toLowerCase(string memory str) + internal pure + returns (string memory) + { + bytes memory lowered = new bytes(bytes(str).length); + unchecked { + for (uint i = 0; i < lowered.length; i ++) { + uint8 char = uint8(bytes(str)[i]); + if (char >= 65 && char <= 90) { + lowered[i] = bytes1(char + 32); + } else { + lowered[i] = bytes1(char); + } + } + } + return string(lowered); + } + /// @notice Convert a `uint64` into a 2 characters long `string` representing its two less significant hexadecimal values. /// @param _u A `uint64` value. /// @return The `string` representing its hexadecimal value. diff --git a/contracts/libs/WitnetV2.sol b/contracts/libs/WitnetV2.sol index 0e5ce99c1..887f7c865 100644 --- a/contracts/libs/WitnetV2.sol +++ b/contracts/libs/WitnetV2.sol @@ -11,9 +11,13 @@ library WitnetV2 { error InsufficientFee(uint256 weiProvided, uint256 weiExpected); error Unauthorized(address violator); + error RadonFilterMissingArgs(uint8 opcode); + error RadonRetrievalNoSources(); - error RadonRetrievalArgsMismatch(string[][] args); + error RadonRetrievalSourcesArgsMismatch(uint expected, uint actual); + error RadonRetrievalMissingArgs(uint index, uint expected, uint actual); error RadonRetrievalResultsMismatch(uint index, uint8 read, uint8 expected); + error RadonRetrievalTooHeavy(bytes bytecode, uint weight); error RadonSlaNoReward(); error RadonSlaNoWitnesses(); @@ -22,11 +26,15 @@ library WitnetV2 { error RadonSlaLowCollateral(uint256 collateral); error UnsupportedDataRequestMethod(uint8 method, string schema, string body, string[2][] headers); + error UnsupportedDataRequestMinMaxRanks(uint8 method, uint16 min, uint16 max); error UnsupportedRadonDataType(uint8 datatype, uint256 maxlength); - error UnsupportedRadonFilter(uint8 filter, bytes args); - error UnsupportedRadonReducer(uint8 reducer); + error UnsupportedRadonFilterOpcode(uint8 opcode); + error UnsupportedRadonFilterArgs(uint8 opcode, bytes args); + error UnsupportedRadonReducerOpcode(uint8 opcode); + error UnsupportedRadonReducerScript(uint8 opcode, bytes script, uint256 offset); error UnsupportedRadonScript(bytes script, uint256 offset); error UnsupportedRadonScriptOpcode(bytes script, uint256 cursor, uint8 opcode); + error UnsupportedRadonTallyScript(bytes32 hash); function toEpoch(uint _timestamp) internal pure returns (uint) { return 1 + (_timestamp - 11111) / 15; @@ -116,6 +124,8 @@ library WitnetV2 { struct DataSource { DataRequestMethods method; RadonDataTypes resultDataType; + uint16 resultMinRank; + uint16 resultMaxRank; string url; string body; string[2][] headers; @@ -160,6 +170,7 @@ library WitnetV2 { struct RadonReducer { RadonReducerOpcodes opcode; RadonFilter[] filters; + bytes script; } enum RadonReducerOpcodes { @@ -185,14 +196,4 @@ library WitnetV2 { uint64 collateral; } - - // // ================================================================================================================ - // // --- Internal view/pure methods --------------------------------------------------------------------------------- - - // /// @notice Witnet function that computes the hash of a CBOR-encoded Data Request. - // /// @param _bytecode CBOR-encoded RADON. - // function hash(bytes memory _bytecode) internal pure returns (bytes32) { - // return sha256(_bytecode); - // } - } \ No newline at end of file From b1e2057dd54f8ab1e33527ee8ce26feddd91d908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Mon, 19 Dec 2022 17:22:36 +0100 Subject: [PATCH 036/119] fix(bytecodes): as to pass WitnetBytecodes tests --- contracts/data/WitnetBytecodesData.sol | 14 +- contracts/impls/bytecodes/WitnetBytecodes.sol | 189 +++++++++++------- contracts/interfaces/V2/IWitnetBytecodes.sol | 14 +- 3 files changed, 142 insertions(+), 75 deletions(-) diff --git a/contracts/data/WitnetBytecodesData.sol b/contracts/data/WitnetBytecodesData.sol index 99a49225d..4386aae44 100644 --- a/contracts/data/WitnetBytecodesData.sol +++ b/contracts/data/WitnetBytecodesData.sol @@ -28,12 +28,13 @@ abstract contract WitnetBytecodesData } struct RadonRetrieval { - WitnetV2.RadonDataTypes dataType; - uint16 dataMaxSize; + WitnetV2.RadonDataTypes resultDataType; + uint16 resultMaxSize; string[][] args; bytes32[] sources; bytes32 aggregator; - bytes32 tally; + bytes32 tally; + uint256 weight; } struct Database { @@ -46,6 +47,11 @@ abstract contract WitnetBytecodesData mapping (bytes32 => WitnetV2.DataSource) sources; } + constructor() { + // auto-initialize upon deployment + __bytecodes().base = address(this); + } + // ================================================================================================================ // --- Internal state-modifying functions ------------------------------------------------------------------------- @@ -68,7 +74,7 @@ abstract contract WitnetBytecodesData return __bytecodes().db; } - function __retrieval(bytes32 _drRetrievalHash) + function __retrievals(bytes32 _drRetrievalHash) internal view returns (RadonRetrieval storage _ptr) { diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol index 23b5cf882..c38591b1d 100644 --- a/contracts/impls/bytecodes/WitnetBytecodes.sol +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -21,7 +21,8 @@ contract WitnetBytecodes { using ERC165Checker for address; - using Witnet for bytes; + using Witnet for bytes; + using WitnetLib for string; using WitnetEncodingLib for WitnetV2.DataRequestMethods; using WitnetEncodingLib for WitnetV2.DataSource; using WitnetEncodingLib for WitnetV2.DataSource[]; @@ -153,16 +154,19 @@ contract WitnetBytecodes override returns (bytes memory) { - RadonRetrieval memory _retrieval = __retrieval(_hash); + RadonRetrieval memory _retrieval = __retrievals(_hash); WitnetV2.DataSource[] memory _sources = new WitnetV2.DataSource[](_retrieval.sources.length); + if (_sources.length == 0) { + revert IWitnetBytecodes.UnknownRadonRetrieval(_hash); + } for (uint _ix = 0; _ix < _retrieval.sources.length; _ix ++) { _sources[_ix] = __database().sources[_retrieval.sources[_ix]]; } return _sources.encode( _retrieval.args, - __database().reducers[_retrieval.aggregator], - __database().reducers[_retrieval.tally], - _retrieval.dataMaxSize + __database().reducers[_retrieval.aggregator].encode(), + __database().reducers[_retrieval.tally].encode(), + _retrieval.resultMaxSize ); } @@ -170,6 +174,10 @@ contract WitnetBytecodes external view returns (bytes memory) { + WitnetV2.RadonSLA storage __sla = __database().slas[_slaHash]; + if (__sla.numWitnesses == 0) { + revert IWitnetBytecodes.UnknownRadonSLA(_slaHash); + } return abi.encodePacked( bytecodeOf(_retrievalHash), __database().slas[_slaHash].encode() @@ -218,12 +226,15 @@ contract WitnetBytecodes returns (bytes32[] memory _sources) { WitnetV2.DataProvider storage __provider = __database().providers[_index]; - if (_offset + _length > __provider.totalSources) { - revert IWitnetBytecodes.IndexOutOfBounds(_offset + _length, __provider.totalSources); - } - _sources = new bytes32[](_length); - for (uint _ix = 0; _ix < _sources.length; _ix ++) { - _sources[_ix] = __provider.sources[_ix + _offset]; + uint _totalSources = __provider.totalSources; + if (_offset < _totalSources){ + if (_offset + _length > _totalSources) { + _length = _totalSources - _offset; + } + _sources = new bytes32[](_length); + for (uint _ix = 0; _ix < _sources.length; _ix ++) { + _sources[_ix] = __provider.sources[_ix + _offset]; + } } } @@ -249,32 +260,32 @@ contract WitnetBytecodes returns (WitnetV2.RadonReducer memory) { return __database().reducers[ - __retrieval(_drRetrievalHash).aggregator + __retrievals(_drRetrievalHash).aggregator ]; } - function lookupRadonRetrievalDataMaxSize(bytes32 _drRetrievalHash) + function lookupRadonRetrievalResultDataType(bytes32 _drRetrievalHash) external view override - returns (uint256) + returns (WitnetV2.RadonDataTypes) { - return __retrieval(_drRetrievalHash).dataMaxSize; + return __retrievals(_drRetrievalHash).resultDataType; } - function lookupRadonRetrievalDataType(bytes32 _drRetrievalHash) + function lookupRadonRetrievalResultMaxSize(bytes32 _drRetrievalHash) external view override - returns (WitnetV2.RadonDataTypes) + returns (uint256) { - return __retrieval(_drRetrievalHash).dataType; - } + return __retrievals(_drRetrievalHash).resultMaxSize; + } function lookupRadonRetrievalSources(bytes32 _drRetrievalHash) external view override returns (bytes32[] memory) { - return __retrieval(_drRetrievalHash).sources; + return __retrievals(_drRetrievalHash).sources; } function lookupRadonRetrievalSourcesCount(bytes32 _drRetrievalHash) @@ -282,7 +293,7 @@ contract WitnetBytecodes override returns (uint) { - return __retrieval(_drRetrievalHash).sources.length; + return __retrievals(_drRetrievalHash).sources.length; } function lookupRadonRetrievalTally(bytes32 _drRetrievalHash) @@ -291,7 +302,7 @@ contract WitnetBytecodes returns (WitnetV2.RadonReducer memory) { return __database().reducers[ - __retrieval(_drRetrievalHash).tally + __retrievals(_drRetrievalHash).tally ]; } @@ -304,7 +315,8 @@ contract WitnetBytecodes } function lookupRadonSLAReward(bytes32 _drSlaHash) - external view + public view + override returns (uint64) { WitnetV2.RadonSLA storage __sla = __database().slas[_drSlaHash]; @@ -313,6 +325,8 @@ contract WitnetBytecodes function verifyDataSource( WitnetV2.DataRequestMethods _requestMethod, + uint16 _resultMinRank, + uint16 _resultMaxRank, string memory _requestSchema, string memory _requestFQDN, string memory _requestPath, @@ -325,7 +339,14 @@ contract WitnetBytecodes virtual override returns (bytes32 _hash) { + // lower case fqdn and schema, as they ought to be case-insenstive: + _requestSchema = _requestSchema.toLowerCase(); + _requestFQDN = _requestFQDN.toLowerCase(); + + // validate input params _requestMethod.validate( + _resultMinRank, + _resultMaxRank, _requestSchema, _requestFQDN, _requestPath, @@ -334,24 +355,47 @@ contract WitnetBytecodes _requestHeaders, _requestRadonScript ); + + // compose data source struct in memory WitnetV2.DataSource memory _source = WitnetV2.DataSource({ - method: _requestMethod, - resultDataType: WitnetEncodingLib.verifyRadonScript(_requestRadonScript), - url: string(abi.encodePacked( - _requestSchema, - _requestFQDN, - bytes(_requestPath).length > 0 - ? abi.encodePacked(bytes("/"), _requestPath) - : bytes(""), - bytes(_requestQuery).length > 0 - ? abi.encodePacked("?", _requestQuery) - : bytes("") - )), - body: _requestBody, - headers: _requestHeaders, - script: _requestRadonScript + method: + _requestMethod, + + resultDataType: + WitnetEncodingLib.verifyRadonRequestScript(_requestRadonScript), + + url: + string(abi.encodePacked( + _requestSchema, + _requestFQDN, + bytes(_requestPath).length > 0 + ? abi.encodePacked(bytes("/"), _requestPath) + : bytes(""), + bytes(_requestQuery).length > 0 + ? abi.encodePacked("?", _requestQuery) + : bytes("") + )), + + body: + _requestBody, + + headers: + _requestHeaders, + + script: + _requestRadonScript, + + resultMinRank: + _resultMinRank, + + resultMaxRank: + _resultMaxRank }); - _hash = keccak256(_source.encode()); + + // generate unique hash based on source metadata: + _hash = keccak256(abi.encode(_source)); + + // add metadata to storage if new: if (__database().sources[_hash].method == WitnetV2.DataRequestMethods.Unknown) { __database().sources[_hash] = _source; __pushDataProviderSource(_requestFQDN, _hash); @@ -366,16 +410,16 @@ contract WitnetBytecodes bytes memory _bytecode = _reducer.encode(); _hash = _bytecode.hash(); WitnetV2.RadonReducer storage __reducer = __database().reducers[_hash]; - if (uint8(__reducer.opcode) == 0 && _reducer.filters.length == 0) { + if (uint8(__reducer.opcode) == 0 && __reducer.filters.length == 0) { __reducer.opcode = _reducer.opcode; __pushRadonReducerFilters(__reducer, _reducer.filters); - emit NewRadonReducerHash(_hash, _bytecode); + emit NewRadonReducerHash(_hash, _bytecode); } } function verifyRadonRetrieval( WitnetV2.RadonDataTypes _resultDataType, - uint16 _resultMaxVariableSize, + uint16 _resultMaxSize, bytes32[] memory _sourcesHashes, string[][] memory _sourcesArgs, bytes32 _aggregatorHash, @@ -386,24 +430,28 @@ contract WitnetBytecodes returns (bytes32 _hash) { // Check provided result type and result max size: - _resultDataType.validate(_resultMaxVariableSize); + // TODO: revisit + _resultMaxSize = _resultDataType.validate(_resultMaxSize); // Check that at least one source is provided; if (_sourcesHashes.length == 0) { revert WitnetV2.RadonRetrievalNoSources(); - } + } // Check that number of args arrays matches the number of sources: if ( _sourcesHashes.length != _sourcesArgs.length) { - revert WitnetV2.RadonRetrievalArgsMismatch(_sourcesArgs); + revert WitnetV2.RadonRetrievalSourcesArgsMismatch( + _sourcesHashes.length, + _sourcesArgs.length + ); } - // Check sources and tally reducers, as those unsupported in the past - // may eventually become so: + // Check sources and tally reducers: WitnetV2.RadonReducer memory _aggregator = __database().reducers[_aggregatorHash]; WitnetV2.RadonReducer memory _tally = __database().reducers[_tallyHash]; - _aggregator.validate(); - _tally.validate(); + if (_tally.script.length > 0) { + revert WitnetV2.UnsupportedRadonTallyScript(_tallyHash); + } // Check result type consistency among all sources: WitnetV2.DataSource[] memory _sources = new WitnetV2.DataSource[](_sourcesHashes.length); @@ -419,24 +467,29 @@ contract WitnetBytecodes } } - // Build RadonRetrieval bytecode: + // Build radon retrieval bytecode: bytes memory _bytecode = _sources.encode( _sourcesArgs, - _aggregator, - _tally, - _resultMaxVariableSize + _aggregator.encode(), + _tally.encode(), + _resultMaxSize ); + if (_bytecode.length > 65535) { + revert WitnetV2.RadonRetrievalTooHeavy(_bytecode, _bytecode.length); + } - // Calculate hash and add to storage if new: + // Calculate hash and add metadata to storage if new: _hash = _bytecode.hash(); + if (__database().retrievals[_hash].sources.length == 0) { __database().retrievals[_hash] = RadonRetrieval({ - dataType: _resultDataType, - dataMaxSize: _resultMaxVariableSize, + resultDataType: _resultDataType, + resultMaxSize: _resultMaxSize, args: _sourcesArgs, sources: _sourcesHashes, aggregator: _aggregatorHash, - tally: _tallyHash + tally: _tallyHash, + weight: uint16(_bytecode.length) }); emit NewRadonRetrievalHash(_hash, _bytecode); } @@ -478,17 +531,19 @@ contract WitnetBytecodes virtual returns (bytes32 _hash) { - _hash = keccak256(abi.encodePacked(_fqdn)); - uint _index = __database().providersIndex[_hash]; - if (_index == 0) { - _index = ++ __bytecodes().totalDataProviders; - __database().providersIndex[keccak256(bytes(_fqdn))] = _index; - __database().providers[_index].fqdn = _fqdn; - emit NewDataProvider(_index); + if (bytes(_fqdn).length > 0) { + _hash = keccak256(abi.encodePacked(_fqdn)); + uint _index = __database().providersIndex[_hash]; + if (_index == 0) { + _index = ++ __bytecodes().totalDataProviders; + __database().providersIndex[keccak256(bytes(_fqdn))] = _index; + __database().providers[_index].fqdn = _fqdn; + emit NewDataProvider(_index); + } + __database().providers[_index].sources[ + __database().providers[_index].totalSources ++ + ] = _sourceHash; } - __database().providers[_index].sources[ - __database().providers[_index].totalSources ++ - ] = _sourceHash; } function __pushRadonReducerFilters( diff --git a/contracts/interfaces/V2/IWitnetBytecodes.sol b/contracts/interfaces/V2/IWitnetBytecodes.sol index 71c47dd37..521e115f0 100644 --- a/contracts/interfaces/V2/IWitnetBytecodes.sol +++ b/contracts/interfaces/V2/IWitnetBytecodes.sol @@ -6,7 +6,8 @@ import "../../libs/WitnetV2.sol"; interface IWitnetBytecodes { - error IndexOutOfBounds(uint256 index, uint256 range); + error UnknownRadonRetrieval(bytes32 hash); + error UnknownRadonSLA(bytes32 hash); event NewDataProvider(uint256 index); event NewDataSourceHash(bytes32 hash); @@ -25,8 +26,8 @@ interface IWitnetBytecodes { function lookupDataSource(bytes32 hash) external view returns (WitnetV2.DataSource memory); function lookupRadonReducer(bytes32 hash) external view returns (WitnetV2.RadonReducer memory); function lookupRadonRetrievalAggregator(bytes32 hash) external view returns (WitnetV2.RadonReducer memory); - function lookupRadonRetrievalDataMaxSize(bytes32 hash) external view returns (uint256); - function lookupRadonRetrievalDataType(bytes32 hash) external view returns (WitnetV2.RadonDataTypes); + function lookupRadonRetrievalResultMaxSize(bytes32 hash) external view returns (uint256); + function lookupRadonRetrievalResultDataType(bytes32 hash) external view returns (WitnetV2.RadonDataTypes); function lookupRadonRetrievalSources(bytes32 hash) external view returns (bytes32[] memory); function lookupRadonRetrievalSourcesCount(bytes32 hash) external view returns (uint); function lookupRadonRetrievalTally(bytes32 hash) external view returns (WitnetV2.RadonReducer memory); @@ -35,6 +36,8 @@ interface IWitnetBytecodes { function verifyDataSource( WitnetV2.DataRequestMethods requestMethod, + uint16 resultMinRank, + uint16 resultMaxRank, string calldata requestSchema, string calldata requestFQDN, string calldata requestPath, @@ -43,15 +46,18 @@ interface IWitnetBytecodes { string[2][] calldata requestHeaders, bytes calldata requestRadonScript ) external returns (bytes32); + function verifyRadonReducer(WitnetV2.RadonReducer calldata reducer) external returns (bytes32); + function verifyRadonRetrieval( WitnetV2.RadonDataTypes resultDataType, - uint16 resultMaxVariableSize, + uint16 resultMaxSize, bytes32[] calldata sources, string[][] calldata sourcesArgs, bytes32 aggregatorHash, bytes32 tallyHash ) external returns (bytes32); + function verifyRadonSLA(WitnetV2.RadonSLA calldata drSLA) external returns (bytes32); function totalDataProviders() external view returns (uint); From bd8293c6808f842232c7929d3273bbfa5808d3c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Mon, 19 Dec 2022 18:41:47 +0100 Subject: [PATCH 037/119] feat(bytecodes): hashWeightRewardOf(b32, b32) --- contracts/impls/bytecodes/WitnetBytecodes.sol | 26 ++++++++++ contracts/interfaces/V2/IWitnetBytecodes.sol | 11 +++-- test/witnet_bytecodes.test.js | 47 ++++++++++++++++--- 3 files changed, 75 insertions(+), 9 deletions(-) diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol index c38591b1d..91985982e 100644 --- a/contracts/impls/bytecodes/WitnetBytecodes.sol +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -195,6 +195,32 @@ contract WitnetBytecodes )); } + function hashWeightWitsOf( + bytes32 _retrievalHash, + bytes32 _slaHash + ) + external view + virtual override + returns (bytes32, uint32, uint256) + { + WitnetV2.RadonSLA storage __sla = __database().slas[_slaHash]; + if (__sla.numWitnesses == 0) { + revert IWitnetBytecodes.UnknownRadonSLA(_slaHash); + } + RadonRetrieval storage __retrieval = __retrievals(_retrievalHash); + if (__retrieval.weight == 0) { + revert IWitnetBytecodes.UnknownRadonRetrieval(_retrievalHash); + } + return ( + hashOf(_retrievalHash, _slaHash), + uint32(__retrieval.weight + + __sla.numWitnesses * 636 + + 100 + ), + __sla.numWitnesses * uint(__sla.witnessReward) + ); + } + function lookupDataProvider(uint256 _index) external view override diff --git a/contracts/interfaces/V2/IWitnetBytecodes.sol b/contracts/interfaces/V2/IWitnetBytecodes.sol index 521e115f0..dc7d5a929 100644 --- a/contracts/interfaces/V2/IWitnetBytecodes.sol +++ b/contracts/interfaces/V2/IWitnetBytecodes.sol @@ -15,10 +15,15 @@ interface IWitnetBytecodes { event NewRadonRetrievalHash(bytes32 hash, bytes bytecode); event NewRadonSLAHash(bytes32 hash, bytes bytecode); - function bytecodeOf(bytes32 _drRetrievalHash) external view returns (bytes memory); - function bytecodeOf(bytes32 _drRetrievalHash, bytes32 _drSlaHash) external view returns (bytes memory); + function bytecodeOf(bytes32 drRetrievalHash) external view returns (bytes memory); + function bytecodeOf(bytes32 drRetrievalHash, bytes32 drSlaHash) external view returns (bytes memory); - function hashOf(bytes32 _drRetrievalHash, bytes32 _drSlaHash) external pure returns (bytes32 _drQueryHash); + function hashOf(bytes32 drRetrievalHash, bytes32 drSlaHash) external pure returns (bytes32 drQueryHash); + function hashWeightWitsOf(bytes32 drRetrievalHash, bytes32 drSlaHash) external view returns ( + bytes32 drQueryHash, + uint32 drQueryWeight, + uint256 drQueryWits + ); function lookupDataProvider(uint256 index) external view returns (string memory, uint); function lookupDataProviderIndex(string calldata fqdn) external view returns (uint); diff --git a/test/witnet_bytecodes.test.js b/test/witnet_bytecodes.test.js index 193e0fd18..80ee90887 100644 --- a/test/witnet_bytecodes.test.js +++ b/test/witnet_bytecodes.test.js @@ -105,6 +105,8 @@ contract('WitnetBytecodes', (accounts) => { var binanceTickerHash var uniswapToken0PriceHash var uniswapToken1PriceHash + var heavyRetrievalHash + var heavyRetrievalBytecode var rngHash var rngBytecode @@ -491,12 +493,10 @@ contract('WitnetBytecodes', (accounts) => { [ // sources binanceTickerHash, binanceTickerHash, - // binanceTickerHash, ], [ [ "BTC", "USD" ], // binance ticker args [ "BTC", "USD" ], // binance ticker args - // [ "BTC", "USD" ], // binance ticker args ], stdev15ReducerHash, // aggregator stdev25ReducerHash, // tally @@ -549,7 +549,6 @@ contract('WitnetBytecodes', (accounts) => { tx.receipt, "NewRadonRetrievalHash" ) - console.log(tx.logs[0].args) fraxUsdtPriceFeedHash = tx.logs[0].args.hash fraxUsdtPriceFeedBytecode = tx.logs[0].args.bytecode }) @@ -581,7 +580,8 @@ contract('WitnetBytecodes', (accounts) => { tx.receipt, "NewRadonRetrievalHash" ) - console.log(tx.logs[0].args) + heavyRetrievalHash = tx.logs[0].args.hash + heavyRetrievalBytecode = tx.logs[0].args.bytecode }) }) @@ -735,12 +735,47 @@ contract('WitnetBytecodes', (accounts) => { await bytecodes.bytecodeOf(btcUsdPriceFeedHash, slaHash) }) it('returns expected bytecode if getting it offchain from known radon retrieval and sla', async () => { - const bytecode = await bytecodes.bytecodeOf.call(btcUsdPriceFeedHash, slaHash) + const bytecode = await bytecodes.bytecodeOf.call(heavyRetrievalHash, slaHash) assert.equal( - btcUsdPriceFeedBytecode + slaBytecode.slice(2), + heavyRetrievalBytecode + slaBytecode.slice(2), bytecode ) }) + }) + }) + }) + + context("hashOf(..)", async () => { + it("hashing unknown radon retrieval doesn't revert", async () => { + await bytecodes.hashOf("0x", slaHash) + }) + it("hashing unknown radon sla doesn't revert", async () => { + await bytecodes.hashOf(btcUsdPriceFeedHash, "0x0") + }) + it("hashing of known radon retrieval and sla works", async () => { + await bytecodes.hashOf(btcUsdPriceFeedHash, slaHash) + }) + }) + + context("hashWeightRewardOf(..)", async () => { + it("hashing unknown radon retrieval reverts", async () => { + await expectRevertCustomError( + WitnetBytecodes, + bytecodes.hashWeightWitsOf("0x0", slaHash), + "UnknownRadonRetrieval" + ) + }) + it("hashing unknown radon sla reverts", async () => { + await expectRevertCustomError( + WitnetBytecodes, + bytecodes.hashWeightWitsOf(btcUsdPriceFeedHash, "0x0"), + "UnknownRadonSLA" + ) + }) + it("hashing of known radon retrieval and sla works", async () => { + await bytecodes.hashWeightWitsOf( + heavyRetrievalHash, slaHash + ) }) }) From 008f3b390b655dd104555b87153eed9e5c9c6db3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Mon, 19 Dec 2022 18:43:47 +0100 Subject: [PATCH 038/119] chore(libs): adapt tests to latest changes --- test/TestWitnetBuffer.sol | 4 ++++ test/TestWitnetEncodingLib.sol | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/test/TestWitnetBuffer.sol b/test/TestWitnetBuffer.sol index e431f9af4..6a2c17377 100644 --- a/test/TestWitnetBuffer.sol +++ b/test/TestWitnetBuffer.sol @@ -77,6 +77,10 @@ contract TestWitnetBuffer { ); } + function testReplaceMissingArgs() external { + // TODO + } + function testReplace0Args() external { string memory input = "In a village of La Mancha, the name of which I have no desire to call to mind, there lived not long since one of those gentlemen that keep a lance in the lance-rack, an old buckler, a lean hack, and a greyhound for coursing"; string[] memory args = new string[](1); diff --git a/test/TestWitnetEncodingLib.sol b/test/TestWitnetEncodingLib.sol index 25206fe27..ce4ddc3d4 100644 --- a/test/TestWitnetEncodingLib.sol +++ b/test/TestWitnetEncodingLib.sol @@ -283,7 +283,7 @@ contract TestWitnetEncodingLib { function testVerifyRadonScriptOk1() external { Assert.equal( - uint(WitnetEncodingLib.verifyRadonScript(hex"861877821866646461746182186664706f6f6c8218646b746f6b656e3150726963658218571a000f4240185b")), + uint(WitnetEncodingLib.verifyRadonRequestScript(hex"861877821866646461746182186664706f6f6c8218646b746f6b656e3150726963658218571a000f4240185b")), uint(WitnetV2.RadonDataTypes.Integer), "unexpected result data type" ); @@ -291,7 +291,7 @@ contract TestWitnetEncodingLib { function testVerifyRadonScriptOk2() external { Assert.equal( - uint(WitnetEncodingLib.verifyRadonScript(hex"80")), + uint(WitnetEncodingLib.verifyRadonRequestScript(hex"80")), uint(WitnetV2.RadonDataTypes.Any), "unexpected result data type" ); From 5d9824f0f939bbfc17373f2da732871ae7e93761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 28 Dec 2022 15:23:20 +0100 Subject: [PATCH 039/119] chore: rename migration scripts --- migrations/scripts/{1_deploy_wrb.js => 1_deploy_witnet.js} | 0 migrations/scripts/{2_deploy_rng.js => 2_deploy_randomness.js} | 0 migrations/scripts/{3_deploy_wpr.js => 3_deploy_router.js} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename migrations/scripts/{1_deploy_wrb.js => 1_deploy_witnet.js} (100%) rename migrations/scripts/{2_deploy_rng.js => 2_deploy_randomness.js} (100%) rename migrations/scripts/{3_deploy_wpr.js => 3_deploy_router.js} (100%) diff --git a/migrations/scripts/1_deploy_wrb.js b/migrations/scripts/1_deploy_witnet.js similarity index 100% rename from migrations/scripts/1_deploy_wrb.js rename to migrations/scripts/1_deploy_witnet.js diff --git a/migrations/scripts/2_deploy_rng.js b/migrations/scripts/2_deploy_randomness.js similarity index 100% rename from migrations/scripts/2_deploy_rng.js rename to migrations/scripts/2_deploy_randomness.js diff --git a/migrations/scripts/3_deploy_wpr.js b/migrations/scripts/3_deploy_router.js similarity index 100% rename from migrations/scripts/3_deploy_wpr.js rename to migrations/scripts/3_deploy_router.js From 79e50184b6051a3f9907ea48be81475176e0ae18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 28 Dec 2022 15:25:55 +0100 Subject: [PATCH 040/119] chore: add migration script for WitnetBytecodes, proxy and encoding lib --- migrations/scripts/4_deploy_bytecodes.js | 90 ++++++++++++++++++++++++ package.json | 5 +- 2 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 migrations/scripts/4_deploy_bytecodes.js diff --git a/migrations/scripts/4_deploy_bytecodes.js b/migrations/scripts/4_deploy_bytecodes.js new file mode 100644 index 000000000..8ad70fef7 --- /dev/null +++ b/migrations/scripts/4_deploy_bytecodes.js @@ -0,0 +1,90 @@ +const fs = require("fs") +const { merge } = require("lodash") + +const addresses = require("../witnet.addresses") +const package = require ("../../package") +const settings = require("../witnet.settings") +const utils = require("../../scripts/utils") + +const WitnetBytecodesProxy = artifacts.require("WitnetProxy") +const WitnetBytecodesImplementation = artifacts.require("WitnetBytecodes") +const WitnetEncodingLib = artifacts.require("WitnetEncodingLib") + +module.exports = async function (deployer, network, accounts) { + if (network !== "test") { + const isDryRun = network.split("-")[1] === "fork" || network.split("-")[0] === "develop" + const ecosystem = utils.getRealmNetworkFromArgs()[0] + network = network.split("-")[0] + + if (!addresses[ecosystem]) addresses[ecosystem] = {} + if (!addresses[ecosystem][network]) addresses[ecosystem][network] = {} + + console.info() + + var proxy + if (utils.isNullAddress(addresses[ecosystem][network]?.WitnetBytecodes)) { + await deployer.deploy(WitnetBytecodesProxy) + proxy = await WitnetBytecodesProxy.deployed() + addresses[ecosystem][network].WitnetBytecodes = proxy.address + if (!isDryRun) { + saveAddresses(addresses) + } + } else { + proxy = await WitnetBytecodesProxy.at(addresses[ecosystem][network].WitnetBytecodes) + console.info(` Skipped: 'WitnetBytecodesProxy' deployed at ${proxy.address}`) + } + + var bytecodes + if ( + utils.isNullAddress(addresses[ecosystem][network]?.WitnetBytecodesImplementation) + || utils.isNullAddress(addresses[ecosystem][network]?.WitnetEncodingLib) + ) { + if (utils.isNullAddress(addresses[ecosystem][network]?.WitnetEncodingLib)) { + await deployer.deploy(WitnetEncodingLib) + await deployer.link(WitnetEncodingLib, [WitnetBytecodesImplementation]) + addresses[ecosystem][network].WitnetEncodingLib = WitnetEncodingLib.address + if (!isDryRun) { + saveAddresses(addresses) + } + } else { + console.info(` Skipped: 'WitnetEncodingLib' presumably deployed at ${addresses[ecosystem][network].WitnetEncodingLib}`) + } + await deployer.deploy( + WitnetBytecodesImplementation, + true, + utils.fromAscii(package.version) + ) + bytecodes = await WitnetBytecodesImplementation.deployed() + addresses[ecosystem][network].WitnetBytecodesImplementation = bytecodes.address + if (!isDryRun) { + saveAddresses(addresses) + } + } else { + bytecodes = await WitnetBytecodesImplementation.at(addresses[ecosystem][network].WitnetBytecodesImplementation) + console.info(` Skipped: 'WitnetBytecodesImplementation' deployed at ${bytecodes.address}`) + } + + var implementation = await proxy.implementation() + if (implementation.toLowerCase() !== bytecodes.address.toLowerCase()) { + console.info() + console.info(" > WitnetBytecodesImplementation:", bytecodes.address, `(v${await bytecodes.version()})`) + console.info(" > WitnetBytecodesProxy:", proxy.address) + console.info(" > WitnetBytecodesProxy.implementation::", implementation) + const answer = await utils.prompt(` > Upgrade the proxy ? [y/N] `) + if (["y", "yes"].includes(answer.toLowerCase().trim())) { + await proxy.upgradeTo(bytecodes.address, "0x") + console.info(" > Done.") + } else { + console.info(" > Not upgraded.") + } + } + } +} + +function saveAddresses(addrs) { + fs.writeFileSync( + "./migrations/witnet.addresses.json", + JSON.stringify(addrs, null, 4), + { flag: 'w+'} + ) +} \ No newline at end of file diff --git a/package.json b/package.json index 6eb1ba6bc..56c53ce69 100644 --- a/package.json +++ b/package.json @@ -12,13 +12,14 @@ "console": "truffle console", "coverage": "solidity-coverage", "flatten": "node ./scripts/flatten.js 2>&1", - "flatten:witnet": "npm run clean && npm run flatten:witnet:libs && npm run flatten:witnet:proxy && npm run flatten:witnet:boards && npm run flatten:witnet:rng && npm run flatten:witnet:registry", + "flatten:witnet": "npm run clean && npm run flatten:witnet:libs && npm run flatten:witnet:proxy && npm run flatten:witnet:boards && npm run flatten:witnet:rng && npm run flatten:witnet:registry && npm run flatten:witnet:bytecodes", "flatten:witnet:rng": "npm run flatten contracts/apps/WitnetRandomness.sol && npm run flatten contracts/requests/WitnetRequestRandomness.sol", "flatten:witnet:libs": "npm run flatten contracts/libs/WitnetLib.sol", "flatten:witnet:proxy": "npm run flatten contracts/impls/WitnetProxy.sol", "flatten:witnet:registry": "npm run flatten contracts/apps/WitnetPriceRouter.sol", "flatten:witnet:boards": "npm run flatten:witnet:boards:trustable", - "flatten:witnet:boards:trustable": "npm run flatten contracts/impls/trustable/", + "flatten:witnet:boards:trustable": "npm run flatten contracts/impls/boards/trustable/", + "flatten:witnet:bytecodes": "npm run flatten contracts/impls/bytecodes", "fmt:js": "eslint \"**/*.js\"", "fmt:sol": "solhint --max-warnings 0 \"contracts/**/*.sol\" && solhint \"test/**/*.sol\" && solhint \"flattened/**/*.sol\"", "fmt!:js": "eslint \"**/*.js\" --fix", From 106f03fd9ba7ff3de6763fc3848ac018f1bb206b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 28 Dec 2022 15:27:32 +0100 Subject: [PATCH 041/119] feat: deploy WitnetBytecodes on polygon.goerli --- migrations/witnet.addresses.json | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/migrations/witnet.addresses.json b/migrations/witnet.addresses.json index 17df28251..11e11dcdd 100644 --- a/migrations/witnet.addresses.json +++ b/migrations/witnet.addresses.json @@ -75,10 +75,10 @@ "WitnetRandomness": "0xbD804467270bCD832b4948242453CA66972860F5" }, "celo.mainnet": { - "WitnetLib": "0x46FF6e05fd0a5fb4D794B99eCAE41c43f4D62d15", - "WitnetPriceRouter": "0x931673904eB6E69D775e35F522c0EA35575297Cb", - "WitnetRandomness": "0x3D7Ccf6518deBE3d2fd20c4b6AEc3FD904c0Ad29", - "WitnetRequestBoard": "0x03E709E6422E30C033456FCde38C70A12553E468" + "WitnetLib": "0x46FF6e05fd0a5fb4D794B99eCAE41c43f4D62d15", + "WitnetPriceRouter": "0x931673904eB6E69D775e35F522c0EA35575297Cb", + "WitnetRandomness": "0x3D7Ccf6518deBE3d2fd20c4b6AEc3FD904c0Ad29", + "WitnetRequestBoard": "0x03E709E6422E30C033456FCde38C70A12553E468" } }, "conflux": { @@ -139,14 +139,8 @@ "dogechain.testnet": { "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", "WitnetPriceRouter": "0x9E943Ab1FD0D35B3BaDe31AA78D60C485EA1a604", - "WitnetRandomness": "0xD9465D38f50f364b3263Cb219e58d4dB2D584530", + "WitnetRandomness": "0xD9465D38f50f364b3263Cb219e58d4dB2D584530", "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" - }, - "dogechain.mainnet": { - "WitnetParserLib": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", - "WitnetPriceRouter": "0xD7f7933992c25A504e9Ddf7e76a3c1D6c432b25D", - "WitnetRandomness": "0x7FdB9CDe98967140a5627dA5221DBC03Bd8e8ad5", - "WitnetRequestBoard": "0xF2712e7114A237625EFC8bBA6a6ed1Bb8b6029c9" } }, "harmony": { @@ -284,6 +278,9 @@ }, "polygon": { "polygon.goerli": { + "WitnetBytecodes": "0x6156a1F312EdA146a2981cd5ed6B8688f1073F08", + "WitnetBytecodesImplementation": "0xf31E26b9f1E1ddb1e85814d52dF102b549cDD74B", + "WitnetEncodingLib": "0xCB1796E56d4246A013F12a24f2a23021b58019A0", "WitnetLib": "0x70767887abBF23291B24620dE6bcEBe8d8F1e6F3", "WitnetPriceRouter": "0x6d5544ca5b35bf2e7a78ace4E7B8d191fe5C9FAb", "WitnetRandomness": "0x0178287DfECA2F13F342512282bc8B59e774aE43", @@ -360,4 +357,4 @@ "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" } } -} +} \ No newline at end of file From c2688cdee496d42c7684e1e8bee26f06f8a54ded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 28 Dec 2022 17:19:46 +0100 Subject: [PATCH 042/119] feat: implement lookupDataSourceResultDataType(hash) --- contracts/impls/bytecodes/WitnetBytecodes.sol | 9 +++++++++ contracts/interfaces/V2/IWitnetBytecodes.sol | 1 + migrations/scripts/4_deploy_bytecodes.js | 9 +++++++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol index 91985982e..3e8f44e5c 100644 --- a/contracts/impls/bytecodes/WitnetBytecodes.sol +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -215,6 +215,7 @@ contract WitnetBytecodes hashOf(_retrievalHash, _slaHash), uint32(__retrieval.weight + __sla.numWitnesses * 636 + // + (8 + 2 + 8 + 4 + 8) + 100 ), __sla.numWitnesses * uint(__sla.witnessReward) @@ -271,6 +272,14 @@ contract WitnetBytecodes { return __database().sources[_hash]; } + + function lookupDataSourceResultDataType(bytes32 _hash) + external view + override + returns (WitnetV2.RadonDataTypes) + { + return __database().sources[_hash].resultDataType; + } function lookupRadonReducer(bytes32 _hash) external view diff --git a/contracts/interfaces/V2/IWitnetBytecodes.sol b/contracts/interfaces/V2/IWitnetBytecodes.sol index dc7d5a929..dff3ca9f3 100644 --- a/contracts/interfaces/V2/IWitnetBytecodes.sol +++ b/contracts/interfaces/V2/IWitnetBytecodes.sol @@ -29,6 +29,7 @@ interface IWitnetBytecodes { function lookupDataProviderIndex(string calldata fqdn) external view returns (uint); function lookupDataProviderSources(uint256 index, uint256 offset, uint256 length) external view returns (bytes32[] memory); function lookupDataSource(bytes32 hash) external view returns (WitnetV2.DataSource memory); + function lookupDataSourceResultDataType(bytes32 hash) external view returns (WitnetV2.RadonDataTypes); function lookupRadonReducer(bytes32 hash) external view returns (WitnetV2.RadonReducer memory); function lookupRadonRetrievalAggregator(bytes32 hash) external view returns (WitnetV2.RadonReducer memory); function lookupRadonRetrievalResultMaxSize(bytes32 hash) external view returns (uint256); diff --git a/migrations/scripts/4_deploy_bytecodes.js b/migrations/scripts/4_deploy_bytecodes.js index 8ad70fef7..04ca0c531 100644 --- a/migrations/scripts/4_deploy_bytecodes.js +++ b/migrations/scripts/4_deploy_bytecodes.js @@ -41,14 +41,19 @@ module.exports = async function (deployer, network, accounts) { ) { if (utils.isNullAddress(addresses[ecosystem][network]?.WitnetEncodingLib)) { await deployer.deploy(WitnetEncodingLib) - await deployer.link(WitnetEncodingLib, [WitnetBytecodesImplementation]) addresses[ecosystem][network].WitnetEncodingLib = WitnetEncodingLib.address if (!isDryRun) { saveAddresses(addresses) } } else { - console.info(` Skipped: 'WitnetEncodingLib' presumably deployed at ${addresses[ecosystem][network].WitnetEncodingLib}`) + WitnetEncodingLib.address = addresses[ecosystem][network].WitnetEncodingLib + await WitnetEncodingLib.deployed() + console.info(` Skipped: 'WitnetEncodingLib' deployed at ${addresses[ecosystem][network].WitnetEncodingLib}`) } + await deployer.link( + WitnetEncodingLib, + [ WitnetBytecodesImplementation ] + ) await deployer.deploy( WitnetBytecodesImplementation, true, From afacaca5a37ea1e07746d978dade50c298d5ec48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 28 Dec 2022 17:57:11 +0100 Subject: [PATCH 043/119] feat(requests): implement WitnetRequestTemplate based on WitnetBytecodes --- contracts/requests/WitnetRequestTemplate.sol | 198 +++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 contracts/requests/WitnetRequestTemplate.sol diff --git a/contracts/requests/WitnetRequestTemplate.sol b/contracts/requests/WitnetRequestTemplate.sol new file mode 100644 index 000000000..d4815cb46 --- /dev/null +++ b/contracts/requests/WitnetRequestTemplate.sol @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.7.0 <0.9.0; +pragma experimental ABIEncoderV2; + +import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; + +import "../UsingWitnet.sol"; +import "../interfaces/V2/IWitnetBytecodes.sol"; +import "../interfaces/IWitnetRequest.sol"; +import "../patterns/Clonable.sol"; + +abstract contract WitnetRequestTemplate + is + IWitnetRequest, + Clonable, + UsingWitnet +{ + using ERC165Checker for address; + + struct InitData { + string[][] args; + bytes32 tallyHash; + bytes32 slaHash; + uint16 resultMaxSize; + } + + /// @notice Reference to Witnet Data Requests Bytecode Registry + IWitnetBytecodes immutable public registry; + + /// @notice Witnet Data Request bytecode after inserting string arguments. + bytes public override bytecode; + + /// @notice SHA-256 hash of the Witnet Data Request bytecode. + bytes32 public override hash; + + /// @notice Array of source hashes encoded as bytes. + bytes /*immutable*/ public template; + + /// @notice Array of string arguments passed upon initialization. + string[][] public args; + + /// @notice Result data type. + WitnetV2.RadonDataTypes immutable public resultDataType; + + /// @notice Aggregator reducer hash. + bytes32 immutable public aggregatorHash; + + /// @notice Tally reducer hash. + bytes32 public tallyHash; + + /// @notice Radon Retrieval hash. + bytes32 public retrievalHash; + + /// @notice Radon SLA hash. + bytes32 public slaHash; + + /// @notice Unique id of last request attempt. + uint256 public postId; + + modifier initialized { + if (retrievalHash == bytes32(0)) { + revert("WitnetRequestTemplate: not initialized"); + } + _; + } + + modifier notPending { + require(!pending(), "WitnetRequestTemplate: pending"); + _; + } + + constructor( + WitnetRequestBoard _wrb, + IWitnetBytecodes _registry, + WitnetV2.RadonDataTypes _resultDataType, + bytes32[] memory _sources, + bytes32 _aggregatorHash + ) + UsingWitnet(_wrb) + { + require( + address(_registry).supportsInterface(type(IWitnetBytecodes).interfaceId), + "WitnetRequestTemplate: uncompliant registry" + ); + require( + _sources.length > 0, + "WitnetRequestTemplate: no sources" + ); + assert(_aggregatorHash != bytes32(0)); + for (uint i = 0; i < _sources.length; i ++) { + require( + _registry.lookupDataSourceResultDataType(_sources[i]) == _resultDataType, + "WitnetRequestTemplate: mismatching sources" + ); + } + aggregatorHash = _aggregatorHash; + registry = _registry; + resultDataType = _resultDataType; + template = abi.encode(_sources); + } + + /// ======================================================================= + /// --- WitnetRequestTemplate interface ----------------------------------- + + receive () external payable {} + + function _decode(WitnetCBOR.CBOR memory) virtual internal view returns (bytes memory); + + function getRadonAggregator() + external view + returns (WitnetV2.RadonReducer memory) + { + return registry.lookupRadonRetrievalAggregator(retrievalHash); + } + + function getRadonTally() + external view + initialized + returns (WitnetV2.RadonReducer memory) + { + return registry.lookupRadonRetrievalTally(retrievalHash); + } + + function getRadonSLA() + external view + initialized + returns (WitnetV2.RadonSLA memory) + { + return registry.lookupRadonSLA(slaHash); + } + + function sources() + external view + returns (bytes32[] memory) + { + return abi.decode(template, (bytes32[])); + } + + function post() virtual external payable returns (uint256 _usedFunds) { + if ( + postId == 0 + || ( + _witnetCheckResultAvailability(postId) + && witnet.isError(_witnetReadResult(postId)) + ) + ) { + (postId, _usedFunds) = _witnetPostRequest(this); + if (_usedFunds < msg.value) { + payable(msg.sender).transfer(msg.value - _usedFunds); + } + } + } + + function pending() virtual public view returns (bool) { + return ( + postId == 0 + || _witnetCheckResultAvailability(postId) + ); + } + + function read() + virtual + external view + notPending + returns (bool, bytes memory) + { + require(!pending(), "WitnetRequestTemplate: pending"); + Witnet.Result memory _result = _witnetReadResult(postId); + return (_result.success, _decode(_result.value)); + } + + + // ================================================================================================================ + // --- Implement 'Initializable' functions ------------------------------------------------------------------------ + + function initialize(bytes memory _initData) external { + require(retrievalHash == 0, "WitnetRequestTemplate: already initialized"); + bytes32[] memory _sources = abi.decode(WitnetRequestTemplate(payable(self)).template(), (bytes32[])); + InitData memory _init = abi.decode(_initData, (InitData)); + args = _init.args; + bytes32 _retrievalHash = registry.verifyRadonRetrieval( + resultDataType, + _init.resultMaxSize, + _sources, + _init.args, + aggregatorHash, + _init.tallyHash + ); + retrievalHash = _retrievalHash; + slaHash = _init.slaHash; + tallyHash = _init.tallyHash; + template = abi.encode(_sources); + bytecode = registry.bytecodeOf(retrievalHash, _init.slaHash); + hash = sha256(bytecode); + } + +} \ No newline at end of file From da53838e26ddd75b356b4577b6fd959aee8b7e40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 29 Dec 2022 13:48:46 +0100 Subject: [PATCH 044/119] fix(libs): disable formal validation of data source url parts --- contracts/impls/bytecodes/WitnetBytecodes.sol | 12 +++++++++--- contracts/libs/WitnetEncodingLib.sol | 8 +++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol index 3e8f44e5c..88e2b6d62 100644 --- a/contracts/impls/bytecodes/WitnetBytecodes.sol +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -268,9 +268,12 @@ contract WitnetBytecodes function lookupDataSource(bytes32 _hash) external view override - returns (WitnetV2.DataSource memory) + returns (WitnetV2.DataSource memory _source) { - return __database().sources[_hash]; + _source = __database().sources[_hash]; + if (_source.method == WitnetV2.DataRequestMethods.Unknown) { + revert IWitnetBytecodes.UnknownDataSource(_hash); + } } function lookupDataSourceResultDataType(bytes32 _hash) @@ -278,6 +281,9 @@ contract WitnetBytecodes override returns (WitnetV2.RadonDataTypes) { + if (__database().sources[_hash].method == WitnetV2.DataRequestsMethods.Unknown) { + revert IWitnetBytecodes.UnknownDataSource(_hash); + } return __database().sources[_hash].resultDataType; } @@ -285,7 +291,7 @@ contract WitnetBytecodes external view override returns (WitnetV2.RadonReducer memory) - { + { return __database().reducers[_hash]; } diff --git a/contracts/libs/WitnetEncodingLib.sol b/contracts/libs/WitnetEncodingLib.sol index 05bb51b74..59b3dc06a 100644 --- a/contracts/libs/WitnetEncodingLib.sol +++ b/contracts/libs/WitnetEncodingLib.sol @@ -378,9 +378,11 @@ library WitnetEncodingLib { resultMaxRank ); } - validateUrlHost(host); - validateUrlPath(path); - validateUrlQuery(query); + // Cannot perform formal validation of url parts, as they + // could templatized. + // NOP: validateUrlHost(host); + // NOP: validateUrlPath(path); + // NOP: validateUrlQuery(query); } function validate( From 4f8d9f908efeba484d55cca6dc35aaaa1d5438b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 29 Dec 2022 14:04:10 +0100 Subject: [PATCH 045/119] feat(bytecodes): revert if looking up for unknown data source --- contracts/impls/bytecodes/WitnetBytecodes.sol | 2 +- contracts/interfaces/V2/IWitnetBytecodes.sol | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol index 88e2b6d62..eaf3ba32e 100644 --- a/contracts/impls/bytecodes/WitnetBytecodes.sol +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -281,7 +281,7 @@ contract WitnetBytecodes override returns (WitnetV2.RadonDataTypes) { - if (__database().sources[_hash].method == WitnetV2.DataRequestsMethods.Unknown) { + if (__database().sources[_hash].method == WitnetV2.DataRequestMethods.Unknown) { revert IWitnetBytecodes.UnknownDataSource(_hash); } return __database().sources[_hash].resultDataType; diff --git a/contracts/interfaces/V2/IWitnetBytecodes.sol b/contracts/interfaces/V2/IWitnetBytecodes.sol index dff3ca9f3..09d6e9644 100644 --- a/contracts/interfaces/V2/IWitnetBytecodes.sol +++ b/contracts/interfaces/V2/IWitnetBytecodes.sol @@ -6,6 +6,7 @@ import "../../libs/WitnetV2.sol"; interface IWitnetBytecodes { + error UnknownDataSource(bytes32 hash); error UnknownRadonRetrieval(bytes32 hash); error UnknownRadonSLA(bytes32 hash); From cd4f681223d3a3b257cce71750f3e9908a652105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 29 Dec 2022 14:08:14 +0100 Subject: [PATCH 046/119] chore(requests): polish WitnetRequestTemplate --- contracts/requests/WitnetRequestTemplate.sol | 103 +++++++++++-------- 1 file changed, 61 insertions(+), 42 deletions(-) diff --git a/contracts/requests/WitnetRequestTemplate.sol b/contracts/requests/WitnetRequestTemplate.sol index d4815cb46..ad4fc0279 100644 --- a/contracts/requests/WitnetRequestTemplate.sol +++ b/contracts/requests/WitnetRequestTemplate.sol @@ -21,7 +21,7 @@ abstract contract WitnetRequestTemplate struct InitData { string[][] args; bytes32 tallyHash; - bytes32 slaHash; + bytes32 __slaHash; uint16 resultMaxSize; } @@ -34,7 +34,7 @@ abstract contract WitnetRequestTemplate /// @notice SHA-256 hash of the Witnet Data Request bytecode. bytes32 public override hash; - /// @notice Array of source hashes encoded as bytes. + /// @notice Array of source hashes encoded as bytes. bytes /*immutable*/ public template; /// @notice Array of string arguments passed upon initialization. @@ -44,22 +44,22 @@ abstract contract WitnetRequestTemplate WitnetV2.RadonDataTypes immutable public resultDataType; /// @notice Aggregator reducer hash. - bytes32 immutable public aggregatorHash; + bytes32 immutable internal __aggregatorHash; /// @notice Tally reducer hash. - bytes32 public tallyHash; + bytes32 internal __tallyHash; /// @notice Radon Retrieval hash. - bytes32 public retrievalHash; + bytes32 internal __retrievalHash; /// @notice Radon SLA hash. - bytes32 public slaHash; + bytes32 internal __slaHash; /// @notice Unique id of last request attempt. uint256 public postId; modifier initialized { - if (retrievalHash == bytes32(0)) { + if (__retrievalHash == bytes32(0)) { revert("WitnetRequestTemplate: not initialized"); } _; @@ -73,45 +73,51 @@ abstract contract WitnetRequestTemplate constructor( WitnetRequestBoard _wrb, IWitnetBytecodes _registry, - WitnetV2.RadonDataTypes _resultDataType, bytes32[] memory _sources, - bytes32 _aggregatorHash + bytes32 _aggregator, + WitnetV2.RadonDataTypes _resultDataType ) UsingWitnet(_wrb) { - require( - address(_registry).supportsInterface(type(IWitnetBytecodes).interfaceId), - "WitnetRequestTemplate: uncompliant registry" - ); - require( - _sources.length > 0, - "WitnetRequestTemplate: no sources" - ); - assert(_aggregatorHash != bytes32(0)); - for (uint i = 0; i < _sources.length; i ++) { + { require( - _registry.lookupDataSourceResultDataType(_sources[i]) == _resultDataType, - "WitnetRequestTemplate: mismatching sources" + address(_registry).supportsInterface(type(IWitnetBytecodes).interfaceId), + "WitnetRequestTemplate: uncompliant registry" ); + registry = _registry; } - aggregatorHash = _aggregatorHash; - registry = _registry; - resultDataType = _resultDataType; - template = abi.encode(_sources); + { + resultDataType = _resultDataType; + } + { + require( + _sources.length > 0, + "WitnetRequestTemplate: no sources" + ); + for (uint _i = 0; _i < _sources.length; _i ++) { + require( + _registry.lookupDataSourceResultDataType(_sources[_i]) == _resultDataType, + "WitnetRequestTemplate: mismatching sources" + ); + } + template = abi.encode(_sources); + } + { + assert(_aggregator != bytes32(0)); + __aggregatorHash = _aggregator; + } } /// ======================================================================= /// --- WitnetRequestTemplate interface ----------------------------------- - receive () external payable {} - - function _decode(WitnetCBOR.CBOR memory) virtual internal view returns (bytes memory); - + receive () virtual external payable {} + function getRadonAggregator() external view returns (WitnetV2.RadonReducer memory) { - return registry.lookupRadonRetrievalAggregator(retrievalHash); + return registry.lookupRadonRetrievalAggregator(__retrievalHash); } function getRadonTally() @@ -119,7 +125,7 @@ abstract contract WitnetRequestTemplate initialized returns (WitnetV2.RadonReducer memory) { - return registry.lookupRadonRetrievalTally(retrievalHash); + return registry.lookupRadonRetrievalTally(__retrievalHash); } function getRadonSLA() @@ -127,7 +133,7 @@ abstract contract WitnetRequestTemplate initialized returns (WitnetV2.RadonSLA memory) { - return registry.lookupRadonSLA(slaHash); + return registry.lookupRadonSLA(__slaHash); } function sources() @@ -137,7 +143,11 @@ abstract contract WitnetRequestTemplate return abi.decode(template, (bytes32[])); } - function post() virtual external payable returns (uint256 _usedFunds) { + function post() + virtual + external payable + returns (uint256 _usedFunds) + { if ( postId == 0 || ( @@ -152,7 +162,11 @@ abstract contract WitnetRequestTemplate } } - function pending() virtual public view returns (bool) { + function pending() + virtual + public view + returns (bool) + { return ( postId == 0 || _witnetCheckResultAvailability(postId) @@ -167,15 +181,20 @@ abstract contract WitnetRequestTemplate { require(!pending(), "WitnetRequestTemplate: pending"); Witnet.Result memory _result = _witnetReadResult(postId); - return (_result.success, _decode(_result.value)); + return (_result.success, _read(_result.value)); } + function _read(WitnetCBOR.CBOR memory) virtual internal view returns (bytes memory); + // ================================================================================================================ // --- Implement 'Initializable' functions ------------------------------------------------------------------------ - function initialize(bytes memory _initData) external { - require(retrievalHash == 0, "WitnetRequestTemplate: already initialized"); + function initialize(bytes memory _initData) + external + virtual override + { + require(__retrievalHash == 0, "WitnetRequestTemplate: already initialized"); bytes32[] memory _sources = abi.decode(WitnetRequestTemplate(payable(self)).template(), (bytes32[])); InitData memory _init = abi.decode(_initData, (InitData)); args = _init.args; @@ -184,14 +203,14 @@ abstract contract WitnetRequestTemplate _init.resultMaxSize, _sources, _init.args, - aggregatorHash, + __aggregatorHash, _init.tallyHash ); - retrievalHash = _retrievalHash; - slaHash = _init.slaHash; - tallyHash = _init.tallyHash; + __retrievalHash = _retrievalHash; + __slaHash = _init.__slaHash; + __tallyHash = _init.tallyHash; template = abi.encode(_sources); - bytecode = registry.bytecodeOf(retrievalHash, _init.slaHash); + bytecode = registry.bytecodeOf(__retrievalHash, _init.__slaHash); hash = sha256(bytecode); } From 410c67221e4b16751ca39e63514c5188f9be2f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 29 Dec 2022 17:11:59 +0100 Subject: [PATCH 047/119] fix(libs): verifyRadonRequestResultDataType --- contracts/impls/bytecodes/WitnetBytecodes.sol | 2 +- contracts/libs/WitnetEncodingLib.sol | 15 ++++++++++----- test/TestWitnetEncodingLib.sol | 12 ++++++++++-- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol index eaf3ba32e..d77289816 100644 --- a/contracts/impls/bytecodes/WitnetBytecodes.sol +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -403,7 +403,7 @@ contract WitnetBytecodes _requestMethod, resultDataType: - WitnetEncodingLib.verifyRadonRequestScript(_requestRadonScript), + WitnetEncodingLib.verifyRadonScriptResultDataType(_requestRadonScript), url: string(abi.encodePacked( diff --git a/contracts/libs/WitnetEncodingLib.sol b/contracts/libs/WitnetEncodingLib.sol index 59b3dc06a..30fc39840 100644 --- a/contracts/libs/WitnetEncodingLib.sol +++ b/contracts/libs/WitnetEncodingLib.sol @@ -561,13 +561,13 @@ library WitnetEncodingLib { } } - function verifyRadonRequestScript(bytes memory script) + function verifyRadonScriptResultDataType(bytes memory script) public pure returns (WitnetV2.RadonDataTypes) { - // TODO: formal validation of radon script return _verifyRadonScriptResultDataType( - WitnetCBOR.fromBytes(script) + WitnetCBOR.fromBytes(script), + false ); } @@ -625,14 +625,19 @@ library WitnetEncodingLib { self.buffer.cursor = _start; } - function _verifyRadonScriptResultDataType(WitnetCBOR.CBOR memory self) + // event Log(WitnetCBOR.CBOR self, bool flip); + function _verifyRadonScriptResultDataType(WitnetCBOR.CBOR memory self, bool flip) private pure returns (WitnetV2.RadonDataTypes) { + // emit Log(self, flip); if (self.majorType == WitnetCBOR.MAJOR_TYPE_ARRAY) { WitnetCBOR.CBOR[] memory items = self.readArray(); if (items.length > 1) { - return _verifyRadonScriptResultDataType(items[items.length - 2]); + return flip + ? _verifyRadonScriptResultDataType(items[0], false) + : _verifyRadonScriptResultDataType(items[items.length - 2], true) + ; } else { return WitnetV2.RadonDataTypes.Any; } diff --git a/test/TestWitnetEncodingLib.sol b/test/TestWitnetEncodingLib.sol index ce4ddc3d4..b5d401b86 100644 --- a/test/TestWitnetEncodingLib.sol +++ b/test/TestWitnetEncodingLib.sol @@ -283,7 +283,7 @@ contract TestWitnetEncodingLib { function testVerifyRadonScriptOk1() external { Assert.equal( - uint(WitnetEncodingLib.verifyRadonRequestScript(hex"861877821866646461746182186664706f6f6c8218646b746f6b656e3150726963658218571a000f4240185b")), + uint(WitnetEncodingLib.verifyRadonScriptResultDataType(hex"861877821866646461746182186664706f6f6c8218646b746f6b656e3150726963658218571a000f4240185b")), uint(WitnetV2.RadonDataTypes.Integer), "unexpected result data type" ); @@ -291,10 +291,18 @@ contract TestWitnetEncodingLib { function testVerifyRadonScriptOk2() external { Assert.equal( - uint(WitnetEncodingLib.verifyRadonRequestScript(hex"80")), + uint(WitnetEncodingLib.verifyRadonScriptResultDataType(hex"80")), uint(WitnetV2.RadonDataTypes.Any), "unexpected result data type" ); } + function testVerifyRadonScriptOk3() external { + Assert.equal( + uint(WitnetEncodingLib.verifyRadonScriptResultDataType(hex"8218778218676445746167")), + uint(WitnetV2.RadonDataTypes.String), + "unexpected result data type" + ); + } + } \ No newline at end of file From 0f0d8dc21845438fa9aeaa319ffaffd5c6aa4e2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 29 Dec 2022 19:41:37 +0100 Subject: [PATCH 048/119] chore(request): keep polishing bytecodes template contract --- contracts/requests/WitnetRequestTemplate.sol | 40 ++++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/contracts/requests/WitnetRequestTemplate.sol b/contracts/requests/WitnetRequestTemplate.sol index ad4fc0279..d75b4ee33 100644 --- a/contracts/requests/WitnetRequestTemplate.sol +++ b/contracts/requests/WitnetRequestTemplate.sol @@ -21,27 +21,33 @@ abstract contract WitnetRequestTemplate struct InitData { string[][] args; bytes32 tallyHash; - bytes32 __slaHash; + bytes32 slaHash; uint16 resultMaxSize; } - /// @notice Reference to Witnet Data Requests Bytecode Registry - IWitnetBytecodes immutable public registry; - /// @notice Witnet Data Request bytecode after inserting string arguments. bytes public override bytecode; /// @notice SHA-256 hash of the Witnet Data Request bytecode. bytes32 public override hash; + /// @notice Reference to Witnet Data Requests Bytecode Registry + IWitnetBytecodes immutable public registry; + + /// @notice Result data type. + WitnetV2.RadonDataTypes immutable public resultDataType; + /// @notice Array of source hashes encoded as bytes. bytes /*immutable*/ public template; /// @notice Array of string arguments passed upon initialization. string[][] public args; - /// @notice Result data type. - WitnetV2.RadonDataTypes immutable public resultDataType; + /// @notice Radon Retrieval hash. + bytes32 public retrievalHash; + + /// @notice Radon SLA hash. + bytes32 public slaHash; /// @notice Aggregator reducer hash. bytes32 immutable internal __aggregatorHash; @@ -49,17 +55,11 @@ abstract contract WitnetRequestTemplate /// @notice Tally reducer hash. bytes32 internal __tallyHash; - /// @notice Radon Retrieval hash. - bytes32 internal __retrievalHash; - - /// @notice Radon SLA hash. - bytes32 internal __slaHash; - /// @notice Unique id of last request attempt. uint256 public postId; modifier initialized { - if (__retrievalHash == bytes32(0)) { + if (retrievalHash == bytes32(0)) { revert("WitnetRequestTemplate: not initialized"); } _; @@ -117,7 +117,7 @@ abstract contract WitnetRequestTemplate external view returns (WitnetV2.RadonReducer memory) { - return registry.lookupRadonRetrievalAggregator(__retrievalHash); + return registry.lookupRadonRetrievalAggregator(retrievalHash); } function getRadonTally() @@ -125,7 +125,7 @@ abstract contract WitnetRequestTemplate initialized returns (WitnetV2.RadonReducer memory) { - return registry.lookupRadonRetrievalTally(__retrievalHash); + return registry.lookupRadonRetrievalTally(retrievalHash); } function getRadonSLA() @@ -133,7 +133,7 @@ abstract contract WitnetRequestTemplate initialized returns (WitnetV2.RadonSLA memory) { - return registry.lookupRadonSLA(__slaHash); + return registry.lookupRadonSLA(slaHash); } function sources() @@ -194,7 +194,7 @@ abstract contract WitnetRequestTemplate external virtual override { - require(__retrievalHash == 0, "WitnetRequestTemplate: already initialized"); + require(retrievalHash == 0, "WitnetRequestTemplate: already initialized"); bytes32[] memory _sources = abi.decode(WitnetRequestTemplate(payable(self)).template(), (bytes32[])); InitData memory _init = abi.decode(_initData, (InitData)); args = _init.args; @@ -206,11 +206,11 @@ abstract contract WitnetRequestTemplate __aggregatorHash, _init.tallyHash ); - __retrievalHash = _retrievalHash; - __slaHash = _init.__slaHash; + retrievalHash = _retrievalHash; + slaHash = _init.slaHash; __tallyHash = _init.tallyHash; template = abi.encode(_sources); - bytecode = registry.bytecodeOf(__retrievalHash, _init.__slaHash); + bytecode = registry.bytecodeOf(retrievalHash, _init.slaHash); hash = sha256(bytecode); } From f8c89526c1e74a0633f75442ca7fe2460155acc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Tue, 3 Jan 2023 09:21:16 +0100 Subject: [PATCH 049/119] refactor(patterns): harmonize Upgradable and Clonable --- contracts/apps/WitnetRandomness.sol | 11 +++++++++ contracts/impls/WitnetProxy.sol | 15 ++++++------ .../WitnetRequestBoardTrustlessBase.sol | 3 ++- contracts/patterns/Clonable.sol | 22 ++++++++--------- contracts/patterns/Upgradable.sol | 24 +++++++------------ .../requests/WitnetRequestMalleableBase.sol | 11 +++++++++ .../requests/WitnetRequestRandomness.sol | 5 +++- contracts/requests/WitnetRequestTemplate.sol | 2 +- 8 files changed, 56 insertions(+), 37 deletions(-) diff --git a/contracts/apps/WitnetRandomness.sol b/contracts/apps/WitnetRandomness.sol index e16db4766..41d04c0f2 100644 --- a/contracts/apps/WitnetRandomness.sol +++ b/contracts/apps/WitnetRandomness.sol @@ -26,6 +26,8 @@ contract WitnetRandomness uint256 witnetQueryId; } + address immutable internal _SELF = address(this); + /// Include an address to specify the immutable WitnetRequestBoard entrypoint address. /// @param _wrb The WitnetRequestBoard immutable entrypoint address. constructor(WitnetRequestBoard _wrb) @@ -251,6 +253,15 @@ contract WitnetRandomness // ================================================================================================================ // --- 'Clonable' overriden functions ----------------------------------------------------------------------------- + /// Contract address to which clones will be re-directed. + function self() + public view + virtual override + returns (address) + { + return _SELF; + } + /// Deploys and returns the address of a minimal proxy clone that replicates contract /// behaviour while using its own EVM storage. /// @dev This function should always provide a new address, no matter how many times diff --git a/contracts/impls/WitnetProxy.sol b/contracts/impls/WitnetProxy.sol index 07103f994..81cd704f8 100644 --- a/contracts/impls/WitnetProxy.sol +++ b/contracts/impls/WitnetProxy.sol @@ -5,11 +5,12 @@ pragma experimental ABIEncoderV2; import "../patterns/Upgradable.sol"; -/// @title WitnetProxy: upgradable delegate-proxy contract that routes Witnet data requests coming from a -/// `UsingWitnet`-inheriting contract to a currently active `WitnetRequestBoard` implementation. +/// @title WitnetProxy: upgradable delegate-proxy contract. /// @author The Witnet Foundation. contract WitnetProxy { + address immutable public proxy = address(this); + struct WitnetProxySlot { address implementation; } @@ -20,15 +21,13 @@ contract WitnetProxy { /// Constructor with no params as to ease eventual support of Singleton pattern (i.e. ERC-2470). constructor () {} - /// WitnetProxies will never accept direct transfer of ETHs. - receive() external payable { + receive() virtual external payable { revert("WitnetProxy: no transfers accepted"); } /// Payable fallback accepts delegating calls to payable functions. fallback() external payable { /* solhint-disable no-complex-fallback */ address _implementation = implementation(); - assembly { /* solhint-disable avoid-low-level-calls */ // Gas optimized delegate call to 'implementation' contract. // Note: `msg.data`, `msg.sender` and `msg.value` will be passed over @@ -52,7 +51,7 @@ contract WitnetProxy { /// Returns proxy's current implementation address. function implementation() public view returns (address) { - return _proxySlot().implementation; + return __proxySlot().implementation; } /// Upgrades the `implementation` address. @@ -102,7 +101,7 @@ contract WitnetProxy { require(_wasInitialized, "WitnetProxy: unable to initialize"); // If all checks and initialization pass, update implementation address: - _proxySlot().implementation = _newImplementation; + __proxySlot().implementation = _newImplementation; emit Upgraded(_newImplementation); // Asserts new implementation complies w/ minimal implementation of Upgradable interface: @@ -115,7 +114,7 @@ contract WitnetProxy { } /// @dev Complying with EIP-1967, retrieves storage struct containing proxy's current implementation address. - function _proxySlot() private pure returns (WitnetProxySlot storage _slot) { + function __proxySlot() private pure returns (WitnetProxySlot storage _slot) { assembly { // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) _slot.slot := 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc diff --git a/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol b/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol index 9cd90585f..bd1a91abf 100644 --- a/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol +++ b/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol @@ -436,7 +436,8 @@ abstract contract WitnetRequestBoardTrustlessBase function postDr( bytes32 _drRadHash, - bytes32 _drSlaHash + bytes32 _drSlaHash, + uint256 ) external payable returns (bytes32 _drHash) diff --git a/contracts/patterns/Clonable.sol b/contracts/patterns/Clonable.sol index 37d0b0042..e1b8d0d9c 100644 --- a/contracts/patterns/Clonable.sol +++ b/contracts/patterns/Clonable.sol @@ -5,19 +5,19 @@ pragma solidity >=0.6.0 <0.9.0; import "./Initializable.sol"; abstract contract Clonable is Initializable { - /// Immutable contract address that actually attends all calls to this contract. - /// @dev Differs from `address(this)` when reached within a DELEGATECALL. - address immutable public self = address(this); event Cloned(address indexed by, Clonable indexed self, Clonable indexed clone); - /// Tells whether this contract is a clone of another (i.e. `self()`) + /// Contract address to which clones will be re-directed. + function self() virtual public view returns (address); + + /// Tells whether this contract is a clone of `self()` function cloned() public view returns (bool) { return ( - address(this) != self + address(this) != self() ); } @@ -31,21 +31,21 @@ abstract contract Clonable is Initializable { public virtual returns (Clonable _instance) { - address _self = self; + address _base = self(); assembly { // ptr to free mem: let ptr := mload(0x40) // begin minimal proxy construction bytecode: mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) // make minimal proxy delegate all calls to `self()`: - mstore(add(ptr, 0x14), shl(0x60, _self)) + mstore(add(ptr, 0x14), shl(0x60, _base)) // end minimal proxy construction bytecode: mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) // CREATE new instance: _instance := create(0, ptr, 0x37) } require(address(_instance) != address(0), "Clonable: CREATE failed"); - emit Cloned(msg.sender, Clonable(self), _instance); + emit Cloned(msg.sender, Clonable(self()), _instance); } /// Deploys and returns the address of a minimal proxy clone that replicates contract @@ -59,19 +59,19 @@ abstract contract Clonable is Initializable { public virtual returns (Clonable _instance) { - address _self = self; + address _base = self(); assembly { // ptr to free mem: let ptr := mload(0x40) mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) // make minimal proxy delegate all calls to `self()`: - mstore(add(ptr, 0x14), shl(0x60, _self)) + mstore(add(ptr, 0x14), shl(0x60, _base)) // end minimal proxy construction bytecode: mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) // CREATE2 new instance: _instance := create2(0, ptr, 0x37, _salt) } require(address(_instance) != address(0), "Clonable: CREATE2 failed"); - emit Cloned(msg.sender, Clonable(self), _instance); + emit Cloned(msg.sender, Clonable(self()), _instance); } } diff --git a/contracts/patterns/Upgradable.sol b/contracts/patterns/Upgradable.sol index f021ee0b4..8a6e2c037 100644 --- a/contracts/patterns/Upgradable.sol +++ b/contracts/patterns/Upgradable.sol @@ -32,34 +32,28 @@ abstract contract Upgradable is Initializable, Proxiable { _codehash := extcodehash(_base) } _BASE = _base; - _CODEHASH = _codehash; + _CODEHASH = _codehash; _UPGRADABLE = _isUpgradable; } - /// @dev Tells whether provided address could eventually upgrade the contract. - function isUpgradableFrom(address from) virtual external view returns (bool); - - /// TODO: the following methods should be all declared as pure - /// whenever this Solidity's PR gets merged and released: - /// https://github.com/ethereum/solidity/pull/10240 - - /// @dev Retrieves base contract. Differs from address(this) when via delegate-proxy pattern. + /// @dev Retrieves base contract. Differs from address(this) when called via delegate-proxy pattern. function base() public view returns (address) { return _BASE; } /// @dev Retrieves the immutable codehash of this contract, even if invoked as delegatecall. - /// @return _codehash This contracts immutable codehash. - function codehash() public view returns (bytes32 _codehash) { + function codehash() public view returns (bytes32) { return _CODEHASH; } - - /// @dev Determines whether current instance allows being upgraded. - /// @dev Returned value should be invariant from whoever is calling. - function isUpgradable() public view returns (bool) { + + /// @dev Determines whether the logic of this contract is potentially upgradable. + function isUpgradable() public view returns (bool) { return _UPGRADABLE; } + /// @dev Tells whether provided address could eventually upgrade the contract. + function isUpgradableFrom(address from) virtual external view returns (bool); + /// @dev Retrieves human-redable named version of current implementation. function version() virtual public view returns (string memory); } \ No newline at end of file diff --git a/contracts/requests/WitnetRequestMalleableBase.sol b/contracts/requests/WitnetRequestMalleableBase.sol index 00f706b55..3977d620a 100644 --- a/contracts/requests/WitnetRequestMalleableBase.sol +++ b/contracts/requests/WitnetRequestMalleableBase.sol @@ -16,6 +16,8 @@ abstract contract WitnetRequestMalleableBase { using Witnet for *; + address immutable internal _SELF = address(this); + event WitnessingParamsChanged( address indexed by, uint8 numWitnesses, @@ -163,6 +165,15 @@ abstract contract WitnetRequestMalleableBase // ================================================================================================================ // --- 'Clonable' overriden functions ----------------------------------------------------------------------------- + /// Contract address to which clones will be re-directed. + function self() + public view + virtual override + returns (address) + { + return _SELF; + } + /// Deploys and returns the address of a minimal proxy clone that replicates contract /// behaviour while using its own EVM storage. /// @dev This function should always provide a new address, no matter how many times diff --git a/contracts/requests/WitnetRequestRandomness.sol b/contracts/requests/WitnetRequestRandomness.sol index 6a3e96888..a89b921fc 100644 --- a/contracts/requests/WitnetRequestRandomness.sol +++ b/contracts/requests/WitnetRequestRandomness.sol @@ -4,7 +4,10 @@ pragma solidity >=0.7.0 <0.9.0; import "./WitnetRequestMalleableBase.sol"; -contract WitnetRequestRandomness is WitnetRequestMalleableBase { +contract WitnetRequestRandomness + is + WitnetRequestMalleableBase +{ bytes internal constant _WITNET_RANDOMNESS_BYTECODE_TEMPLATE = hex"0a0f120508021a01801a0210022202100b"; constructor() { diff --git a/contracts/requests/WitnetRequestTemplate.sol b/contracts/requests/WitnetRequestTemplate.sol index d75b4ee33..e7e7ff497 100644 --- a/contracts/requests/WitnetRequestTemplate.sol +++ b/contracts/requests/WitnetRequestTemplate.sol @@ -195,7 +195,7 @@ abstract contract WitnetRequestTemplate virtual override { require(retrievalHash == 0, "WitnetRequestTemplate: already initialized"); - bytes32[] memory _sources = abi.decode(WitnetRequestTemplate(payable(self)).template(), (bytes32[])); + bytes32[] memory _sources = abi.decode(WitnetRequestTemplate(payable(self())).template(), (bytes32[])); InitData memory _init = abi.decode(_initData, (InitData)); args = _init.args; bytes32 _retrievalHash = registry.verifyRadonRetrieval( From 5f876f8210163ba206dbae6d1a4c87259d5b63d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Tue, 3 Jan 2023 12:54:07 +0100 Subject: [PATCH 050/119] feat: let tally and result size be set on WitnetRequestTemplate constructor --- contracts/requests/WitnetRequestTemplate.sol | 60 +++++++++++++------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/contracts/requests/WitnetRequestTemplate.sol b/contracts/requests/WitnetRequestTemplate.sol index e7e7ff497..e04287d86 100644 --- a/contracts/requests/WitnetRequestTemplate.sol +++ b/contracts/requests/WitnetRequestTemplate.sol @@ -19,10 +19,8 @@ abstract contract WitnetRequestTemplate using ERC165Checker for address; struct InitData { - string[][] args; - bytes32 tallyHash; bytes32 slaHash; - uint16 resultMaxSize; + string[][] args; } /// @notice Witnet Data Request bytecode after inserting string arguments. @@ -34,9 +32,12 @@ abstract contract WitnetRequestTemplate /// @notice Reference to Witnet Data Requests Bytecode Registry IWitnetBytecodes immutable public registry; - /// @notice Result data type. + /// @notice Result data type. WitnetV2.RadonDataTypes immutable public resultDataType; + /// @notice Result max size or rank (if variable type). + uint16 immutable public resultDataMaxSize; + /// @notice Array of source hashes encoded as bytes. bytes /*immutable*/ public template; @@ -50,13 +51,16 @@ abstract contract WitnetRequestTemplate bytes32 public slaHash; /// @notice Aggregator reducer hash. - bytes32 immutable internal __aggregatorHash; + bytes32 immutable internal _AGGREGATOR_HASH; /// @notice Tally reducer hash. - bytes32 internal __tallyHash; + bytes32 immutable internal _TALLY_HASH; /// @notice Unique id of last request attempt. - uint256 public postId; + uint256 public postId; + + /// @notice Contract address to which clones will be re-directed. + address immutable internal _SELF = address(this); modifier initialized { if (retrievalHash == bytes32(0)) { @@ -75,7 +79,9 @@ abstract contract WitnetRequestTemplate IWitnetBytecodes _registry, bytes32[] memory _sources, bytes32 _aggregator, - WitnetV2.RadonDataTypes _resultDataType + bytes32 _tally, + WitnetV2.RadonDataTypes _resultDataType, + uint16 _resultDataMaxSize ) UsingWitnet(_wrb) { @@ -88,6 +94,7 @@ abstract contract WitnetRequestTemplate } { resultDataType = _resultDataType; + resultDataMaxSize = _resultDataMaxSize; } { require( @@ -104,14 +111,20 @@ abstract contract WitnetRequestTemplate } { assert(_aggregator != bytes32(0)); - __aggregatorHash = _aggregator; - } + _AGGREGATOR_HASH = _aggregator; + } + { + assert(_tally != bytes32(0)); + _TALLY_HASH = _tally; + } } /// ======================================================================= /// --- WitnetRequestTemplate interface ----------------------------------- receive () virtual external payable {} + + function _read(WitnetCBOR.CBOR memory) virtual internal view returns (bytes memory); function getRadonAggregator() external view @@ -184,7 +197,18 @@ abstract contract WitnetRequestTemplate return (_result.success, _read(_result.value)); } - function _read(WitnetCBOR.CBOR memory) virtual internal view returns (bytes memory); + + // ================================================================================================================ + // --- 'Clonable' overriden functions ----------------------------------------------------------------------------- + + /// Contract address to which clones will be re-directed. + function self() + public view + virtual override + returns (address) + { + return _SELF; + } // ================================================================================================================ @@ -200,18 +224,16 @@ abstract contract WitnetRequestTemplate args = _init.args; bytes32 _retrievalHash = registry.verifyRadonRetrieval( resultDataType, - _init.resultMaxSize, + resultDataMaxSize, _sources, _init.args, - __aggregatorHash, - _init.tallyHash + _AGGREGATOR_HASH, + _TALLY_HASH ); - retrievalHash = _retrievalHash; + bytecode = registry.bytecodeOf(_retrievalHash, _init.slaHash); + hash = sha256(bytecode); + retrievalHash = _retrievalHash; slaHash = _init.slaHash; - __tallyHash = _init.tallyHash; template = abi.encode(_sources); - bytecode = registry.bytecodeOf(retrievalHash, _init.slaHash); - hash = sha256(bytecode); } - } \ No newline at end of file From 5dc956ad433bae2c337fa5b4f3e9673ddab3ecb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Tue, 3 Jan 2023 18:07:48 +0100 Subject: [PATCH 051/119] chore: adopt OpenZeppelin's Initializable pattern --- contracts/apps/WitnetRandomness.sol | 40 ++++++------------- contracts/impls/WitnetUpgradableBase.sol | 2 +- .../WitnetRequestBoardTrustableBase.sol | 19 ++++++--- .../WitnetRequestBoardTrustlessBase.sol | 20 ++++++---- contracts/impls/bytecodes/WitnetBytecodes.sol | 21 +++++++--- contracts/patterns/Clonable.sol | 34 ++++++++++++---- contracts/patterns/Initializable.sol | 9 +---- contracts/patterns/Upgradable.sol | 12 ++++++ .../WitnetRequestInitializableBase.sol | 25 ------------ .../requests/WitnetRequestMalleableBase.sol | 34 ++++++---------- .../requests/WitnetRequestRandomness.sol | 8 ++-- contracts/requests/WitnetRequestTemplate.sol | 32 ++++++--------- package.json | 3 +- 13 files changed, 127 insertions(+), 132 deletions(-) delete mode 100644 contracts/requests/WitnetRequestInitializableBase.sol diff --git a/contracts/apps/WitnetRandomness.sol b/contracts/apps/WitnetRandomness.sol index 41d04c0f2..d206cde09 100644 --- a/contracts/apps/WitnetRandomness.sol +++ b/contracts/apps/WitnetRandomness.sol @@ -26,8 +26,6 @@ contract WitnetRandomness uint256 witnetQueryId; } - address immutable internal _SELF = address(this); - /// Include an address to specify the immutable WitnetRequestBoard entrypoint address. /// @param _wrb The WitnetRequestBoard immutable entrypoint address. constructor(WitnetRequestBoard _wrb) @@ -253,15 +251,21 @@ contract WitnetRandomness // ================================================================================================================ // --- 'Clonable' overriden functions ----------------------------------------------------------------------------- - /// Contract address to which clones will be re-directed. - function self() - public view - virtual override - returns (address) + /// @notice Re-initialize contract's storage context upon a new upgrade from a proxy. + /// @dev Must fail when trying to upgrade to same logic contract more than once. + function initialize(bytes memory _initData) + public + override + initializer { - return _SELF; + witnetRandomnessRequest = WitnetRequestRandomness( + abi.decode( + _initData, + (address) + ) + ); } - + /// Deploys and returns the address of a minimal proxy clone that replicates contract /// behaviour while using its own EVM storage. /// @dev This function should always provide a new address, no matter how many times @@ -290,24 +294,6 @@ contract WitnetRandomness } - // ================================================================================================================ - // --- 'Initializable' overriden functions ------------------------------------------------------------------------ - - /// @dev Initializes contract's storage context. - function initialize(bytes memory _initData) - public - virtual override - { - require(address(witnetRandomnessRequest) == address(0), "WitnetRandomness: already initialized"); - witnetRandomnessRequest = WitnetRequestRandomness( - abi.decode( - _initData, - (address) - ) - ); - } - - // ================================================================================================================ // --- INTERNAL FUNCTIONS ----------------------------------------------------------------------------------------- diff --git a/contracts/impls/WitnetUpgradableBase.sol b/contracts/impls/WitnetUpgradableBase.sol index f5559d3ac..a5bc7d8c7 100644 --- a/contracts/impls/WitnetUpgradableBase.sol +++ b/contracts/impls/WitnetUpgradableBase.sol @@ -21,7 +21,7 @@ abstract contract WitnetUpgradableBase { bytes32 internal immutable _WITNET_UPGRADABLE_VERSION; - error AlreadyInitialized(address implementation); + error AlreadyUpgraded(address implementation); error NotCompliant(bytes4 interfaceId); error NotUpgradable(address self); error OnlyOwner(address owner); diff --git a/contracts/impls/boards/trustable/WitnetRequestBoardTrustableBase.sol b/contracts/impls/boards/trustable/WitnetRequestBoardTrustableBase.sol index 531c95684..85d4fa5a5 100644 --- a/contracts/impls/boards/trustable/WitnetRequestBoardTrustableBase.sol +++ b/contracts/impls/boards/trustable/WitnetRequestBoardTrustableBase.sol @@ -58,9 +58,12 @@ abstract contract WitnetRequestBoardTrustableBase // ================================================================================================================ // --- Overrides 'Upgradable' ------------------------------------------------------------------------------------- - /// Initialize storage-context when invoked as delegatecall. - /// @dev Must fail when trying to initialize same instance more than once. - function initialize(bytes memory _initData) virtual external override { + /// @notice Re-initialize contract's storage context upon a new upgrade from a proxy. + /// @dev Must fail when trying to upgrade to same logic contract more than once. + function initialize(bytes memory _initData) + public + override + { address _owner = _state().owner; if (_owner == address(0)) { // set owner if none set yet @@ -68,12 +71,18 @@ abstract contract WitnetRequestBoardTrustableBase _state().owner = _owner; } else { // only owner can initialize: - require(msg.sender == _owner, "WitnetRequestBoardTrustableBase: only owner"); + require( + msg.sender == _owner, + "WitnetRequestBoardTrustableBase: only owner" + ); } if (_state().base != address(0)) { // current implementation cannot be initialized more than once: - require(_state().base != base(), "WitnetRequestBoardTrustableBase: already initialized"); + require( + _state().base != base(), + "WitnetRequestBoardTrustableBase: already upgraded" + ); } _state().base = base(); diff --git a/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol b/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol index bd1a91abf..dea9df631 100644 --- a/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol +++ b/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol @@ -180,11 +180,11 @@ abstract contract WitnetRequestBoardTrustlessBase // ================================================================================================================ // --- Overrides 'Upgradable' ------------------------------------------------------------------------------------- - /// Initialize storage-context when invoked as delegatecall. - /// @dev Must fail when trying to initialize same instance more than once. + /// @notice Re-initialize contract's storage context upon a new upgrade from a proxy. + /// @dev Must fail when trying to upgrade to same logic contract more than once. function initialize(bytes memory _initData) public - virtual override + override { address _owner = __board().owner; if (_owner == address(0)) { @@ -193,7 +193,9 @@ abstract contract WitnetRequestBoardTrustlessBase __board().owner = _owner; } else { // only owner can initialize: - if (msg.sender != _owner) revert WitnetUpgradableBase.OnlyOwner(_owner); + if (msg.sender != _owner) { + revert WitnetUpgradableBase.OnlyOwner(_owner); + } } if (__board().serviceTag == bytes4(0)) { @@ -202,7 +204,9 @@ abstract contract WitnetRequestBoardTrustlessBase if (__board().base != address(0)) { // current implementation cannot be initialized more than once: - if(__board().base == base()) revert WitnetUpgradableBase.AlreadyInitialized(base()); + if(__board().base == base()) { + revert WitnetUpgradableBase.AlreadyUpgraded(base()); + } } __board().base = base(); @@ -290,8 +294,8 @@ abstract contract WitnetRequestBoardTrustlessBase function estimateBaseFee( bytes32 _drRadHash, uint256 _gasPrice, - bytes32 _drSlaHash, - uint256 _witPrice + bytes32, + uint256 ) public view override @@ -303,7 +307,7 @@ abstract contract WitnetRequestBoardTrustlessBase ); } - function estimateReportFee(bytes32 _drRadHash, uint256 _gasPrice) + function estimateReportFee(bytes32, uint256) public view virtual override returns (uint256) diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol index d77289816..327b029d7 100644 --- a/contracts/impls/bytecodes/WitnetBytecodes.sol +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -110,11 +110,11 @@ contract WitnetBytecodes // ================================================================================================================ // --- Overrides 'Upgradable' ------------------------------------------------------------------------------------- - /// Initialize storage-context when invoked as delegatecall. - /// @dev Must fail when trying to initialize same instance more than once. + /// @notice Re-initialize contract's storage context upon a new upgrade from a proxy. + /// @dev Must fail when trying to upgrade to same logic contract more than once. function initialize(bytes memory) public - virtual override + override { address _owner = __bytecodes().owner; if (_owner == address(0)) { @@ -123,16 +123,25 @@ contract WitnetBytecodes __bytecodes().owner = _owner; } else { // only owner can initialize: - if (msg.sender != _owner) revert WitnetUpgradableBase.OnlyOwner(_owner); + if (msg.sender != _owner) { + revert WitnetUpgradableBase.OnlyOwner(_owner); + } } if (__bytecodes().base != address(0)) { // current implementation cannot be initialized more than once: - if(__bytecodes().base == base()) revert WitnetUpgradableBase.AlreadyInitialized(base()); + if(__bytecodes().base == base()) { + revert WitnetUpgradableBase.AlreadyUpgraded(base()); + } } __bytecodes().base = base(); - emit Upgraded(msg.sender, base(), codehash(), version()); + emit Upgraded( + msg.sender, + base(), + codehash(), + version() + ); } /// Tells whether provided address could eventually upgrade the contract. diff --git a/contracts/patterns/Clonable.sol b/contracts/patterns/Clonable.sol index e1b8d0d9c..9805bacb3 100644 --- a/contracts/patterns/Clonable.sol +++ b/contracts/patterns/Clonable.sol @@ -4,13 +4,14 @@ pragma solidity >=0.6.0 <0.9.0; import "./Initializable.sol"; -abstract contract Clonable is Initializable { +abstract contract Clonable + is + Initializable +{ + address immutable internal _SELF = address(this); event Cloned(address indexed by, Clonable indexed self, Clonable indexed clone); - /// Contract address to which clones will be re-directed. - function self() virtual public view returns (address); - /// Tells whether this contract is a clone of `self()` function cloned() public view @@ -44,8 +45,15 @@ abstract contract Clonable is Initializable { // CREATE new instance: _instance := create(0, ptr, 0x37) } - require(address(_instance) != address(0), "Clonable: CREATE failed"); - emit Cloned(msg.sender, Clonable(self()), _instance); + require( + address(_instance) != address(0), + "Clonable: CREATE failed" + ); + emit Cloned( + msg.sender, + Clonable(self()), + _instance + ); } /// Deploys and returns the address of a minimal proxy clone that replicates contract @@ -71,7 +79,19 @@ abstract contract Clonable is Initializable { // CREATE2 new instance: _instance := create2(0, ptr, 0x37, _salt) } - require(address(_instance) != address(0), "Clonable: CREATE2 failed"); + require( + address(_instance) != address(0), + "Clonable: CREATE2 failed" + ); emit Cloned(msg.sender, Clonable(self()), _instance); } + + /// @notice Re-initialize contract's storage context upon a new upgrade from a proxy. + /// @dev Must fail when trying to upgrade to same logic contract more than once. + function initialize(bytes memory) virtual external; + + /// @notice Contract address to which clones will be re-directed. + function self() virtual public view returns (address) { + return _SELF; + } } diff --git a/contracts/patterns/Initializable.sol b/contracts/patterns/Initializable.sol index 2cb115bda..1a9dc6131 100644 --- a/contracts/patterns/Initializable.sol +++ b/contracts/patterns/Initializable.sol @@ -1,8 +1,3 @@ // SPDX-License-Identifier: MIT - -pragma solidity >=0.6.0 <0.9.0; - -interface Initializable { - /// @dev Initialize contract's storage context. - function initialize(bytes calldata) external; -} +pragma solidity >=0.8.0 <0.9.0; +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; \ No newline at end of file diff --git a/contracts/patterns/Upgradable.sol b/contracts/patterns/Upgradable.sol index 8a6e2c037..a7bb5640f 100644 --- a/contracts/patterns/Upgradable.sol +++ b/contracts/patterns/Upgradable.sol @@ -13,6 +13,14 @@ abstract contract Upgradable is Initializable, Proxiable { bytes32 internal immutable _CODEHASH; bool internal immutable _UPGRADABLE; + modifier onlyDelegateCalls { + require( + address(this) != _BASE, + "Upgradable: not a delegate call" + ); + _; + } + /// Emitted every time the contract gets upgraded. /// @param from The address who ordered the upgrading. Namely, the WRB operator in "trustable" implementations. /// @param baseAddr The address of the new implementation contract. @@ -54,6 +62,10 @@ abstract contract Upgradable is Initializable, Proxiable { /// @dev Tells whether provided address could eventually upgrade the contract. function isUpgradableFrom(address from) virtual external view returns (bool); + /// @notice Re-initialize contract's storage context upon a new upgrade from a proxy. + /// @dev Must fail when trying to upgrade to same logic contract more than once. + function initialize(bytes memory) virtual external; + /// @dev Retrieves human-redable named version of current implementation. function version() virtual public view returns (string memory); } \ No newline at end of file diff --git a/contracts/requests/WitnetRequestInitializableBase.sol b/contracts/requests/WitnetRequestInitializableBase.sol deleted file mode 100644 index fed2d087d..000000000 --- a/contracts/requests/WitnetRequestInitializableBase.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.7.0 <0.9.0; - -import "./WitnetRequestBase.sol"; -import "../patterns/Initializable.sol"; - -abstract contract WitnetRequestInitializableBase - is - Initializable, - WitnetRequestBase -{ - using Witnet for bytes; - function initialize(bytes memory _bytecode) - public - virtual override - { - require( - bytecode.length == 0, - "WitnetRequestInitializableBase: cannot change bytecode" - ); - bytecode = _bytecode; - hash = _bytecode.hash(); - } -} diff --git a/contracts/requests/WitnetRequestMalleableBase.sol b/contracts/requests/WitnetRequestMalleableBase.sol index 3977d620a..d4cb585db 100644 --- a/contracts/requests/WitnetRequestMalleableBase.sol +++ b/contracts/requests/WitnetRequestMalleableBase.sol @@ -16,8 +16,6 @@ abstract contract WitnetRequestMalleableBase { using Witnet for *; - address immutable internal _SELF = address(this); - event WitnessingParamsChanged( address indexed by, uint8 numWitnesses, @@ -165,13 +163,14 @@ abstract contract WitnetRequestMalleableBase // ================================================================================================================ // --- 'Clonable' overriden functions ----------------------------------------------------------------------------- - /// Contract address to which clones will be re-directed. - function self() - public view - virtual override - returns (address) + /// @notice Re-initialize contract's storage context upon a new upgrade from a proxy. + /// @dev Must fail when trying to upgrade to same logic contract more than once. + function initialize(bytes memory _template) + public + override + initializer { - return _SELF; + _initialize(_template); } /// Deploys and returns the address of a minimal proxy clone that replicates contract @@ -204,19 +203,6 @@ abstract contract WitnetRequestMalleableBase } - // ================================================================================================================ - // --- 'Initializable' overriden functions ------------------------------------------------------------------------ - - /// @dev Initializes contract's storage context. - function initialize(bytes memory _template) - public - virtual override - { - require(_request().template.length == 0, "WitnetRequestMalleableBase: already initialized"); - _initialize(_template); - _transferOwnership(_msgSender()); - } - // ================================================================================================================ // --- 'Ownable' overriden functions ------------------------------------------------------------------------------ @@ -239,6 +225,7 @@ abstract contract WitnetRequestMalleableBase emit OwnershipTransferred(oldOwner, newOwner); } + // ================================================================================================================ // --- 'Proxiable 'overriden functions ---------------------------------------------------------------------------- @@ -259,10 +246,13 @@ abstract contract WitnetRequestMalleableBase // ================================================================================================================ // --- INTERNAL FUNCTIONS ----------------------------------------------------------------------------------------- - /// @dev Initializes witnessing params and template bytecode. + /// @dev Initializes witnessing params and template bytecode. function _initialize(bytes memory _template) internal + virtual { + _transferOwnership(_msgSender()); + assert(_template.length > 0); _request().template = _template; diff --git a/contracts/requests/WitnetRequestRandomness.sol b/contracts/requests/WitnetRequestRandomness.sol index a89b921fc..65417115a 100644 --- a/contracts/requests/WitnetRequestRandomness.sol +++ b/contracts/requests/WitnetRequestRandomness.sol @@ -11,13 +11,13 @@ contract WitnetRequestRandomness bytes internal constant _WITNET_RANDOMNESS_BYTECODE_TEMPLATE = hex"0a0f120508021a01801a0210022202100b"; constructor() { - initialize(bytes("")); + _initialize(hex""); } - function initialize(bytes memory) - public + function _initialize(bytes memory) + internal virtual override { - super.initialize(_WITNET_RANDOMNESS_BYTECODE_TEMPLATE); + super._initialize(_WITNET_RANDOMNESS_BYTECODE_TEMPLATE); } } diff --git a/contracts/requests/WitnetRequestTemplate.sol b/contracts/requests/WitnetRequestTemplate.sol index e04287d86..896068650 100644 --- a/contracts/requests/WitnetRequestTemplate.sol +++ b/contracts/requests/WitnetRequestTemplate.sol @@ -59,9 +59,6 @@ abstract contract WitnetRequestTemplate /// @notice Unique id of last request attempt. uint256 public postId; - /// @notice Contract address to which clones will be re-directed. - address immutable internal _SELF = address(this); - modifier initialized { if (retrievalHash == bytes32(0)) { revert("WitnetRequestTemplate: not initialized"); @@ -199,26 +196,23 @@ abstract contract WitnetRequestTemplate // ================================================================================================================ - // --- 'Clonable' overriden functions ----------------------------------------------------------------------------- + // --- Overriden 'Clonable' functions ----------------------------------------------------------------------------- - /// Contract address to which clones will be re-directed. - function self() - public view - virtual override - returns (address) + /// @notice Re-initialize contract's storage context upon a new upgrade from a proxy. + /// @dev Must fail when trying to upgrade to same logic contract more than once. + function initialize(bytes memory _initData) + public + override + initializer { - return _SELF; - } - + _initialize(_initData); + } - // ================================================================================================================ - // --- Implement 'Initializable' functions ------------------------------------------------------------------------ - - function initialize(bytes memory _initData) - external - virtual override + /// @dev Internal virtual method containing actual initialization logic for every new clone. + function _initialize(bytes memory _initData) + internal + virtual { - require(retrievalHash == 0, "WitnetRequestTemplate: already initialized"); bytes32[] memory _sources = abi.decode(WitnetRequestTemplate(payable(self())).template(), (bytes32[])); InitData memory _init = abi.decode(_initData, (InitData)); args = _init.args; diff --git a/package.json b/package.json index 56c53ce69..fa8823690 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,8 @@ "license": "MIT", "dependencies": { "@eth-optimism/solc": "0.7.6-alpha.1", - "@openzeppelin/contracts": "~4.8.0-rc.2", + "@openzeppelin/contracts": "4.8.0", + "@openzeppelin/contracts-upgradeable": "4.8.0", "ado-contracts": "1.0.0", "lodash": "^4.17.21" }, From d7855f523298236ab0630114822c3bb9b1a12f37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Tue, 3 Jan 2023 19:05:25 +0100 Subject: [PATCH 052/119] refactor(patterns): rename Upgradable to Upgradeable --- contracts/impls/WitnetProxy.sol | 10 +++++----- contracts/impls/WitnetUpgradableBase.sol | 16 ++++++++-------- .../WitnetRequestBoardTrustableBase.sol | 2 +- .../WitnetRequestBoardTrustlessBase.sol | 2 +- contracts/impls/bytecodes/WitnetBytecodes.sol | 2 +- contracts/patterns/Proxiable.sol | 2 +- .../patterns/{Upgradable.sol => Upgradeable.sol} | 4 ++-- .../requests/WitnetRequestMalleableBase.sol | 2 +- ...WitnetRequestBoardTrojanHorseBadProxiable.sol | 2 +- ...itnetRequestBoardTrojanHorseNotUpgradable.sol | 4 ++-- test/witnet_bytecodes.test.js | 2 +- test/wrb.test.js | 2 +- test/wrb_proxy.test.js | 2 +- 13 files changed, 26 insertions(+), 26 deletions(-) rename contracts/patterns/{Upgradable.sol => Upgradeable.sol} (95%) diff --git a/contracts/impls/WitnetProxy.sol b/contracts/impls/WitnetProxy.sol index 81cd704f8..2ac3b4752 100644 --- a/contracts/impls/WitnetProxy.sol +++ b/contracts/impls/WitnetProxy.sol @@ -3,7 +3,7 @@ pragma solidity >=0.7.0 <0.9.0; pragma experimental ABIEncoderV2; -import "../patterns/Upgradable.sol"; +import "../patterns/Upgradeable.sol"; /// @title WitnetProxy: upgradable delegate-proxy contract. /// @author The Witnet Foundation. @@ -70,7 +70,7 @@ contract WitnetProxy { require(_newImplementation != _oldImplementation, "WitnetProxy: nothing to upgrade"); // Assert whether current implementation is intrinsically upgradable: - try Upgradable(_oldImplementation).isUpgradable() returns (bool _isUpgradable) { + try Upgradeable(_oldImplementation).isUpgradable() returns (bool _isUpgradable) { require(_isUpgradable, "WitnetProxy: not upgradable"); } catch { revert("WitnetProxy: unable to check upgradability"); @@ -86,7 +86,7 @@ contract WitnetProxy { require(_wasCalled, "WitnetProxy: not compliant"); require(abi.decode(_result, (bool)), "WitnetProxy: not authorized"); require( - Upgradable(_oldImplementation).proxiableUUID() == Upgradable(_newImplementation).proxiableUUID(), + Upgradeable(_oldImplementation).proxiableUUID() == Upgradeable(_newImplementation).proxiableUUID(), "WitnetProxy: proxiableUUIDs mismatch" ); } @@ -104,8 +104,8 @@ contract WitnetProxy { __proxySlot().implementation = _newImplementation; emit Upgraded(_newImplementation); - // Asserts new implementation complies w/ minimal implementation of Upgradable interface: - try Upgradable(_newImplementation).isUpgradable() returns (bool _isUpgradable) { + // Asserts new implementation complies w/ minimal implementation of Upgradeable interface: + try Upgradeable(_newImplementation).isUpgradable() returns (bool _isUpgradable) { return _isUpgradable; } catch { diff --git a/contracts/impls/WitnetUpgradableBase.sol b/contracts/impls/WitnetUpgradableBase.sol index a5bc7d8c7..87833d8c7 100644 --- a/contracts/impls/WitnetUpgradableBase.sol +++ b/contracts/impls/WitnetUpgradableBase.sol @@ -6,17 +6,17 @@ pragma solidity >=0.8.0 <0.9.0; import "../patterns/ERC165.sol"; import "../patterns/Ownable2Step.sol"; import "../patterns/ReentrancyGuard.sol"; -import "../patterns/Upgradable.sol"; +import "../patterns/Upgradeable.sol"; import "./WitnetProxy.sol"; -/// @title Witnet Request Board base contract, with an Upgradable (and Destructible) touch. +/// @title Witnet Request Board base contract, with an Upgradeable (and Destructible) touch. /// @author The Witnet Foundation. abstract contract WitnetUpgradableBase is ERC165, Ownable2Step, - Upgradable, + Upgradeable, ReentrancyGuard { bytes32 internal immutable _WITNET_UPGRADABLE_VERSION; @@ -31,7 +31,7 @@ abstract contract WitnetUpgradableBase bytes32 _versionTag, string memory _proxiableUUID ) - Upgradable(_upgradable) + Upgradeable(_upgradable) { _WITNET_UPGRADABLE_VERSION = _versionTag; proxiableUUID = keccak256(bytes(_proxiableUUID)); @@ -55,21 +55,21 @@ abstract contract WitnetUpgradableBase returns (bool) { return _interfaceId == type(Ownable2Step).interfaceId - || _interfaceId == type(Upgradable).interfaceId + || _interfaceId == type(Upgradeable).interfaceId || super.supportsInterface(_interfaceId); } // ================================================================================================================ // --- Overrides 'Proxiable' -------------------------------------------------------------------------------------- - /// @dev Gets immutable "heritage blood line" (ie. genotype) as a Proxiable, and eventually Upgradable, contract. - /// If implemented as an Upgradable touch, upgrading this contract to another one with a different + /// @dev Gets immutable "heritage blood line" (ie. genotype) as a Proxiable, and eventually Upgradeable, contract. + /// If implemented as an Upgradeable touch, upgrading this contract to another one with a different /// `proxiableUUID()` value should fail. bytes32 public immutable override proxiableUUID; // ================================================================================================================ - // --- Overrides 'Upgradable' -------------------------------------------------------------------------------------- + // --- Overrides 'Upgradeable' -------------------------------------------------------------------------------------- /// Retrieves human-readable version tag of current implementation. function version() public view override returns (string memory) { diff --git a/contracts/impls/boards/trustable/WitnetRequestBoardTrustableBase.sol b/contracts/impls/boards/trustable/WitnetRequestBoardTrustableBase.sol index 85d4fa5a5..d8c0a5611 100644 --- a/contracts/impls/boards/trustable/WitnetRequestBoardTrustableBase.sol +++ b/contracts/impls/boards/trustable/WitnetRequestBoardTrustableBase.sol @@ -56,7 +56,7 @@ abstract contract WitnetRequestBoardTrustableBase // ================================================================================================================ - // --- Overrides 'Upgradable' ------------------------------------------------------------------------------------- + // --- Overrides 'Upgradeable' ------------------------------------------------------------------------------------- /// @notice Re-initialize contract's storage context upon a new upgrade from a proxy. /// @dev Must fail when trying to upgrade to same logic contract more than once. diff --git a/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol b/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol index dea9df631..4f7a4aa2b 100644 --- a/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol +++ b/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol @@ -178,7 +178,7 @@ abstract contract WitnetRequestBoardTrustlessBase // ================================================================================================================ - // --- Overrides 'Upgradable' ------------------------------------------------------------------------------------- + // --- Overrides 'Upgradeable' ------------------------------------------------------------------------------------- /// @notice Re-initialize contract's storage context upon a new upgrade from a proxy. /// @dev Must fail when trying to upgrade to same logic contract more than once. diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol index 327b029d7..30e5fa18e 100644 --- a/contracts/impls/bytecodes/WitnetBytecodes.sol +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -108,7 +108,7 @@ contract WitnetBytecodes // ================================================================================================================ - // --- Overrides 'Upgradable' ------------------------------------------------------------------------------------- + // --- Overrides 'Upgradeable' ------------------------------------------------------------------------------------- /// @notice Re-initialize contract's storage context upon a new upgrade from a proxy. /// @dev Must fail when trying to upgrade to same logic contract more than once. diff --git a/contracts/patterns/Proxiable.sol b/contracts/patterns/Proxiable.sol index 680607658..3b5470746 100644 --- a/contracts/patterns/Proxiable.sol +++ b/contracts/patterns/Proxiable.sol @@ -3,7 +3,7 @@ pragma solidity >=0.6.0 <0.9.0; interface Proxiable { - /// @dev Complying with EIP-1822: Universal Upgradable Proxy Standard (UUPS) + /// @dev Complying with EIP-1822: Universal Upgradeable Proxy Standard (UUPS) /// @dev See https://eips.ethereum.org/EIPS/eip-1822. function proxiableUUID() external view returns (bytes32); } diff --git a/contracts/patterns/Upgradable.sol b/contracts/patterns/Upgradeable.sol similarity index 95% rename from contracts/patterns/Upgradable.sol rename to contracts/patterns/Upgradeable.sol index a7bb5640f..7384b9559 100644 --- a/contracts/patterns/Upgradable.sol +++ b/contracts/patterns/Upgradeable.sol @@ -7,7 +7,7 @@ pragma solidity >=0.6.0 <0.9.0; import "./Initializable.sol"; import "./Proxiable.sol"; -abstract contract Upgradable is Initializable, Proxiable { +abstract contract Upgradeable is Initializable, Proxiable { address internal immutable _BASE; bytes32 internal immutable _CODEHASH; @@ -16,7 +16,7 @@ abstract contract Upgradable is Initializable, Proxiable { modifier onlyDelegateCalls { require( address(this) != _BASE, - "Upgradable: not a delegate call" + "Upgradeable: not a delegate call" ); _; } diff --git a/contracts/requests/WitnetRequestMalleableBase.sol b/contracts/requests/WitnetRequestMalleableBase.sol index d4cb585db..133ede894 100644 --- a/contracts/requests/WitnetRequestMalleableBase.sol +++ b/contracts/requests/WitnetRequestMalleableBase.sol @@ -229,7 +229,7 @@ abstract contract WitnetRequestMalleableBase // ================================================================================================================ // --- 'Proxiable 'overriden functions ---------------------------------------------------------------------------- - /// @dev Complying with EIP-1822: Universal Upgradable Proxy Standard (UUPS) + /// @dev Complying with EIP-1822: Universal Upgradeable Proxy Standard (UUPS) /// @dev See https://eips.ethereum.org/EIPS/eip-1822. function proxiableUUID() external pure diff --git a/test/helpers/WitnetRequestBoardTrojanHorseBadProxiable.sol b/test/helpers/WitnetRequestBoardTrojanHorseBadProxiable.sol index 01ae7f628..93f6ac71b 100644 --- a/test/helpers/WitnetRequestBoardTrojanHorseBadProxiable.sol +++ b/test/helpers/WitnetRequestBoardTrojanHorseBadProxiable.sol @@ -11,7 +11,7 @@ import "../../contracts/patterns/Proxiable.sol"; * @title Witnet Requests Board Trojan Horse: Proxiable with a bad `proxiableUUID()`. * @notice Contract to test proxy upgrade assertions. * @dev Upgrading an existing WitnetRequestBoard implementation with an instance of - * this kind (i.e. Proxiable and Upgradable) but with a `proxiableUUID()` value different + * this kind (i.e. Proxiable and Upgradeable) but with a `proxiableUUID()` value different * to the one required for WitnetRequestBoards, should not be permitted by the WitnetProxy. * The contract has been created for testing purposes. * @author Witnet Foundation diff --git a/test/helpers/WitnetRequestBoardTrojanHorseNotUpgradable.sol b/test/helpers/WitnetRequestBoardTrojanHorseNotUpgradable.sol index a903a7df0..92f391995 100644 --- a/test/helpers/WitnetRequestBoardTrojanHorseNotUpgradable.sol +++ b/test/helpers/WitnetRequestBoardTrojanHorseNotUpgradable.sol @@ -8,10 +8,10 @@ import "../../contracts/patterns/Initializable.sol"; import "../../contracts/patterns/Proxiable.sol"; /** - * @title Witnet Requests Board Trojan Horse: Proxiable but not-Upgradable + * @title Witnet Requests Board Trojan Horse: Proxiable but not-Upgradeable * @notice Contract to test proxy upgrade assertions. * @dev Upgrading an existing WitnetRequestBoard implementation with an instance of - * this kind (i.e. Proxiable but not-Upgradable), should not be permitted by the WitnetProxy. + * this kind (i.e. Proxiable but not-Upgradeable), should not be permitted by the WitnetProxy. * The contract has been created for testing purposes. * @author Witnet Foundation */ diff --git a/test/witnet_bytecodes.test.js b/test/witnet_bytecodes.test.js index 80ee90887..799a4785b 100644 --- a/test/witnet_bytecodes.test.js +++ b/test/witnet_bytecodes.test.js @@ -62,7 +62,7 @@ contract('WitnetBytecodes', (accounts) => { }) }) - context("Upgradable", async () => { + context("Upgradeable", async () => { it('should manifest to be upgradable from actual owner', async () => { assert.equal( await bytecodes.isUpgradableFrom(firstOwnerAddress), diff --git a/test/wrb.test.js b/test/wrb.test.js index b6c41b0f3..da19aabf9 100644 --- a/test/wrb.test.js +++ b/test/wrb.test.js @@ -712,7 +712,7 @@ contract("WitnetRequestBoard", ([ }) describe("interfaces", async () => { - describe("Upgradable:", async () => { + describe("Upgradeable:", async () => { it("initialization fails if called from non owner address", async () => { await expectRevert( this.WitnetRequestBoard.initialize( diff --git a/test/wrb_proxy.test.js b/test/wrb_proxy.test.js index c13f0abaa..e44470927 100644 --- a/test/wrb_proxy.test.js +++ b/test/wrb_proxy.test.js @@ -91,7 +91,7 @@ contract("Witnet Requests Board Proxy", accounts => { ) }) - it("fails if owner tries to upgrade to not Upgradable-compliant implementation", async () => { + it("fails if owner tries to upgrade to not Upgradeable-compliant implementation", async () => { const troyHorse = await TrojanHorseNotUpgradable.new() await truffleAssert.reverts( proxy.upgradeWitnetRequestBoard(troyHorse.address, { from: contractOwner }), From 7fa0dc50669a137b74c60d7c7a95e49a5778df24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Tue, 10 Jan 2023 15:05:04 +0100 Subject: [PATCH 053/119] chore: fmt! --- .eslintrc | 2 +- .solhint.json | 1 + .../WitnetRequestBoardTrustlessBase.sol | 3 +- migrations/scripts/1_deploy_witnet.js | 3 +- migrations/scripts/3_deploy_router.js | 1 - migrations/scripts/4_deploy_bytecodes.js | 26 +- test/witnet_bytecodes.test.js | 1496 ++++++++--------- 7 files changed, 763 insertions(+), 769 deletions(-) diff --git a/.eslintrc b/.eslintrc index 82f3c849f..de0dfa0cb 100644 --- a/.eslintrc +++ b/.eslintrc @@ -32,6 +32,6 @@ "import/export": 0, "no-useless-constructor": 1, "handle-callback-err": 1, - "max-len" : ["error", { "code": 120 }] + "max-len" : ["error", { "code": 130 }] } } diff --git a/.solhint.json b/.solhint.json index 5f47d907a..7e8130457 100644 --- a/.solhint.json +++ b/.solhint.json @@ -16,6 +16,7 @@ "modifier-name-mixedcase": "warn", "no-empty-blocks": "off", "no-inline-assembly": "off", + "var-name-mixedcase": "off", "reason-string": [ "warn", { diff --git a/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol b/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol index 4f7a4aa2b..2a688c8c6 100644 --- a/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol +++ b/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol @@ -448,7 +448,8 @@ abstract contract WitnetRequestBoardTrustlessBase { // TODO // Calculate current epoch in Witnet terms: - uint256 _currentEpoch = block.timestamp; // TODO: .toEpoch(); + // solhint-disable-next-line + uint256 _currentEpoch = block.timestamp; // TODO: .toEpoch(); // Calculate data request delivery tag: bytes8 _drDeliveryTag = bytes8(keccak256(abi.encode( diff --git a/migrations/scripts/1_deploy_witnet.js b/migrations/scripts/1_deploy_witnet.js index dd80e55d7..ea77c21f2 100644 --- a/migrations/scripts/1_deploy_witnet.js +++ b/migrations/scripts/1_deploy_witnet.js @@ -3,12 +3,11 @@ const settings = require("../witnet.settings") const utils = require("../../scripts/utils") module.exports = async function (deployer, network, accounts) { - if (network === "test") { const WitnetRequestBoardTrustlessReporting1 = artifacts.require("WitnetRequestBoardTrustlessReporting1") await deployer.deploy(WitnetRequestBoardTrustlessReporting1, true, utils.fromAscii("testing")) const WitnetLib = artifacts.require("WitnetLib") - await deployer.deploy(WitnetLib); + await deployer.deploy(WitnetLib) const WitnetEncodingLib = artifacts.require("WitnetEncodingLib") await deployer.deploy(WitnetEncodingLib) const WitnetBytecodes = artifacts.require("WitnetBytecodes") diff --git a/migrations/scripts/3_deploy_router.js b/migrations/scripts/3_deploy_router.js index d2b8b9095..1963df906 100644 --- a/migrations/scripts/3_deploy_router.js +++ b/migrations/scripts/3_deploy_router.js @@ -4,7 +4,6 @@ const settings = require("../witnet.settings") const utils = require("../../scripts/utils") module.exports = async function (deployer, network, accounts) { - if (network === "test") return const realm = utils.getRealmNetworkFromArgs()[0] diff --git a/migrations/scripts/4_deploy_bytecodes.js b/migrations/scripts/4_deploy_bytecodes.js index 04ca0c531..7552109c6 100644 --- a/migrations/scripts/4_deploy_bytecodes.js +++ b/migrations/scripts/4_deploy_bytecodes.js @@ -1,9 +1,7 @@ const fs = require("fs") -const { merge } = require("lodash") const addresses = require("../witnet.addresses") -const package = require ("../../package") -const settings = require("../witnet.settings") +const thePackage = require("../../package") const utils = require("../../scripts/utils") const WitnetBytecodesProxy = artifacts.require("WitnetProxy") @@ -21,7 +19,7 @@ module.exports = async function (deployer, network, accounts) { console.info() - var proxy + let proxy if (utils.isNullAddress(addresses[ecosystem][network]?.WitnetBytecodes)) { await deployer.deploy(WitnetBytecodesProxy) proxy = await WitnetBytecodesProxy.deployed() @@ -34,10 +32,10 @@ module.exports = async function (deployer, network, accounts) { console.info(` Skipped: 'WitnetBytecodesProxy' deployed at ${proxy.address}`) } - var bytecodes + let bytecodes if ( - utils.isNullAddress(addresses[ecosystem][network]?.WitnetBytecodesImplementation) - || utils.isNullAddress(addresses[ecosystem][network]?.WitnetEncodingLib) + utils.isNullAddress(addresses[ecosystem][network]?.WitnetBytecodesImplementation) || + utils.isNullAddress(addresses[ecosystem][network]?.WitnetEncodingLib) ) { if (utils.isNullAddress(addresses[ecosystem][network]?.WitnetEncodingLib)) { await deployer.deploy(WitnetEncodingLib) @@ -52,12 +50,12 @@ module.exports = async function (deployer, network, accounts) { } await deployer.link( WitnetEncodingLib, - [ WitnetBytecodesImplementation ] + [WitnetBytecodesImplementation] ) await deployer.deploy( WitnetBytecodesImplementation, true, - utils.fromAscii(package.version) + utils.fromAscii(thePackage.version) ) bytecodes = await WitnetBytecodesImplementation.deployed() addresses[ecosystem][network].WitnetBytecodesImplementation = bytecodes.address @@ -69,13 +67,13 @@ module.exports = async function (deployer, network, accounts) { console.info(` Skipped: 'WitnetBytecodesImplementation' deployed at ${bytecodes.address}`) } - var implementation = await proxy.implementation() + const implementation = await proxy.implementation() if (implementation.toLowerCase() !== bytecodes.address.toLowerCase()) { console.info() console.info(" > WitnetBytecodesImplementation:", bytecodes.address, `(v${await bytecodes.version()})`) console.info(" > WitnetBytecodesProxy:", proxy.address) console.info(" > WitnetBytecodesProxy.implementation::", implementation) - const answer = await utils.prompt(` > Upgrade the proxy ? [y/N] `) + const answer = await utils.prompt(" > Upgrade the proxy ? [y/N] ") if (["y", "yes"].includes(answer.toLowerCase().trim())) { await proxy.upgradeTo(bytecodes.address, "0x") console.info(" > Done.") @@ -86,10 +84,10 @@ module.exports = async function (deployer, network, accounts) { } } -function saveAddresses(addrs) { +function saveAddresses (addrs) { fs.writeFileSync( "./migrations/witnet.addresses.json", JSON.stringify(addrs, null, 4), - { flag: 'w+'} + { flag: "w+" } ) -} \ No newline at end of file +} diff --git a/test/witnet_bytecodes.test.js b/test/witnet_bytecodes.test.js index 799a4785b..76021ebe0 100644 --- a/test/witnet_bytecodes.test.js +++ b/test/witnet_bytecodes.test.js @@ -1,784 +1,780 @@ const utils = require("../scripts/utils") -const { expectEvent, expectRevert } = require("@openzeppelin/test-helpers"); -const { assert } = require("chai"); -const { expectRevertCustomError } = require("custom-error-test-helper"); +const { expectEvent, expectRevert } = require("@openzeppelin/test-helpers") +const { assert } = require("chai") +const { expectRevertCustomError } = require("custom-error-test-helper") const WitnetBuffer = artifacts.require("WitnetBuffer") -const WitnetBytecodes = artifacts.require("WitnetBytecodes"); +const WitnetBytecodes = artifacts.require("WitnetBytecodes") const WitnetV2 = artifacts.require("WitnetV2") -contract('WitnetBytecodes', (accounts) => { - var creatorAddress = accounts[0]; - var firstOwnerAddress = accounts[1]; - var secondOwnerAddress = accounts[2]; - var externalAddress = accounts[3]; - var unprivilegedAddress = accounts[4] - - var bytecodes +contract("WitnetBytecodes", (accounts) => { + const creatorAddress = accounts[0] + const firstOwnerAddress = accounts[1] + // const secondOwnerAddress = accounts[2] + // const externalAddress = accounts[3] + const unprivilegedAddress = accounts[4] - before(async () => { - bytecodes = await WitnetBytecodes.new( - true, - utils.fromAscii("testing") - ) + let bytecodes + + before(async () => { + bytecodes = await WitnetBytecodes.new( + true, + utils.fromAscii("testing") + ) + }) + + beforeEach(async () => { + /* before each context */ + }) + + context("Ownable2Step", async () => { + it("should revert if transferring ownership from stranger", async () => { + await expectRevert( + bytecodes.transferOwnership(unprivilegedAddress, { from: unprivilegedAddress }), + "not the owner" + ) + }) + it("owner can start transferring ownership", async () => { + const tx = await bytecodes.transferOwnership(firstOwnerAddress, { from: creatorAddress }) + expectEvent( + tx.receipt, + "OwnershipTransferStarted", + { newOwner: firstOwnerAddress } + ) }) - - beforeEach(async () => { - /* before each context */ + it("stranger cannot accept transferring ownership", async () => { + await expectRevert( + bytecodes.acceptOwnership({ from: unprivilegedAddress }), + "not the new owner" + ) }) + it("ownership is fully transferred upon acceptance", async () => { + const tx = await bytecodes.acceptOwnership({ from: firstOwnerAddress }) + expectEvent( + tx.receipt, + "OwnershipTransferred", + { + previousOwner: creatorAddress, + newOwner: firstOwnerAddress, + } + ) + assert.equal(firstOwnerAddress, await bytecodes.owner()) + }) + }) - context("Ownable2Step", async () => { - it('should revert if transferring ownership from stranger', async () => { - await expectRevert( - bytecodes.transferOwnership(unprivilegedAddress, { from: unprivilegedAddress }), - "not the owner" - ) + context("Upgradeable", async () => { + it("should manifest to be upgradable from actual owner", async () => { + assert.equal( + await bytecodes.isUpgradableFrom(firstOwnerAddress), + true + ) + }) + it("should manifest to not be upgradable from anybody else", async () => { + assert.equal( + await bytecodes.isUpgradableFrom(unprivilegedAddress), + false + ) + }) + it("cannot be initialized more than once", async () => { + await expectRevertCustomError( + WitnetBytecodes, + bytecodes.initialize("0x", { from: firstOwnerAddress }), + "AlreadyInitialized" + ) + await expectRevertCustomError( + WitnetBytecodes, + bytecodes.initialize("0x", { from: unprivilegedAddress }), + "OnlyOwner" + ) + }) + }) + + context("IWitnetBytecodes", async () => { + let slaHash + let slaBytecode + + let concathashReducerHash + // let concathashReducerBytecode + let modeNoFiltersReducerHash + // let modeNoFitlersReducerBytecode + let stdev15ReducerHash + let stdev25ReducerHash + + let rngSourceHash + let binanceTickerHash + // let uniswapToken0PriceHash + let uniswapToken1PriceHash + let heavyRetrievalHash + let heavyRetrievalBytecode + + let rngHash + // let rngBytecode + + let btcUsdPriceFeedHash + let btcUsdPriceFeedBytecode + // let fraxUsdtPriceFeedHash + // let fraxUsdtPriceFeedBytecode + + context("verifyDataSource(..)", async () => { + context("WitnetV2.DataRequestMethods.Rng", async () => { + it("emits appropiate single event when verifying randomness data source for the first time", async () => { + const tx = await bytecodes.verifyDataSource( + 2, // requestMethod + 0, // resultMinRank + 0, // resultMaxRank + "", // requestSchema + "", // requestFQDN + "", // requestPath + "", // requestQuery + "", // requestBody + [], // requestHeaders + "0x80", // requestRadonScript + ) + expectEvent( + tx.receipt, + "NewDataSourceHash" + ) + rngSourceHash = tx.logs[0].args.hash }) - it('owner can start transferring ownership', async () => { - const tx = await bytecodes.transferOwnership(firstOwnerAddress, { from: creatorAddress }) - expectEvent( - tx.receipt, - "OwnershipTransferStarted", - { newOwner: firstOwnerAddress } - ) + it("emits no event when verifying already existing randomness data source", async () => { + const tx = await bytecodes.verifyDataSource( + 2, // requestMethod + 0, // resultMinRank + 0, // resultMaxRank + "", // requestSchema + "", // requestFQDN + "", // requestPath + "", // requestQuery + "", // requestBody + [], // requestHeaders + "0x80", // requestRadonScript + ) + assert.equal(tx.logs.length, 0, "some unexpected event was emitted") }) - it('stranger cannot accept transferring ownership', async () => { - await expectRevert( - bytecodes.acceptOwnership({ from: unprivilegedAddress }), - "not the new owner" - ) + it("generates proper hash upon offchain verification of already existing randmoness source", async () => { + const hash = await bytecodes.verifyDataSource.call( + 2, // requestMethod + 0, // resultMinRank + 0, // resultMaxRank + "", // requestSchema + "", // requestFQDN + "", // requestPath + "", // requestQuery + "", // requestBody + [], // requestHeaders + "0x80", // requestRadonScript + ) + assert.equal(hash, rngSourceHash) }) - it('ownership is fully transferred upon acceptance', async () => { - const tx = await bytecodes.acceptOwnership({ from: firstOwnerAddress }) + // ... reverts + }) + context("WitnetV2.DataRequestMethods.HttpGet", async () => { + it( + "emits new data provider and source events when verifying a new http-get source for the first time", async () => { + const tx = await bytecodes.verifyDataSource( + 1, // requestMethod + 0, // resultMinRank + 0, // resultMaxRank + "HTTPs://", // requestSchema + "api.binance.US", // requestFQDN + "api/v3/ticker/price", // requestPath + "symbol=\\0\\\\1\\", // requestQuery + "", // requestBody + [], // requestHeaders + "0x841877821864696c61737450726963658218571a000f4240185b", // requestRadonScript + ) expectEvent( - tx.receipt, - "OwnershipTransferred", - { - previousOwner: creatorAddress, - newOwner: firstOwnerAddress - } + tx.receipt, + "NewDataProvider" ) - assert.equal(firstOwnerAddress, await bytecodes.owner()) - }) - }) - - context("Upgradeable", async () => { - it('should manifest to be upgradable from actual owner', async () => { - assert.equal( - await bytecodes.isUpgradableFrom(firstOwnerAddress), - true + assert.equal(tx.logs[0].args.index, 1) + expectEvent( + tx.receipt, + "NewDataSourceHash" ) + binanceTickerHash = tx.logs[1].args.hash + }) + it("data source metadata gets stored as expected", async () => { + const ds = await bytecodes.lookupDataSource(binanceTickerHash) + assert.equal(ds.method, 1) // HTTP-GET + assert.equal(ds.resultDataType, 4) // Integer + assert.equal(ds.url, "https://api.binance.us/api/v3/ticker/price?symbol=\\0\\\\1\\") + assert.equal(ds.body, "") + assert(ds.headers.length === 0) + assert.equal(ds.script, "0x841877821864696c61737450726963658218571a000f4240185b") }) - it('should manifest to not be upgradable from anybody else', async () => { - assert.equal( - await bytecodes.isUpgradableFrom(unprivilegedAddress), - false - ) + it("emits one single event when verifying new http-get endpoint to already existing provider", async () => { + const tx = await bytecodes.verifyDataSource( + 1, // requestMethod + 0, // resultMinRank + 0, // resultMaxRank + "http://", // requestSchema + "api.binance.us", // requestFQDN + "api/v3/ticker/24hr", // requestPath + "symbol=\\0\\\\1\\", // requestQuery + "", // requestBody + [], // requestHeaders + "0x841877821864696c61737450726963658218571a000f4240185b", // requestRadonScript + ) + assert.equal(tx.logs.length, 1) + expectEvent( + tx.receipt, + "NewDataSourceHash" + ) }) - it('cannot be initialized more than once', async () => { - await expectRevertCustomError( - WitnetBytecodes, - bytecodes.initialize("0x", { from: firstOwnerAddress }), - "AlreadyInitialized" + }) + context("WitnetV2.DataRequestMethods.HttpPost", async () => { + it( + "emits new data provider and source events when verifying a new http-post source for the first time", async () => { + const tx = await bytecodes.verifyDataSource( + 3, // requestMethod + 0, // resultMinRank + 0, // resultMaxRank + "HTTPs://", // requestSchema + "api.thegraph.com", // requestFQDN + "subgraphs/name/uniswap/uniswap-v3", // requestPath + "", // requestQuery + "{\"query\":\"{pool(id:\"\\0\\\"){token1Price}}\"}", // requestBody + [ + ["user-agent", "witnet-rust"], + ["content-type", "text/html; charset=utf-8"], + ], // requestHeaders + "0x861877821866646461746182186664706f6f6c8218646b746f6b656e3150726963658218571a000f4240185b", // requestRadonScript ) - await expectRevertCustomError( - WitnetBytecodes, - bytecodes.initialize("0x", { from: unprivilegedAddress }), - "OnlyOwner" + expectEvent( + tx.receipt, + "NewDataProvider" ) + assert.equal(tx.logs[0].args.index, 2) + expectEvent( + tx.receipt, + "NewDataSourceHash" + ) + uniswapToken1PriceHash = tx.logs[1].args.hash + }) + it("data source metadata gets stored as expected", async () => { + const ds = await bytecodes.lookupDataSource(uniswapToken1PriceHash) + assert.equal(ds.method, 3) // HTTP-GET + assert.equal(ds.resultDataType, 4) // Integer + assert.equal(ds.url, "https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3") + assert.equal(ds.body, "{\"query\":\"{pool(id:\"\\0\\\"){token1Price}}\"}") + assert(ds.headers.length === 2) + assert.equal(ds.headers[0][0], "user-agent") + assert.equal(ds.headers[0][1], "witnet-rust") + assert.equal(ds.headers[1][0], "content-type") + assert.equal(ds.headers[1][1], "text/html; charset=utf-8") + assert.equal(ds.script, "0x861877821866646461746182186664706f6f6c8218646b746f6b656e3150726963658218571a000f4240185b") }) + }) }) - context("IWitnetBytecodes", async () => { - - var slaHash - var slaBytecode - - var concathashReducerHash - var concathashReducerBytecode - var modeNoFiltersReducerHash - var modeNoFitlersReducerBytecode - var stdev15ReducerHash - var stdev25ReducerHash - - var rngSourceHash - var binanceTickerHash - var uniswapToken0PriceHash - var uniswapToken1PriceHash - var heavyRetrievalHash - var heavyRetrievalBytecode - - var rngHash - var rngBytecode - - var btcUsdPriceFeedHash - var btcUsdPriceFeedBytecode - var fraxUsdtPriceFeedHash - var fraxUsdtPriceFeedBytecode + context("verifyRadonReducer(..)", async () => { + it("emits event when verifying new radon reducer with no filter", async () => { + const tx = await bytecodes.verifyRadonReducer([ + 11, // opcode: ConcatenateAndHash + [], // filters + "0x", // script + ]) + expectEvent( + tx.receipt, + "NewRadonReducerHash" + ) + concathashReducerHash = tx.logs[0].args.hash + // concathashReducerBytecode = tx.logs[0].args.bytecode + }) + it("emits no event when verifying an already verified radon sla with no filter", async () => { + const tx = await bytecodes.verifyRadonReducer([ + 11, // ConcatenateAndHash + [], // filters + "0x", // script + ]) + assert.equal( + tx.logs.length, + 0, + "some unexpected event was emitted" + ) + }) + it("generates proper hash upon offchain call", async () => { + const hash = await bytecodes.verifyRadonReducer.call([ + 11, // ConcatenateAndHash + [], // filters + "0x", // script + ]) + assert.equal(hash, concathashReducerHash) + }) + it("reverts custom error if verifying radon reducer with unsupported opcode", async () => { + await expectRevertCustomError( + WitnetV2, + bytecodes.verifyRadonReducer([ + 0, // Minimum + [], // filters + "0x", // script + ]), + "UnsupportedRadonReducerOpcode" + ) + }) + it("reverts custom error if verifying radon reducer with at least one unsupported filter", async () => { + await expectRevertCustomError( + WitnetV2, + bytecodes.verifyRadonReducer([ + 5, // AverageMedian + [ + [8, "0x"], // Mode: supported + [0, "0x"], // Greater than: not yet supported + ], + "0x", // script + ]), + "UnsupportedRadonFilterOpcode" + ) + }) + it("reverts custom error if verifying radon reducer with stdev filter but no args", async () => { + await expectRevertCustomError( + WitnetV2, + bytecodes.verifyRadonReducer([ + 2, // Mode + [ + [5, "0x"], // Standard deviation filter + ], + "0x", // script + ]), + "RadonFilterMissingArgs" + ) + }) + it("verifying radon reducer with stdev filter and args works", async () => { + let tx = await bytecodes.verifyRadonReducer([ + 3, // AverageMean + [ + [5, "0xF93E00"], // StdDev(1.5) filter + ], + "0x", // script + ]) + expectEvent( + tx.receipt, + "NewRadonReducerHash" + ) + stdev15ReducerHash = tx.logs[0].args.hash + tx = await bytecodes.verifyRadonReducer([ + 2, // Mode + [ + [5, "0xF94100"], // StdDev(2.5) filter + ], + "0x", // script + ]) + stdev25ReducerHash = tx.logs[0].args.hash + }) + }) - context("verifyDataSource(..)", async() => { - context("WitnetV2.DataRequestMethods.Rng", async () => { - it('emits appropiate single event when verifying randomness data source for the first time', async () => { - const tx = await bytecodes.verifyDataSource( - 2, // requestMethod - 0, // resultMinRank - 0, // resultMaxRank - "", // requestSchema - "", // requestFQDN - "", // requestPath - "", // requestQuery - "", // requestBody - [], // requestHeaders - "0x80", // requestRadonScript - ) - expectEvent( - tx.receipt, - "NewDataSourceHash" - ) - rngSourceHash = tx.logs[0].args.hash - }) - it('emits no event when verifying already existing randomness data source', async () => { - const tx = await bytecodes.verifyDataSource( - 2, // requestMethod - 0, // resultMinRank - 0, // resultMaxRank - "", // requestSchema - "", // requestFQDN - "", // requestPath - "", // requestQuery - "", // requestBody - [], // requestHeaders - "0x80", // requestRadonScript - ) - 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 bytecodes.verifyDataSource.call( - 2, // requestMethod - 0, // resultMinRank - 0, // resultMaxRank - "", // requestSchema - "", // requestFQDN - "", // requestPath - "", // requestQuery - "", // requestBody - [], // requestHeaders - "0x80", // requestRadonScript - ) - assert.equal(hash, rngSourceHash); - }) - // ... reverts - }) - context("WitnetV2.DataRequestMethods.HttpGet", async () => { - it('emits new data provider and source events when verifying a new http-get source for the first time', async () => { - const tx = await bytecodes.verifyDataSource( - 1, // requestMethod - 0, // resultMinRank - 0, // resultMaxRank - "HTTPs://", // requestSchema - "api.binance.US", // requestFQDN - "api/v3/ticker/price", // requestPath - "symbol=\\0\\\\1\\", // requestQuery - "", // requestBody - [], // requestHeaders - "0x841877821864696c61737450726963658218571a000f4240185b", // requestRadonScript - ) - expectEvent( - tx.receipt, - "NewDataProvider" - ) - assert.equal(tx.logs[0].args.index, 1) - expectEvent( - tx.receipt, - "NewDataSourceHash" - ) - binanceTickerHash = tx.logs[1].args.hash - }) - it('data source metadata gets stored as expected', async () => { - const ds = await bytecodes.lookupDataSource(binanceTickerHash) - assert.equal(ds.method, 1) // HTTP-GET - assert.equal(ds.resultDataType, 4) // Integer - assert.equal(ds.url, "https://api.binance.us/api/v3/ticker/price?symbol=\\0\\\\1\\") - assert.equal(ds.body, "") - assert(ds.headers.length == 0) - assert.equal(ds.script, "0x841877821864696c61737450726963658218571a000f4240185b") - }) - it('emits one single event when verifying new http-get endpoint to already existing provider', async () => { - const tx = await bytecodes.verifyDataSource( - 1, // requestMethod - 0, // resultMinRank - 0, // resultMaxRank - "http://", // requestSchema - "api.binance.us", // requestFQDN - "api/v3/ticker/24hr", // requestPath - "symbol=\\0\\\\1\\", // requestQuery - "", // requestBody - [], // requestHeaders - "0x841877821864696c61737450726963658218571a000f4240185b", // requestRadonScript - ) - assert.equal(tx.logs.length, 1) - expectEvent( - tx.receipt, - "NewDataSourceHash" - ) - }) - }) - context("WitnetV2.DataRequestMethods.HttpPost", async () => { - it('emits new data provider and source events when verifying a new http-post source for the first time', async () => { - const tx = await bytecodes.verifyDataSource( - 3, // requestMethod - 0, // resultMinRank - 0, // resultMaxRank - "HTTPs://", // requestSchema - "api.thegraph.com", // requestFQDN - "subgraphs/name/uniswap/uniswap-v3", // requestPath - "", // requestQuery - `{"query":"{pool(id:\"\\0\\\"){token1Price}}"}`, // requestBody - [ - ["user-agent", "witnet-rust"], - ["content-type", "text/html; charset=utf-8"], - ], // requestHeaders - "0x861877821866646461746182186664706f6f6c8218646b746f6b656e3150726963658218571a000f4240185b", // requestRadonScript - ) - expectEvent( - tx.receipt, - "NewDataProvider" - ) - assert.equal(tx.logs[0].args.index, 2) - expectEvent( - tx.receipt, - "NewDataSourceHash" - ) - uniswapToken1PriceHash = tx.logs[1].args.hash - }) - it('data source metadata gets stored as expected', async () => { - const ds = await bytecodes.lookupDataSource(uniswapToken1PriceHash) - assert.equal(ds.method, 3) // HTTP-GET - assert.equal(ds.resultDataType, 4) // Integer - assert.equal(ds.url, "https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3") - assert.equal(ds.body, `{"query":"{pool(id:\"\\0\\\"){token1Price}}"}`) - assert(ds.headers.length == 2) - assert.equal(ds.headers[0][0], "user-agent") - assert.equal(ds.headers[0][1], "witnet-rust") - assert.equal(ds.headers[1][0], "content-type") - assert.equal(ds.headers[1][1], "text/html; charset=utf-8") - assert.equal(ds.script, "0x861877821866646461746182186664706f6f6c8218646b746f6b656e3150726963658218571a000f4240185b") - }) - }) + context("verifyRadonRetrieval(..)", async () => { + context("Use case: Randomness", async () => { + it("emits single event when verifying new radomness retrieval", async () => { + let tx = await bytecodes.verifyRadonReducer([ + 2, // Mode + [], // no filters + "0x", // script + ]) + expectEvent( + tx.receipt, + "NewRadonReducerHash" + ) + modeNoFiltersReducerHash = tx.logs[0].args.hash + // modeNoFiltersReducerBytecode = tx.logs[0].args.bytecode + tx = await bytecodes.verifyRadonRetrieval( + 0, // resultDataType + 0, // resultMaxVariableSize + [ // sources + rngSourceHash, + ], + [[]], // sourcesArgs + modeNoFiltersReducerHash, // aggregator + concathashReducerHash, // tally + ) + assert(tx.logs.length === 1) + expectEvent( + tx.receipt, + "NewRadonRetrievalHash" + ) + rngHash = tx.logs[0].args.hash + // rngBytecode = tx.logs[0].args.bytecode }) - - context("verifyRadonReducer(..)", async() => { - it('emits event when verifying new radon reducer with no filter', async () => { - const tx = await bytecodes.verifyRadonReducer([ - 11, // opcode: ConcatenateAndHash - [], // filters - "0x" // script - ]) - expectEvent( - tx.receipt, - "NewRadonReducerHash" - ) - concathashReducerHash = tx.logs[0].args.hash - concathashReducerBytecode = tx.logs[0].args.bytecode - }) - it('emits no event when verifying an already verified radon sla with no filter', async () => { - const tx = await bytecodes.verifyRadonReducer([ - 11, // ConcatenateAndHash - [], // filters - "0x" // script - ]) - assert.equal( - tx.logs.length, - 0, - "some unexpected event was emitted" - ) - }) - it('generates proper hash upon offchain call', async () => { - const hash = await bytecodes.verifyRadonReducer.call([ - 11, // ConcatenateAndHash - [], // filters - "0x" // script - ]) - assert.equal(hash, concathashReducerHash) - }) - it('reverts custom error if verifying radon reducer with unsupported opcode', async () => { - await expectRevertCustomError( - WitnetV2, - bytecodes.verifyRadonReducer([ - 0, // Minimum - [], // filters - "0x" // script - ]), - "UnsupportedRadonReducerOpcode" - ) - }) - it('reverts custom error if verifying radon reducer with at least one unsupported filter', async () => { - await expectRevertCustomError( - WitnetV2, - bytecodes.verifyRadonReducer([ - 5, // AverageMedian - [ - [ 8, "0x" ], // Mode: supported - [ 0, "0x" ], // Greater than: not yet supported - ], - "0x" // script - ]), - "UnsupportedRadonFilterOpcode" - ) - }) - it('reverts custom error if verifying radon reducer with stdev filter but no args', async () => { - await expectRevertCustomError( - WitnetV2, - bytecodes.verifyRadonReducer([ - 2, // Mode - [ - [ 5, "0x" ], // Standard deviation filter - ], - "0x" // script - ]), - "RadonFilterMissingArgs" - ) - }) - it('verifying radon reducer with stdev filter and args works', async () => { - var tx = await bytecodes.verifyRadonReducer([ - 3, // AverageMean - [ - [ 5, "0xF93E00" ], // StdDev(1.5) filter - ], - "0x" // script - ]) - expectEvent( - tx.receipt, - "NewRadonReducerHash" - ) - stdev15ReducerHash = tx.logs[0].args.hash - tx = await bytecodes.verifyRadonReducer([ - 2, // Mode - [ - [ 5, "0xF94100" ], // StdDev(2.5) filter - ], - "0x" // script - ]) - stdev25ReducerHash = tx.logs[0].args.hash - }) + it("emits no event when verifying same randomness retrieval", async () => { + const tx = await bytecodes.verifyRadonRetrieval( + 0, // resultDataType + 0, // resultMaxVariableSize + [ // sources + rngSourceHash, + ], + [[]], // sourcesArgs + modeNoFiltersReducerHash, // aggregator + concathashReducerHash, // tally + ) + assert(tx.logs.length === 0) + }) + it("generates same hash when verifying same randomness retrieval offchain", async () => { + const hash = await bytecodes.verifyRadonRetrieval.call( + 0, // resultDataType + 0, // resultMaxVariableSize + [ // sources + rngSourceHash, + ], + [[]], // sourcesArgs + modeNoFiltersReducerHash, // aggregator + concathashReducerHash, // tally + ) + assert.equal(hash, rngHash) + }) + }) + context("Use case: Price feeds", async () => { + it("reverts custom error if trying to verify retrieval w/ templated source and 0 args out of 2", async () => { + await expectRevertCustomError( + WitnetBuffer, + bytecodes.verifyRadonRetrieval( + 4, // resultDataType + 0, // resultMaxVariableSize + [ // sources + binanceTickerHash, + ], + [ // sourcesArgs + [], + ], + stdev15ReducerHash, // aggregator + stdev25ReducerHash, // tally + ), + "MissingArgs", [ + 1, // expected + 0, // given + ] + ) + }) + it("reverts custom error if trying to verify retrieval w/ templated source and 1 args out of 2", async () => { + await expectRevertCustomError( + WitnetBuffer, + bytecodes.verifyRadonRetrieval( + 4, // resultDataType + 0, // resultMaxVariableSize + [ // sources + binanceTickerHash, + ], + [ // sourcesArgs + ["BTC"], + ], + stdev15ReducerHash, // aggregator + stdev25ReducerHash, // tally + ), + "MissingArgs", [ + 2, // expected + 1, // given + ] + ) }) - - context("verifyRadonRetrieval(..)", async () => { - context("Use case: Randomness", async () => { - it('emits single event when verifying new radomness retrieval', async () => { - var tx = await bytecodes.verifyRadonReducer([ - 2, // Mode - [], // no filters - "0x" // script - ]); - expectEvent( - tx.receipt, - "NewRadonReducerHash" - ) - modeNoFiltersReducerHash = tx.logs[0].args.hash - modeNoFiltersReducerBytecode = tx.logs[0].args.bytecode - tx = await bytecodes.verifyRadonRetrieval( - 0, // resultDataType - 0, // resultMaxVariableSize - [ // sources - rngSourceHash - ], - [[]], // sourcesArgs - modeNoFiltersReducerHash, // aggregator - concathashReducerHash, // tally - ) - assert(tx.logs.length == 1) - expectEvent( - tx.receipt, - "NewRadonRetrievalHash" - ) - rngHash = tx.logs[0].args.hash - rngBytecode = tx.logs[0].args.bytecode - }) - it('emits no event when verifying same randomness retrieval', async () => { - const tx = await bytecodes.verifyRadonRetrieval( - 0, // resultDataType - 0, // resultMaxVariableSize - [ // sources - rngSourceHash - ], - [[]], // sourcesArgs - modeNoFiltersReducerHash, // aggregator - concathashReducerHash, // tally - ) - assert(tx.logs.length == 0) - }) - it('generates same hash when verifying same randomness retrieval offchain', async () => { - const hash = await bytecodes.verifyRadonRetrieval.call( - 0, // resultDataType - 0, // resultMaxVariableSize - [ // sources - rngSourceHash - ], - [[]], // sourcesArgs - modeNoFiltersReducerHash, // aggregator - concathashReducerHash, // tally - ) - assert.equal(hash, rngHash) - }) - }) - context("Use case: Price feeds", async () => { - it('reverts custom error if trying to verify retrieval w/ templated source and 0 args out of 2', async () => { - await expectRevertCustomError( - WitnetBuffer, - bytecodes.verifyRadonRetrieval( - 4, // resultDataType - 0, // resultMaxVariableSize - [ // sources - binanceTickerHash, - ], - [ // sourcesArgs - [] - ], - stdev15ReducerHash, // aggregator - stdev25ReducerHash, // tally - ), - "MissingArgs", [ - 1, // expected - 0, // given - ] - ) - }) - it('reverts custom error if trying to verify retrieval w/ templated source and 1 args out of 2', async () => { - await expectRevertCustomError( - WitnetBuffer, - bytecodes.verifyRadonRetrieval( - 4, // resultDataType - 0, // resultMaxVariableSize - [ // sources - binanceTickerHash, - ], - [ // sourcesArgs - [ "BTC" ] - ], - stdev15ReducerHash, // aggregator - stdev25ReducerHash, // tally - ), - "MissingArgs", [ - 2, // expected - 1, // given - ] - ) - }) - it('emits single event when verifying new price feed retrieval for the first time', async () => { - const tx = await bytecodes.verifyRadonRetrieval( - 4, // resultDataType - 0, // resultMaxVariableSize, - [ // sources - binanceTickerHash, - ], - [ - [ "BTC", "USD" ] // binance ticker args - ], - stdev15ReducerHash, // aggregator - stdev25ReducerHash, // tally - ) - assert(tx.logs.length == 1) - expectEvent( - tx.receipt, - "NewRadonRetrievalHash" - ) - btcUsdPriceFeedHash = tx.logs[0].args.hash - btcUsdPriceFeedBytecode = tx.logs[0].args.bytecode - }) - it('verifying radon retrieval with repeated sources works', async () => { - const tx = await bytecodes.verifyRadonRetrieval( - 4, // resultDataType - 0, // resultMaxVariableSize, - [ // sources - binanceTickerHash, - binanceTickerHash, - ], - [ - [ "BTC", "USD" ], // binance ticker args - [ "BTC", "USD" ], // binance ticker args - ], - stdev15ReducerHash, // aggregator - stdev25ReducerHash, // tally - ) - assert(tx.logs.length == 1) - expectEvent( - tx.receipt, - "NewRadonRetrievalHash" - ) - }) - it('reverts if trying to verify radon retrieval w/ incompatible sources', async () => { - await expectRevertCustomError( - WitnetV2, - bytecodes.verifyRadonRetrieval( - 4, // resultDataType - 0, // resultMaxVariableSize, - [ // sources - binanceTickerHash, - rngSourceHash, - ], - [ - [ "BTC", "USD" ], // binance ticker args - [], - ], - stdev15ReducerHash, // aggregator - stdev25ReducerHash, // tally - ), - "RadonRetrievalResultsMismatch", [ - 1, // index - 0, // read - 4, // expected - ] - ) - }) - it('emits single event when verifying new radon retrieval w/ http-post source', async () => { - const tx = await bytecodes.verifyRadonRetrieval( - 4, // resultDataType - 0, // resultMaxVariableSize, - [ // sources - uniswapToken1PriceHash, - ], - [ - [ "0xc2a856c3aff2110c1171b8f942256d40e980c726" ] // pair id - ], - stdev15ReducerHash, // aggregator - stdev25ReducerHash, // tally - ) - assert(tx.logs.length == 1) - expectEvent( - tx.receipt, - "NewRadonRetrievalHash" - ) - fraxUsdtPriceFeedHash = tx.logs[0].args.hash - fraxUsdtPriceFeedBytecode = tx.logs[0].args.bytecode - }) - it('emits single event when verifying new radon retrieval w/ repeated http-post sources', async () => { - const tx = await bytecodes.verifyRadonRetrieval( - 4, // resultDataType - 0, // resultMaxVariableSize, - [ // sources - uniswapToken1PriceHash, - uniswapToken1PriceHash, - uniswapToken1PriceHash, - uniswapToken1PriceHash, - uniswapToken1PriceHash, - uniswapToken1PriceHash, - ], - [ - [ "0xc2a856c3aff2110c1171b8f942256d40e980c726" ], // pair id - [ "0xc2a856c3aff2110c1171b8f942256d40e980c726" ], // pair id - [ "0xc2a856c3aff2110c1171b8f942256d40e980c726" ], // pair id - [ "0xc2a856c3aff2110c1171b8f942256d40e980c726" ], // pair id - [ "0xc2a856c3aff2110c1171b8f942256d40e980c726" ], // pair id - [ "0xc2a856c3aff2110c1171b8f942256d40e980c726" ], // pair id - ], - stdev15ReducerHash, // aggregator - stdev25ReducerHash, // tally - ) - assert(tx.logs.length == 1) - expectEvent( - tx.receipt, - "NewRadonRetrievalHash" - ) - heavyRetrievalHash = tx.logs[0].args.hash - heavyRetrievalBytecode = tx.logs[0].args.bytecode - }) - }) - + it("emits single event when verifying new price feed retrieval for the first time", async () => { + const tx = await bytecodes.verifyRadonRetrieval( + 4, // resultDataType + 0, // resultMaxVariableSize, + [ // sources + binanceTickerHash, + ], + [ + ["BTC", "USD"], // binance ticker args + ], + stdev15ReducerHash, // aggregator + stdev25ReducerHash, // tally + ) + assert(tx.logs.length === 1) + expectEvent( + tx.receipt, + "NewRadonRetrievalHash" + ) + btcUsdPriceFeedHash = tx.logs[0].args.hash + btcUsdPriceFeedBytecode = tx.logs[0].args.bytecode }) + it("verifying radon retrieval with repeated sources works", async () => { + const tx = await bytecodes.verifyRadonRetrieval( + 4, // resultDataType + 0, // resultMaxVariableSize, + [ // sources + binanceTickerHash, + binanceTickerHash, + ], + [ + ["BTC", "USD"], // binance ticker args + ["BTC", "USD"], // binance ticker args + ], + stdev15ReducerHash, // aggregator + stdev25ReducerHash, // tally + ) + assert(tx.logs.length === 1) + expectEvent( + tx.receipt, + "NewRadonRetrievalHash" + ) + }) + it("reverts if trying to verify radon retrieval w/ incompatible sources", async () => { + await expectRevertCustomError( + WitnetV2, + bytecodes.verifyRadonRetrieval( + 4, // resultDataType + 0, // resultMaxVariableSize, + [ // sources + binanceTickerHash, + rngSourceHash, + ], + [ + ["BTC", "USD"], // binance ticker args + [], + ], + stdev15ReducerHash, // aggregator + stdev25ReducerHash, // tally + ), + "RadonRetrievalResultsMismatch", [ + 1, // index + 0, // read + 4, // expected + ] + ) + }) + it("emits single event when verifying new radon retrieval w/ http-post source", async () => { + const tx = await bytecodes.verifyRadonRetrieval( + 4, // resultDataType + 0, // resultMaxVariableSize, + [ // sources + uniswapToken1PriceHash, + ], + [ + ["0xc2a856c3aff2110c1171b8f942256d40e980c726"], // pair id + ], + stdev15ReducerHash, // aggregator + stdev25ReducerHash, // tally + ) + assert(tx.logs.length === 1) + expectEvent( + tx.receipt, + "NewRadonRetrievalHash" + ) + // fraxUsdtPriceFeedHash = tx.logs[0].args.hash + // fraxUsdtPriceFeedBytecode = tx.logs[0].args.bytecode + }) + it("emits single event when verifying new radon retrieval w/ repeated http-post sources", async () => { + const tx = await bytecodes.verifyRadonRetrieval( + 4, // resultDataType + 0, // resultMaxVariableSize, + [ // sources + uniswapToken1PriceHash, + uniswapToken1PriceHash, + uniswapToken1PriceHash, + uniswapToken1PriceHash, + uniswapToken1PriceHash, + uniswapToken1PriceHash, + ], + [ + ["0xc2a856c3aff2110c1171b8f942256d40e980c726"], // pair id + ["0xc2a856c3aff2110c1171b8f942256d40e980c726"], // pair id + ["0xc2a856c3aff2110c1171b8f942256d40e980c726"], // pair id + ["0xc2a856c3aff2110c1171b8f942256d40e980c726"], // pair id + ["0xc2a856c3aff2110c1171b8f942256d40e980c726"], // pair id + ["0xc2a856c3aff2110c1171b8f942256d40e980c726"], // pair id + ], + stdev15ReducerHash, // aggregator + stdev25ReducerHash, // tally + ) + assert(tx.logs.length === 1) + expectEvent( + tx.receipt, + "NewRadonRetrievalHash" + ) + heavyRetrievalHash = tx.logs[0].args.hash + heavyRetrievalBytecode = tx.logs[0].args.bytecode + }) + }) + }) - context("verifyRadonSLA(..)", async () => { + context("verifyRadonSLA(..)", async () => { + it("emits event when verifying new radon sla", async () => { + const tx = await bytecodes.verifyRadonSLA([ + 10 ** 6, + 10, + 10 ** 6, + 51, + 5 * 10 ** 9, + ]) + expectEvent( + tx.receipt, + "NewRadonSLAHash" + ) + slaHash = tx.logs[0].args.hash + slaBytecode = tx.logs[0].args.bytecode + }) + it("emits no event when verifying an already verified radon sla", async () => { + const tx = await bytecodes.verifyRadonSLA([ + 10 ** 6, + 10, + 10 ** 6, + 51, + 5 * 10 ** 9, + ]) + assert.equal( + tx.logs.length, + 0, + "some unexpected event was emitted" + ) + }) + it("generates proper hash upon offchain call", async () => { + const hash = await bytecodes.verifyRadonSLA.call([ + 10 ** 6, + 10, + 10 ** 6, + 51, + 5 * 10 ** 9, + ]) + assert.equal(hash, slaHash) + }) + it("reverts custom error if verifying radon sla with no reward", async () => { + await expectRevertCustomError( + WitnetV2, + bytecodes.verifyRadonSLA([ + 0, + 10, + 10 ** 6, + 51, + 5 * 10 ** 9, + ]), + "RadonSlaNoReward" + ) + }) + it("reverts custom error if verifying radon sla with no witnesses", async () => { + await expectRevertCustomError( + WitnetV2, + bytecodes.verifyRadonSLA([ + 10 ** 6, + 0, + 10 ** 6, + 51, + 5 * 10 ** 9, + ]), + "RadonSlaNoWitnesses" + ) + }) + it("reverts custom error if verifying radon sla with too many witnesses", async () => { + await expectRevertCustomError( + WitnetV2, + bytecodes.verifyRadonSLA([ + 10 ** 6, + 500, + 10 ** 6, + 51, + 5 * 10 ** 9, + ]), + "RadonSlaTooManyWitnesses" + ) + }) + it("reverts custom error if verifying radon sla with quorum out of range", async () => { + await expectRevertCustomError( + WitnetV2, + bytecodes.verifyRadonSLA([ + 10 ** 6, + 10, + 10 ** 6, + 50, + 5 * 10 ** 9, + ]), + "RadonSlaConsensusOutOfRange" + ) + await expectRevertCustomError( + WitnetV2, + bytecodes.verifyRadonSLA([ + 10 ** 6, + 10, + 10 ** 6, + 100, + 5 * 10 ** 9, + ]), + "RadonSlaConsensusOutOfRange" + ) + }) + it("reverts custom error if verifying radon sla with too low collateral", async () => { + await expectRevertCustomError( + WitnetV2, + bytecodes.verifyRadonSLA([ + 10 ** 6, + 10, + 10 ** 6, + 51, + 10 ** 6, + ]), + "RadonSlaLowCollateral" + ) + }) + }) - it('emits event when verifying new radon sla', async () => { - const tx = await bytecodes.verifyRadonSLA([ - 10 ** 6, - 10, - 10 ** 6, - 51, - 5 * 10 ** 9 - ]) - expectEvent( - tx.receipt, - "NewRadonSLAHash" - ) - slaHash = tx.logs[0].args.hash - slaBytecode = tx.logs[0].args.bytecode - }) - it('emits no event when verifying an already verified radon sla', async () => { - const tx = await bytecodes.verifyRadonSLA([ - 10 ** 6, - 10, - 10 ** 6, - 51, - 5 * 10 ** 9 - ]) - assert.equal( - tx.logs.length, - 0, - "some unexpected event was emitted" - ) - }) - it('generates proper hash upon offchain call', async() => { - const hash = await bytecodes.verifyRadonSLA.call([ - 10 ** 6, - 10, - 10 ** 6, - 51, - 5 * 10 ** 9 - ]) - assert.equal(hash, slaHash); - }) - it('reverts custom error if verifying radon sla with no reward', async () => { - await expectRevertCustomError( - WitnetV2, - bytecodes.verifyRadonSLA([ - 0, - 10, - 10 ** 6, - 51, - 5 * 10 ** 9 - ]), - "RadonSlaNoReward" - ) - }) - it('reverts custom error if verifying radon sla with no witnesses', async () => { - await expectRevertCustomError( - WitnetV2, - bytecodes.verifyRadonSLA([ - 10 ** 6, - 0, - 10 ** 6, - 51, - 5 * 10 ** 9 - ]), - "RadonSlaNoWitnesses" - ) - }) - it('reverts custom error if verifying radon sla with too many witnesses', async () => { - await expectRevertCustomError( - WitnetV2, - bytecodes.verifyRadonSLA([ - 10 ** 6, - 500, - 10 ** 6, - 51, - 5 * 10 ** 9 - ]), - "RadonSlaTooManyWitnesses" - ) - }) - it('reverts custom error if verifying radon sla with quorum out of range', async () => { - await expectRevertCustomError( - WitnetV2, - bytecodes.verifyRadonSLA([ - 10 ** 6, - 10, - 10 ** 6, - 50, - 5 * 10 ** 9 - ]), - "RadonSlaConsensusOutOfRange" - ) - await expectRevertCustomError( - WitnetV2, - bytecodes.verifyRadonSLA([ - 10 ** 6, - 10, - 10 ** 6, - 100, - 5 * 10 ** 9 - ]), - "RadonSlaConsensusOutOfRange" - ) - }) - it('reverts custom error if verifying radon sla with too low collateral', async () => { - await expectRevertCustomError( - WitnetV2, - bytecodes.verifyRadonSLA([ - 10 ** 6, - 10, - 10 ** 6, - 51, - 10 ** 6 - ]), - "RadonSlaLowCollateral" - ) - }) + context("bytecodeOf(..)", async () => { + context("radon retrievals", async () => { + it("reverts if trying to get bytecode from unknown radon retrieval", async () => { + await expectRevertCustomError( + WitnetBytecodes, + bytecodes.bytecodeOf("0x0"), + "UnknownRadonRetrieval" + ) }) - - context("bytecodeOf(..)", async () => { - context("radon retrievals", async () => { - it('reverts if trying to get bytecode from unknown radon retrieval', async () => { - await expectRevertCustomError( - WitnetBytecodes, - bytecodes.bytecodeOf("0x0"), - "UnknownRadonRetrieval" - ) - }) - it('works if trying to get bytecode onchain from known radon retrieval', async () => { - await bytecodes.bytecodeOf(btcUsdPriceFeedHash); - }) - it('returns expected bytecode if getting it offchain from known radon retrieval', async () =>{ - const bytecode = await bytecodes.bytecodeOf(btcUsdPriceFeedHash) - assert.equal(bytecode, btcUsdPriceFeedBytecode) - }) - }) - context("radon slas", async () => { - it('reverts if trying to get bytecode from unknown radon sla', async () => { - await expectRevertCustomError( - WitnetBytecodes, - bytecodes.bytecodeOf(btcUsdPriceFeedHash, "0x0"), - "UnknownRadonSLA" - ) - }) - it('works if trying to get bytecode onchain from known radon retrieval and sla', async () => { - await bytecodes.bytecodeOf(btcUsdPriceFeedHash, slaHash) - }) - it('returns expected bytecode if getting it offchain from known radon retrieval and sla', async () => { - const bytecode = await bytecodes.bytecodeOf.call(heavyRetrievalHash, slaHash) - assert.equal( - heavyRetrievalBytecode + slaBytecode.slice(2), - bytecode - ) - }) - }) - }) + it("works if trying to get bytecode onchain from known radon retrieval", async () => { + await bytecodes.bytecodeOf(btcUsdPriceFeedHash) }) - - context("hashOf(..)", async () => { - it("hashing unknown radon retrieval doesn't revert", async () => { - await bytecodes.hashOf("0x", slaHash) - }) - it("hashing unknown radon sla doesn't revert", async () => { - await bytecodes.hashOf(btcUsdPriceFeedHash, "0x0") - }) - it("hashing of known radon retrieval and sla works", async () => { - await bytecodes.hashOf(btcUsdPriceFeedHash, slaHash) - }) + it("returns expected bytecode if getting it offchain from known radon retrieval", async () => { + const bytecode = await bytecodes.bytecodeOf(btcUsdPriceFeedHash) + assert.equal(bytecode, btcUsdPriceFeedBytecode) }) - - context("hashWeightRewardOf(..)", async () => { - it("hashing unknown radon retrieval reverts", async () => { - await expectRevertCustomError( - WitnetBytecodes, - bytecodes.hashWeightWitsOf("0x0", slaHash), - "UnknownRadonRetrieval" - ) - }) - it("hashing unknown radon sla reverts", async () => { - await expectRevertCustomError( - WitnetBytecodes, - bytecodes.hashWeightWitsOf(btcUsdPriceFeedHash, "0x0"), - "UnknownRadonSLA" - ) - }) - it("hashing of known radon retrieval and sla works", async () => { - await bytecodes.hashWeightWitsOf( - heavyRetrievalHash, slaHash - ) - }) + }) + context("radon slas", async () => { + it("reverts if trying to get bytecode from unknown radon sla", async () => { + await expectRevertCustomError( + WitnetBytecodes, + bytecodes.bytecodeOf(btcUsdPriceFeedHash, "0x0"), + "UnknownRadonSLA" + ) }) + it("works if trying to get bytecode onchain from known radon retrieval and sla", async () => { + await bytecodes.bytecodeOf(btcUsdPriceFeedHash, slaHash) + }) + it("returns expected bytecode if getting it offchain from known radon retrieval and sla", async () => { + const bytecode = await bytecodes.bytecodeOf.call(heavyRetrievalHash, slaHash) + assert.equal( + heavyRetrievalBytecode + slaBytecode.slice(2), + bytecode + ) + }) + }) + }) + + context("hashOf(..)", async () => { + it("hashing unknown radon retrieval doesn't revert", async () => { + await bytecodes.hashOf("0x", slaHash) + }) + it("hashing unknown radon sla doesn't revert", async () => { + await bytecodes.hashOf(btcUsdPriceFeedHash, "0x0") + }) + it("hashing of known radon retrieval and sla works", async () => { + await bytecodes.hashOf(btcUsdPriceFeedHash, slaHash) + }) + }) + context("hashWeightRewardOf(..)", async () => { + it("hashing unknown radon retrieval reverts", async () => { + await expectRevertCustomError( + WitnetBytecodes, + bytecodes.hashWeightWitsOf("0x0", slaHash), + "UnknownRadonRetrieval" + ) + }) + it("hashing unknown radon sla reverts", async () => { + await expectRevertCustomError( + WitnetBytecodes, + bytecodes.hashWeightWitsOf(btcUsdPriceFeedHash, "0x0"), + "UnknownRadonSLA" + ) + }) + it("hashing of known radon retrieval and sla works", async () => { + await bytecodes.hashWeightWitsOf( + heavyRetrievalHash, slaHash + ) + }) }) - -}); + }) +}) From 7ab169c52546856381e36bd78e02f950318c4e1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Tue, 10 Jan 2023 16:06:47 +0100 Subject: [PATCH 054/119] fix: test helper contracts --- test/helpers/WitnetRequestBoardTrojanHorseBadProxiable.sol | 2 +- test/helpers/WitnetRequestBoardTrojanHorseNotUpgradable.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/helpers/WitnetRequestBoardTrojanHorseBadProxiable.sol b/test/helpers/WitnetRequestBoardTrojanHorseBadProxiable.sol index 93f6ac71b..88cafe84b 100644 --- a/test/helpers/WitnetRequestBoardTrojanHorseBadProxiable.sol +++ b/test/helpers/WitnetRequestBoardTrojanHorseBadProxiable.sol @@ -38,7 +38,7 @@ contract WitnetRequestBoardTrojanHorseBadProxiable is Initializable, Proxiable { } } - function initialize(bytes calldata) external override { + function initialize(bytes calldata) external { // WATCH OUT: any one could reset storage context after // upgrading the WRB to this implementation. } diff --git a/test/helpers/WitnetRequestBoardTrojanHorseNotUpgradable.sol b/test/helpers/WitnetRequestBoardTrojanHorseNotUpgradable.sol index 92f391995..25ee71d4a 100644 --- a/test/helpers/WitnetRequestBoardTrojanHorseNotUpgradable.sol +++ b/test/helpers/WitnetRequestBoardTrojanHorseNotUpgradable.sol @@ -37,7 +37,7 @@ contract WitnetRequestBoardTrojanHorseNotUpgradable is Initializable, Proxiable } } - function initialize(bytes calldata) external override { + function initialize(bytes calldata) external { // WATCH OUT: any one could reset storage context after // upgrading the WRB to this implementation. } From c01a1bbdbe2200f007fc4c650a43998d62306165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Tue, 10 Jan 2023 16:07:27 +0100 Subject: [PATCH 055/119] chore: bump package version to 0.6.3 --- package.json | 19 +++++++++---------- test/witnet_bytecodes.test.js | 2 +- test/wrb_proxy.test.js | 2 +- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index fa8823690..60b49eeb7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "witnet-solidity-bridge", - "version": "2.0.0", + "version": "0.6.3", "description": "Witnet Solidity Bridge contracts for EVM-compatible chains", "main": "", "scripts": { @@ -42,29 +42,28 @@ ], "license": "MIT", "dependencies": { - "@eth-optimism/solc": "0.7.6-alpha.1", "@openzeppelin/contracts": "4.8.0", "@openzeppelin/contracts-upgradeable": "4.8.0", "ado-contracts": "1.0.0", - "lodash": "^4.17.21" + "lodash": "4.17.21" }, "devDependencies": { - "@openzeppelin/test-helpers": "0.5.5", + "@openzeppelin/test-helpers": "~0.5.16", "@witnet/truffle-flattener-single-experimental": "^0.1.0", "chai": "4.3.6", "custom-error-test-helper": "^1.0.6", "dotenv": "8.2.0", - "eslint": "^8.26.0", - "eslint-config-standard": "^17.0.0", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-n": "^15.3.0", - "eslint-plugin-promise": "^6.1.1", + "eslint": "~8.31.0", + "eslint-config-standard": "~17.0.0", + "eslint-plugin-import": "~2.26.0", + "eslint-plugin-n": "~15.6.0", + "eslint-plugin-promise": "~6.1.1", "eth-gas-reporter": "0.2.25", "js-sha256": "0.9.0", "solhint": "3.3.7", "solidity-coverage": "0.7.16", "solidity-stringutils": "https://github.com/Arachnid/solidity-stringutils/", - "truffle": "5.6.2", + "truffle": "~5.7.2", "truffle-assertions": "0.9.2" } } diff --git a/test/witnet_bytecodes.test.js b/test/witnet_bytecodes.test.js index 76021ebe0..3e500d50f 100644 --- a/test/witnet_bytecodes.test.js +++ b/test/witnet_bytecodes.test.js @@ -79,7 +79,7 @@ contract("WitnetBytecodes", (accounts) => { await expectRevertCustomError( WitnetBytecodes, bytecodes.initialize("0x", { from: firstOwnerAddress }), - "AlreadyInitialized" + "AlreadyUpgraded" ) await expectRevertCustomError( WitnetBytecodes, diff --git a/test/wrb_proxy.test.js b/test/wrb_proxy.test.js index e44470927..5551efe52 100644 --- a/test/wrb_proxy.test.js +++ b/test/wrb_proxy.test.js @@ -141,7 +141,7 @@ contract("Witnet Requests Board Proxy", accounts => { it("fails also if the owner tries to re-initialize current implementation", async () => { await truffleAssert.reverts( wrb.initialize(web3.eth.abi.encodeParameter("address[]", [requestSender]), { from: contractOwner }), - "already initialized" + "already upgraded" ) }) From 0e243dea0eeacfcc9cadd181f82167ea57f5d273 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 26 Jan 2023 12:21:57 +0100 Subject: [PATCH 056/119] refactor: remove WitnetProxy.proxy(): just not yet --- contracts/impls/WitnetProxy.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/contracts/impls/WitnetProxy.sol b/contracts/impls/WitnetProxy.sol index 2ac3b4752..99e4b19cd 100644 --- a/contracts/impls/WitnetProxy.sol +++ b/contracts/impls/WitnetProxy.sol @@ -9,8 +9,6 @@ import "../patterns/Upgradeable.sol"; /// @author The Witnet Foundation. contract WitnetProxy { - address immutable public proxy = address(this); - struct WitnetProxySlot { address implementation; } From d6802ca90d24e8c1e6b584063d109d9ebbe15820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 26 Jan 2023 12:24:12 +0100 Subject: [PATCH 057/119] feat: further abstraction of Clonable pattern --- contracts/apps/WitnetRandomness.sol | 69 ++++++---- contracts/patterns/Clonable.sol | 101 ++++++++------ .../requests/WitnetRequestMalleableBase.sol | 130 +++++++++--------- contracts/requests/WitnetRequestTemplate.sol | 111 +++++++++------ 4 files changed, 231 insertions(+), 180 deletions(-) diff --git a/contracts/apps/WitnetRandomness.sol b/contracts/apps/WitnetRandomness.sol index d206cde09..62c2d9ae0 100644 --- a/contracts/apps/WitnetRandomness.sol +++ b/contracts/apps/WitnetRandomness.sol @@ -249,34 +249,18 @@ contract WitnetRandomness // ================================================================================================================ - // --- 'Clonable' overriden functions ----------------------------------------------------------------------------- + // --- 'Clonable' extension --------------------------------------------------------------------------------------- - /// @notice Re-initialize contract's storage context upon a new upgrade from a proxy. - /// @dev Must fail when trying to upgrade to same logic contract more than once. - function initialize(bytes memory _initData) - public - override - initializer - { - witnetRandomnessRequest = WitnetRequestRandomness( - abi.decode( - _initData, - (address) - ) - ); - } - /// Deploys and returns the address of a minimal proxy clone that replicates contract /// behaviour while using its own EVM storage. /// @dev This function should always provide a new address, no matter how many times /// @dev is actually called from the same `msg.sender`. function clone() - public - virtual override - returns (Clonable _newInstance) + virtual public + wasInitialized + returns (WitnetRandomness) { - _newInstance = super.clone(); - _clone(_newInstance); + return _afterClone(_clone()); } /// Deploys and returns the address of a minimal proxy clone that replicates contract @@ -285,12 +269,33 @@ contract WitnetRandomness /// @dev the clone. Using the same `_salt` multiple time will revert, since /// @dev no contract can be deployed more than once at the same address. function cloneDeterministic(bytes32 _salt) - public - virtual override - returns (Clonable _newInstance) + virtual public + wasInitialized + returns (WitnetRandomness) { - _newInstance = super.cloneDeterministic(_salt); - _clone(_newInstance); + return _afterClone(_cloneDeterministic(_salt)); + } + + /// @notice Tells whether this instance has been initialized. + function initialized() + override + public view + returns (bool) + { + return address(witnetRandomnessRequest) != address(0); + } + + /// @notice Re-initialize contract's storage context upon a new upgrade from a proxy. + /// @dev Must fail when trying to upgrade to same logic contract more than once. + function _initialize(bytes memory _initData) + virtual override internal + { + witnetRandomnessRequest = WitnetRequestRandomness( + abi.decode( + _initData, + (address) + ) + ); } @@ -298,10 +303,14 @@ contract WitnetRandomness // --- INTERNAL FUNCTIONS ----------------------------------------------------------------------------------------- /// @dev Common steps for both deterministic and non-deterministic cloning. - function _clone(Clonable _instance) internal { - address _request = address(witnetRandomnessRequest.clone()); - Ownable(_request).transferOwnership(msg.sender); - _instance.initialize(abi.encode(_request)); + function _afterClone(address _instance) + virtual internal + returns (WitnetRandomness) + { + address _randomnessRequest = address(witnetRandomnessRequest.clone()); + Ownable(_randomnessRequest).transferOwnership(msg.sender); + WitnetRandomness(_instance).initializeClone(abi.encode(_randomnessRequest)); + return WitnetRandomness(_instance); } /// @dev Returns index of the Most Significant Bit of the given number, applying De Bruijn O(1) algorithm. diff --git a/contracts/patterns/Clonable.sol b/contracts/patterns/Clonable.sol index 9805bacb3..9776f7384 100644 --- a/contracts/patterns/Clonable.sol +++ b/contracts/patterns/Clonable.sol @@ -10,9 +10,19 @@ abstract contract Clonable { address immutable internal _SELF = address(this); - event Cloned(address indexed by, Clonable indexed self, Clonable indexed clone); + event Cloned(address indexed by, address indexed self, address indexed clone); - /// Tells whether this contract is a clone of `self()` + modifier onlyDelegateCalls { + require(address(this) != _SELF, "Clonable: not a delegate call"); + _; + } + + modifier wasInitialized { + require(initialized(), "Clonable: not initialized"); + _; + } + + /// @notice Tells whether this contract is a clone of `self()` function cloned() public view returns (bool) @@ -22,38 +32,62 @@ abstract contract Clonable ); } + /// Internal virtual function for initializing contract's storage + function _initialize(bytes memory) virtual internal; + + /// @notice Initializes a cloned instance. + /// @dev Every cloned instance can only get initialized once. + function initializeClone(bytes memory _initData) + external + initializer // => ensure a cloned instance can only be initialized once + onlyDelegateCalls // => this method can only be called upon cloned instances + { + _initialize(_initData); + } + + /// @notice Tells whether this instance has been initialized. + function initialized() virtual public view returns (bool); + + /// @notice Contract address to which clones will be re-directed. + function self() virtual public view returns (address) { + return _SELF; + } + /// Deploys and returns the address of a minimal proxy clone that replicates contract /// behaviour while using its own EVM storage. /// @dev This function should always provide a new address, no matter how many times /// @dev is actually called from the same `msg.sender`. /// @dev See https://eips.ethereum.org/EIPS/eip-1167. /// @dev See https://blog.openzeppelin.com/deep-dive-into-the-minimal-proxy-contract/. - function clone() - public virtual - returns (Clonable _instance) + function _clone() + internal + returns (address _instance) + { + bytes memory ptr = _cloneBytecode(); + assembly { + // CREATE new instance: + _instance := create(0, ptr, 0x37) + } + require(_instance != address(0), "Clonable: CREATE failed"); + emit Cloned(msg.sender, self(), _instance); + } + + /// @notice Returns minimal proxy's deploy bytecode. + function _cloneBytecode() + virtual internal + returns (bytes memory ptr) { address _base = self(); assembly { // ptr to free mem: - let ptr := mload(0x40) + ptr := mload(0x40) // begin minimal proxy construction bytecode: mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) // make minimal proxy delegate all calls to `self()`: mstore(add(ptr, 0x14), shl(0x60, _base)) // end minimal proxy construction bytecode: mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) - // CREATE new instance: - _instance := create(0, ptr, 0x37) - } - require( - address(_instance) != address(0), - "Clonable: CREATE failed" - ); - emit Cloned( - msg.sender, - Clonable(self()), - _instance - ); + } } /// Deploys and returns the address of a minimal proxy clone that replicates contract @@ -63,35 +97,16 @@ abstract contract Clonable /// @dev no contract can be deployed more than once at the same address. /// @dev See https://eips.ethereum.org/EIPS/eip-1167. /// @dev See https://blog.openzeppelin.com/deep-dive-into-the-minimal-proxy-contract/. - function cloneDeterministic(bytes32 _salt) - public virtual - returns (Clonable _instance) + function _cloneDeterministic(bytes32 _salt) + internal + returns (address _instance) { - address _base = self(); + bytes memory ptr = _cloneBytecode(); assembly { - // ptr to free mem: - let ptr := mload(0x40) - mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) - // make minimal proxy delegate all calls to `self()`: - mstore(add(ptr, 0x14), shl(0x60, _base)) - // end minimal proxy construction bytecode: - mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) // CREATE2 new instance: _instance := create2(0, ptr, 0x37, _salt) } - require( - address(_instance) != address(0), - "Clonable: CREATE2 failed" - ); - emit Cloned(msg.sender, Clonable(self()), _instance); - } - - /// @notice Re-initialize contract's storage context upon a new upgrade from a proxy. - /// @dev Must fail when trying to upgrade to same logic contract more than once. - function initialize(bytes memory) virtual external; - - /// @notice Contract address to which clones will be re-directed. - function self() virtual public view returns (address) { - return _SELF; + require(_instance != address(0), "Clonable: CREATE2 failed"); + emit Cloned(msg.sender, self(), _instance); } } diff --git a/contracts/requests/WitnetRequestMalleableBase.sol b/contracts/requests/WitnetRequestMalleableBase.sol index 133ede894..7026c6223 100644 --- a/contracts/requests/WitnetRequestMalleableBase.sol +++ b/contracts/requests/WitnetRequestMalleableBase.sol @@ -57,12 +57,12 @@ abstract contract WitnetRequestMalleableBase /// Returns current Witnet Data Request bytecode, encoded using Protocol Buffers. function bytecode() external view override returns (bytes memory) { - return _request().bytecode; + return __storage().bytecode; } /// Returns SHA256 hash of current Witnet Data Request bytecode. function hash() external view override returns (bytes32) { - return _request().hash; + return __storage().hash; } /// Sets amount of nanowits that a witness solving the request will be required to collateralize in the commitment transaction. @@ -71,7 +71,7 @@ abstract contract WitnetRequestMalleableBase virtual onlyOwner { - WitnetRequestWitnessingParams storage _params = _request().params; + WitnetRequestWitnessingParams storage _params = __storage().params; _params.witnessingCollateral = _witnessingCollateral; _malleateBytecode( _params.numWitnesses, @@ -91,7 +91,7 @@ abstract contract WitnetRequestMalleableBase virtual onlyOwner { - WitnetRequestWitnessingParams storage _params = _request().params; + WitnetRequestWitnessingParams storage _params = __storage().params; _params.witnessingReward = _witnessingReward; _params.witnessingUnitaryFee = _witnessingUnitaryFee; _malleateBytecode( @@ -112,7 +112,7 @@ abstract contract WitnetRequestMalleableBase virtual onlyOwner { - WitnetRequestWitnessingParams storage _params = _request().params; + WitnetRequestWitnessingParams storage _params = __storage().params; _params.numWitnesses = _numWitnesses; _params.minWitnessingConsensus = _minWitnessingConsensus; _malleateBytecode( @@ -130,7 +130,7 @@ abstract contract WitnetRequestMalleableBase external view returns (bytes memory) { - return _request().template; + return __storage().template; } /// Returns total amount of nanowits that witnessing nodes will need to collateralize all together. @@ -138,7 +138,7 @@ abstract contract WitnetRequestMalleableBase external view returns (uint128) { - WitnetRequestWitnessingParams storage _params = _request().params; + WitnetRequestWitnessingParams storage _params = __storage().params; return _params.numWitnesses * _params.witnessingCollateral; } @@ -147,7 +147,7 @@ abstract contract WitnetRequestMalleableBase external view returns (uint128) { - WitnetRequestWitnessingParams storage _params = _request().params; + WitnetRequestWitnessingParams storage _params = __storage().params; return _params.numWitnesses * (2 * _params.witnessingUnitaryFee + _params.witnessingReward); } @@ -156,35 +156,24 @@ abstract contract WitnetRequestMalleableBase external view returns (WitnetRequestWitnessingParams memory) { - return _request().params; + return __storage().params; } // ================================================================================================================ - // --- 'Clonable' overriden functions ----------------------------------------------------------------------------- - - /// @notice Re-initialize contract's storage context upon a new upgrade from a proxy. - /// @dev Must fail when trying to upgrade to same logic contract more than once. - function initialize(bytes memory _template) - public - override - initializer - { - _initialize(_template); - } + // --- 'Clonable' extension --------------------------------------------------------------------------------------- /// Deploys and returns the address of a minimal proxy clone that replicates contract /// behaviour while using its own EVM storage. /// @dev This function should always provide a new address, no matter how many times /// @dev is actually called from the same `msg.sender`. + /// @dev Ownership of new clone is transferred to the caller. function clone() - public - virtual override - returns (Clonable _instance) + virtual public + wasInitialized + returns (WitnetRequestMalleableBase) { - _instance = super.clone(); - _instance.initialize(_request().template); - Ownable(address(_instance)).transferOwnership(msg.sender); + return _afterCloning(_clone()); } /// Deploys and returns the address of a minimal proxy clone that replicates contract @@ -192,14 +181,48 @@ abstract contract WitnetRequestMalleableBase /// @dev This function uses the CREATE2 opcode and a `_salt` to deterministically deploy /// @dev the clone. Using the same `_salt` multiple time will revert, since /// @dev no contract can be deployed more than once at the same address. + /// @dev Ownership of new clone is transferred to the caller. function cloneDeterministic(bytes32 _salt) - public - virtual override - returns (Clonable _instance) + virtual public + wasInitialized + returns (WitnetRequestMalleableBase) + { + return _afterCloning(_cloneDeterministic(_salt)); + } + + /// @notice Tells whether this instance has been initialized. + function initialized() + override + public view + returns (bool) + { + return __storage().template.length > 0; + } + + /// @dev Initializes witnessing params and template bytecode. + function _initialize(bytes memory _template) + virtual override internal + initializer { - _instance = super.cloneDeterministic(_salt); - _instance.initialize(_request().template); - Ownable(address(_instance)).transferOwnership(msg.sender); + _transferOwnership(_msgSender()); + + assert(_template.length > 0); + __storage().template = _template; + + WitnetRequestWitnessingParams storage _params = __storage().params; + _params.numWitnesses = 2; + _params.minWitnessingConsensus = 51; + _params.witnessingCollateral = 10 ** 9; // 1 WIT + _params.witnessingReward = 5 * 10 ** 5; // 0.5 milliWITs + _params.witnessingUnitaryFee = 25 * 10 ** 4; // 0.25 milliWITs + + _malleateBytecode( + _params.numWitnesses, + _params.minWitnessingConsensus, + _params.witnessingCollateral, + _params.witnessingReward, + _params.witnessingUnitaryFee + ); } @@ -212,7 +235,7 @@ abstract contract WitnetRequestMalleableBase virtual override returns (address) { - return _request().owner; + return __storage().owner; } /// @dev Transfers ownership of the contract to a new account (`newOwner`). @@ -220,8 +243,8 @@ abstract contract WitnetRequestMalleableBase internal virtual override { - address oldOwner = _request().owner; - _request().owner = newOwner; + address oldOwner = __storage().owner; + __storage().owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } @@ -246,30 +269,13 @@ abstract contract WitnetRequestMalleableBase // ================================================================================================================ // --- INTERNAL FUNCTIONS ----------------------------------------------------------------------------------------- - /// @dev Initializes witnessing params and template bytecode. - function _initialize(bytes memory _template) - internal - virtual + function _afterCloning(address _newInstance) + virtual internal + returns (WitnetRequestMalleableBase) { - _transferOwnership(_msgSender()); - - assert(_template.length > 0); - _request().template = _template; - - WitnetRequestWitnessingParams storage _params = _request().params; - _params.numWitnesses = 2; - _params.minWitnessingConsensus = 51; - _params.witnessingCollateral = 10 ** 9; // 1 WIT - _params.witnessingReward = 5 * 10 ** 5; // 0.5 milliWITs - _params.witnessingUnitaryFee = 25 * 10 ** 4; // 0.25 milliWITs - - _malleateBytecode( - _params.numWitnesses, - _params.minWitnessingConsensus, - _params.witnessingCollateral, - _params.witnessingReward, - _params.witnessingUnitaryFee - ); + WitnetRequestMalleableBase(_newInstance).initializeClone(__storage().template); + Ownable(address(_newInstance)).transferOwnership(msg.sender); + return WitnetRequestMalleableBase(_newInstance); } /// @dev Serializes new `bytecode` by combining immutable template with given parameters. @@ -300,15 +306,15 @@ abstract contract WitnetRequestMalleableBase "WitnetRequestMalleableBase: witnessing collateral below 1 WIT" ); - _request().bytecode = abi.encodePacked( - _request().template, + __storage().bytecode = abi.encodePacked( + __storage().template, _uint64varint(bytes1(0x10), _witnessingReward), _uint8varint(bytes1(0x18), _numWitnesses), _uint64varint(0x20, _witnessingUnitaryFee), _uint8varint(0x28, _minWitnessingConsensus), _uint64varint(0x30, _witnessingCollateral) ); - _request().hash = _request().bytecode.hash(); + __storage().hash = __storage().bytecode.hash(); emit WitnessingParamsChanged( msg.sender, _numWitnesses, @@ -320,7 +326,7 @@ abstract contract WitnetRequestMalleableBase } /// @dev Returns pointer to storage slot where State struct is located. - function _request() + function __storage() internal pure virtual returns (WitnetRequestMalleableBaseContext storage _ptr) diff --git a/contracts/requests/WitnetRequestTemplate.sol b/contracts/requests/WitnetRequestTemplate.sol index 896068650..59b366b75 100644 --- a/contracts/requests/WitnetRequestTemplate.sol +++ b/contracts/requests/WitnetRequestTemplate.sol @@ -29,6 +29,9 @@ abstract contract WitnetRequestTemplate /// @notice SHA-256 hash of the Witnet Data Request bytecode. bytes32 public override hash; + /// @notice Unique id of last request attempt. + uint256 public lastId; + /// @notice Reference to Witnet Data Requests Bytecode Registry IWitnetBytecodes immutable public registry; @@ -38,17 +41,17 @@ abstract contract WitnetRequestTemplate /// @notice Result max size or rank (if variable type). uint16 immutable public resultDataMaxSize; - /// @notice Array of source hashes encoded as bytes. - bytes /*immutable*/ public template; - /// @notice Array of string arguments passed upon initialization. - string[][] public args; + string[][] public args; /// @notice Radon Retrieval hash. bytes32 public retrievalHash; /// @notice Radon SLA hash. bytes32 public slaHash; + + /// @notice Array of source hashes encoded as bytes. + bytes /*immutable*/ internal __sources; /// @notice Aggregator reducer hash. bytes32 immutable internal _AGGREGATOR_HASH; @@ -56,18 +59,8 @@ abstract contract WitnetRequestTemplate /// @notice Tally reducer hash. bytes32 immutable internal _TALLY_HASH; - /// @notice Unique id of last request attempt. - uint256 public postId; - - modifier initialized { - if (retrievalHash == bytes32(0)) { - revert("WitnetRequestTemplate: not initialized"); - } - _; - } - - modifier notPending { - require(!pending(), "WitnetRequestTemplate: pending"); + modifier notUpdating { + require(!updating(), "WitnetRequestTemplate: updating"); _; } @@ -104,7 +97,7 @@ abstract contract WitnetRequestTemplate "WitnetRequestTemplate: mismatching sources" ); } - template = abi.encode(_sources); + __sources = abi.encode(_sources); } { assert(_aggregator != bytes32(0)); @@ -116,13 +109,14 @@ abstract contract WitnetRequestTemplate } } + /// ======================================================================= /// --- WitnetRequestTemplate interface ----------------------------------- receive () virtual external payable {} - function _read(WitnetCBOR.CBOR memory) virtual internal view returns (bytes memory); - + function _parseWitnetResult(WitnetCBOR.CBOR memory) virtual internal view returns (bytes memory); + function getRadonAggregator() external view returns (WitnetV2.RadonReducer memory) @@ -132,7 +126,7 @@ abstract contract WitnetRequestTemplate function getRadonTally() external view - initialized + wasInitialized returns (WitnetV2.RadonReducer memory) { return registry.lookupRadonRetrievalTally(retrievalHash); @@ -140,7 +134,7 @@ abstract contract WitnetRequestTemplate function getRadonSLA() external view - initialized + wasInitialized returns (WitnetV2.RadonSLA memory) { return registry.lookupRadonSLA(slaHash); @@ -150,70 +144,85 @@ abstract contract WitnetRequestTemplate external view returns (bytes32[] memory) { - return abi.decode(template, (bytes32[])); + return abi.decode(__sources, (bytes32[])); } - function post() + function update() virtual external payable + wasInitialized returns (uint256 _usedFunds) { if ( - postId == 0 + lastId == 0 || ( - _witnetCheckResultAvailability(postId) - && witnet.isError(_witnetReadResult(postId)) + _witnetCheckResultAvailability(lastId) + && witnet.isError(_witnetReadResult(lastId)) ) ) { - (postId, _usedFunds) = _witnetPostRequest(this); + (lastId, _usedFunds) = _witnetPostRequest(this); if (_usedFunds < msg.value) { payable(msg.sender).transfer(msg.value - _usedFunds); } } } - function pending() + function updating() virtual public view returns (bool) { return ( - postId == 0 - || _witnetCheckResultAvailability(postId) + lastId == 0 + || _witnetCheckResultAvailability(lastId) ); } function read() virtual external view - notPending + notUpdating returns (bool, bytes memory) { - require(!pending(), "WitnetRequestTemplate: pending"); - Witnet.Result memory _result = _witnetReadResult(postId); - return (_result.success, _read(_result.value)); + Witnet.Result memory _result = _witnetReadResult(lastId); + return ( + _result.success, + _parseWitnetResult(_result.value) + ); } // ================================================================================================================ - // --- Overriden 'Clonable' functions ----------------------------------------------------------------------------- + // --- 'Clonable' extension --------------------------------------------------------------------------------------- + + function clone(bytes memory _initData) + virtual public + returns (WitnetRequestTemplate) + { + return _afterCloning(_clone(), _initData); + } - /// @notice Re-initialize contract's storage context upon a new upgrade from a proxy. - /// @dev Must fail when trying to upgrade to same logic contract more than once. - function initialize(bytes memory _initData) - public + function cloneDeterministic(bytes32 _salt, bytes memory _initData) + virtual public + returns (WitnetRequestTemplate) + { + return _afterCloning(_cloneDeterministic(_salt), _initData); + } + + /// @notice Tells whether this instance has been initialized. + function initialized() override - initializer + public view + returns (bool) { - _initialize(_initData); + return retrievalHash != 0x0; } /// @dev Internal virtual method containing actual initialization logic for every new clone. function _initialize(bytes memory _initData) - internal - virtual + virtual override internal { - bytes32[] memory _sources = abi.decode(WitnetRequestTemplate(payable(self())).template(), (bytes32[])); + bytes32[] memory _sources = WitnetRequestTemplate(payable(self())).sources(); InitData memory _init = abi.decode(_initData, (InitData)); args = _init.args; bytes32 _retrievalHash = registry.verifyRadonRetrieval( @@ -228,6 +237,18 @@ abstract contract WitnetRequestTemplate hash = sha256(bytecode); retrievalHash = _retrievalHash; slaHash = _init.slaHash; - template = abi.encode(_sources); + __sources = abi.encode(_sources); + } + + + /// =============================================================================================================== + /// --- Internal methods ------------------------------------------------------------------------------------------ + + function _afterCloning(address _newInstance, bytes memory _initData) + virtual internal + returns (WitnetRequestTemplate) + { + WitnetRequestTemplate(payable(_newInstance)).initializeClone(_initData); + return WitnetRequestTemplate(payable(_newInstance)); } } \ No newline at end of file From 0cabbd65cb6109036cd34fcaec7f71c2813dc5b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 26 Jan 2023 14:06:07 +0100 Subject: [PATCH 058/119] feat(requests): verify aggregator and tally exist upon deployment of new template --- contracts/requests/WitnetRequestTemplate.sol | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/contracts/requests/WitnetRequestTemplate.sol b/contracts/requests/WitnetRequestTemplate.sol index 59b366b75..de7d74166 100644 --- a/contracts/requests/WitnetRequestTemplate.sol +++ b/contracts/requests/WitnetRequestTemplate.sol @@ -100,11 +100,17 @@ abstract contract WitnetRequestTemplate __sources = abi.encode(_sources); } { - assert(_aggregator != bytes32(0)); + require( + uint8(_registry.lookupRadonReducer(_aggregator).opcode) != 0, + "WitnetRequestTemplate: unknown aggregator" + ); _AGGREGATOR_HASH = _aggregator; } { - assert(_tally != bytes32(0)); + require( + uint8(_registry.lookupRadonReducer(_tally).opcode) != 0, + "WitnetRequestTemplate: unknown tally" + ); _TALLY_HASH = _tally; } } @@ -121,15 +127,14 @@ abstract contract WitnetRequestTemplate external view returns (WitnetV2.RadonReducer memory) { - return registry.lookupRadonRetrievalAggregator(retrievalHash); + return registry.lookupRadonRetrievalAggregator(_AGGREGATOR_HASH); } function getRadonTally() external view - wasInitialized returns (WitnetV2.RadonReducer memory) { - return registry.lookupRadonRetrievalTally(retrievalHash); + return registry.lookupRadonRetrievalTally(_TALLY_HASH); } function getRadonSLA() From bfa5ce97364ebec966b68d752385682673f7bfba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 26 Jan 2023 14:20:01 +0100 Subject: [PATCH 059/119] fix(bytecodes): looking up for unkown sources, radon reducers or slas must revert --- contracts/impls/bytecodes/WitnetBytecodes.sol | 14 ++++++++++---- contracts/interfaces/V2/IWitnetBytecodes.sol | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol index 30e5fa18e..5e5158ba1 100644 --- a/contracts/impls/bytecodes/WitnetBytecodes.sol +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -299,9 +299,12 @@ contract WitnetBytecodes function lookupRadonReducer(bytes32 _hash) external view override - returns (WitnetV2.RadonReducer memory) + returns (WitnetV2.RadonReducer memory _reducer) { - return __database().reducers[_hash]; + _reducer = __database().reducers[_hash]; + if (uint8(_reducer.opcode) == 0) { + revert IWitnetBytecodes.UnknownRadonReducer(_hash); + } } function lookupRadonRetrievalAggregator(bytes32 _drRetrievalHash) @@ -359,9 +362,12 @@ contract WitnetBytecodes function lookupRadonSLA(bytes32 _drSlaHash) external view override - returns (WitnetV2.RadonSLA memory) + returns (WitnetV2.RadonSLA memory _sla) { - return __database().slas[_drSlaHash]; + _sla = __database().slas[_drSlaHash]; + if (_sla.numWitnesses == 0) { + revert IWitnetBytecodes.UnknownRadonSLA(_drSlaHash); + } } function lookupRadonSLAReward(bytes32 _drSlaHash) diff --git a/contracts/interfaces/V2/IWitnetBytecodes.sol b/contracts/interfaces/V2/IWitnetBytecodes.sol index 09d6e9644..ac29e3982 100644 --- a/contracts/interfaces/V2/IWitnetBytecodes.sol +++ b/contracts/interfaces/V2/IWitnetBytecodes.sol @@ -7,6 +7,7 @@ import "../../libs/WitnetV2.sol"; interface IWitnetBytecodes { error UnknownDataSource(bytes32 hash); + error UnknownRadonReducer(bytes32 hash); error UnknownRadonRetrieval(bytes32 hash); error UnknownRadonSLA(bytes32 hash); From 0ef539b717ab0c5ac4081c70f70853bf2e867f44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 26 Jan 2023 16:46:50 +0100 Subject: [PATCH 060/119] chore(libs): avoid WitnetRequestTemplates having to link WitnetLib --- contracts/libs/WitnetLib.sol | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/contracts/libs/WitnetLib.sol b/contracts/libs/WitnetLib.sol index 86fe7faa5..e06adcee6 100644 --- a/contracts/libs/WitnetLib.sol +++ b/contracts/libs/WitnetLib.sol @@ -15,9 +15,21 @@ library WitnetLib { using WitnetCBOR for WitnetCBOR.CBOR[]; using WitnetLib for bytes; + /// =============================================================================================================== /// --- WitnetLib internal methods -------------------------------------------------------------------------------- + /// @notice Decode raw CBOR bytes into a Witnet.Result instance. + /// @param bytecode Raw bytes representing a CBOR-encoded value. + /// @return A `Witnet.Result` instance. + function resultFromCborBytes(bytes memory bytecode) + public pure + returns (Witnet.Result memory) + { + WitnetCBOR.CBOR memory cborValue = WitnetCBOR.fromBytes(bytecode); + return _resultFromCborValue(cborValue); + } + function toAddress(bytes memory _value) internal pure returns (address) { return address(toBytes20(_value)); } @@ -565,15 +577,4 @@ library WitnetLib { return result.value.readUintArray(); } - /// @notice Decode raw CBOR bytes into a Witnet.Result instance. - /// @param bytecode Raw bytes representing a CBOR-encoded value. - /// @return A `Witnet.Result` instance. - function resultFromCborBytes(bytes memory bytecode) - public pure - returns (Witnet.Result memory) - { - WitnetCBOR.CBOR memory cborValue = WitnetCBOR.fromBytes(bytecode); - return _resultFromCborValue(cborValue); - } - } \ No newline at end of file From dffe45bbe0dda4970b2b8f5fd4a836f92c0c55dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 26 Jan 2023 16:47:52 +0100 Subject: [PATCH 061/119] feat(requests): let WitnetRequestTemplate return last valid result, drtxhash and timestamp --- contracts/libs/WitnetLib.sol | 2 +- contracts/requests/WitnetRequestTemplate.sol | 83 ++++++++++++-------- 2 files changed, 53 insertions(+), 32 deletions(-) diff --git a/contracts/libs/WitnetLib.sol b/contracts/libs/WitnetLib.sol index e06adcee6..3b40607d2 100644 --- a/contracts/libs/WitnetLib.sol +++ b/contracts/libs/WitnetLib.sol @@ -23,7 +23,7 @@ library WitnetLib { /// @param bytecode Raw bytes representing a CBOR-encoded value. /// @return A `Witnet.Result` instance. function resultFromCborBytes(bytes memory bytecode) - public pure + internal pure returns (Witnet.Result memory) { WitnetCBOR.CBOR memory cborValue = WitnetCBOR.fromBytes(bytecode); diff --git a/contracts/requests/WitnetRequestTemplate.sol b/contracts/requests/WitnetRequestTemplate.sol index de7d74166..6ee24465d 100644 --- a/contracts/requests/WitnetRequestTemplate.sol +++ b/contracts/requests/WitnetRequestTemplate.sol @@ -8,6 +8,7 @@ import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; import "../UsingWitnet.sol"; import "../interfaces/V2/IWitnetBytecodes.sol"; import "../interfaces/IWitnetRequest.sol"; +import "../libs/WitnetLib.sol"; import "../patterns/Clonable.sol"; abstract contract WitnetRequestTemplate @@ -29,7 +30,10 @@ abstract contract WitnetRequestTemplate /// @notice SHA-256 hash of the Witnet Data Request bytecode. bytes32 public override hash; - /// @notice Unique id of last request attempt. + /// @notice Unique id of last update attempt. + uint256 public lastAttemptId; + + /// @notice Unique id of last update that got solved successfully. uint256 public lastId; /// @notice Reference to Witnet Data Requests Bytecode Registry @@ -100,17 +104,13 @@ abstract contract WitnetRequestTemplate __sources = abi.encode(_sources); } { - require( - uint8(_registry.lookupRadonReducer(_aggregator).opcode) != 0, - "WitnetRequestTemplate: unknown aggregator" - ); + // revert if the aggregator reducer is unknown + _registry.lookupRadonReducer(_aggregator); _AGGREGATOR_HASH = _aggregator; } { - require( - uint8(_registry.lookupRadonReducer(_tally).opcode) != 0, - "WitnetRequestTemplate: unknown tally" - ); + // revert if the tally reducer is unknown + _registry.lookupRadonReducer(_tally); _TALLY_HASH = _tally; } } @@ -158,17 +158,20 @@ abstract contract WitnetRequestTemplate wasInitialized returns (uint256 _usedFunds) { - if ( - lastId == 0 - || ( - _witnetCheckResultAvailability(lastId) - && witnet.isError(_witnetReadResult(lastId)) - ) - ) { - (lastId, _usedFunds) = _witnetPostRequest(this); - if (_usedFunds < msg.value) { - payable(msg.sender).transfer(msg.value - _usedFunds); + uint _lastAttempt = lastAttemptId; + if (updating()) { + _usedFunds = _witnetUpgradeReward(_lastAttempt); + } else { + if ( + _lastAttempt > 0 + && !witnet.isError(_witnetReadResult(_lastAttempt)) + ) { + lastId = _lastAttempt; } + (lastAttemptId, _usedFunds) = _witnetPostRequest(this); + } + if (_usedFunds < msg.value) { + payable(msg.sender).transfer(msg.value - _usedFunds); } } @@ -177,23 +180,41 @@ abstract contract WitnetRequestTemplate public view returns (bool) { + uint _lastAttempt = lastAttemptId; return ( - lastId == 0 - || _witnetCheckResultAvailability(lastId) + _lastAttempt > 0 + && !_witnetCheckResultAvailability(_lastAttempt) ); } - function read() - virtual - external view - notUpdating - returns (bool, bytes memory) + function lastValue() + virtual external view + returns ( + bytes memory _value, + bytes32 _witnetDrTxHash, + uint256 _witnetTimestamp + ) { - Witnet.Result memory _result = _witnetReadResult(lastId); - return ( - _result.success, - _parseWitnetResult(_result.value) - ); + Witnet.Response memory _response; + Witnet.Result memory _result; + if ( + !updating() + && lastAttemptId > 0 + ) { + _response = witnet.readResponse(lastAttemptId); + _result = WitnetLib.resultFromCborBytes(_response.cborBytes); + } + if (WitnetLib.failed(_result)) { + if (lastId > 0) { + _response = witnet.readResponse(lastId); + _result = WitnetLib.resultFromCborBytes(_response.cborBytes); + } else { + revert("WitnetRequestTemplate: no value yet"); + } + } + _value = _parseWitnetResult(_result.value); + _witnetDrTxHash = _response.drTxHash; + _witnetTimestamp = _response.timestamp; } From b65b93b7f8f8c7e09964b4b5ad6a83762265408d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 26 Jan 2023 18:12:07 +0100 Subject: [PATCH 062/119] fix(proxy): allow transfers as to enable usingwitnet contracts to be proxified --- contracts/impls/WitnetProxy.sol | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/contracts/impls/WitnetProxy.sol b/contracts/impls/WitnetProxy.sol index 99e4b19cd..281958976 100644 --- a/contracts/impls/WitnetProxy.sol +++ b/contracts/impls/WitnetProxy.sol @@ -19,9 +19,7 @@ contract WitnetProxy { /// Constructor with no params as to ease eventual support of Singleton pattern (i.e. ERC-2470). constructor () {} - receive() virtual external payable { - revert("WitnetProxy: no transfers accepted"); - } + receive() virtual external payable {} /// Payable fallback accepts delegating calls to payable functions. fallback() external payable { /* solhint-disable no-complex-fallback */ From 9f625bf1241b9011ce72f5e46cc3cf143a09b000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Tue, 31 Jan 2023 19:09:06 +0100 Subject: [PATCH 063/119] chore: re-deploy WitnetBytecodes proxy and impl in polygon goerli --- contracts/requests/WitnetRequestTemplate.sol | 12 +- migrations/witnet.addresses.json | 660 +++++++++---------- 2 files changed, 332 insertions(+), 340 deletions(-) diff --git a/contracts/requests/WitnetRequestTemplate.sol b/contracts/requests/WitnetRequestTemplate.sol index 6ee24465d..6cc7bc7da 100644 --- a/contracts/requests/WitnetRequestTemplate.sol +++ b/contracts/requests/WitnetRequestTemplate.sol @@ -190,9 +190,9 @@ abstract contract WitnetRequestTemplate function lastValue() virtual external view returns ( - bytes memory _value, - bytes32 _witnetDrTxHash, - uint256 _witnetTimestamp + bytes memory value, + bytes32 witnetDrTxHash, + uint256 witnetTimestamp ) { Witnet.Response memory _response; @@ -212,9 +212,9 @@ abstract contract WitnetRequestTemplate revert("WitnetRequestTemplate: no value yet"); } } - _value = _parseWitnetResult(_result.value); - _witnetDrTxHash = _response.drTxHash; - _witnetTimestamp = _response.timestamp; + value = _parseWitnetResult(_result.value); + witnetDrTxHash = _response.drTxHash; + witnetTimestamp = _response.timestamp; } diff --git a/migrations/witnet.addresses.json b/migrations/witnet.addresses.json index 11e11dcdd..ebcb4a513 100644 --- a/migrations/witnet.addresses.json +++ b/migrations/witnet.addresses.json @@ -1,360 +1,352 @@ { - "default": { - "ethereum.goerli": { - "WitnetLib": "0xd80ffbc599ad5f2Bb6b33AEaAC5E4Fac3cfaf0D1", - "WitnetPriceRouter": "0x1cF3Aa9DBF4880d797945726B94B9d29164211BE", - "WitnetRandomness": "0x6Eb87EcCe6218Cd0e97299331D2aa5d2e53da5cD", - "WitnetRequestBoard": "0xb58D05247d16b3F1BD6B59c52f7f61fFef02BeC8" + "default": { + "ethereum.goerli": { + "WitnetLib": "0xd80ffbc599ad5f2Bb6b33AEaAC5E4Fac3cfaf0D1", + "WitnetPriceRouter": "0x1cF3Aa9DBF4880d797945726B94B9d29164211BE", + "WitnetRandomness": "0x6Eb87EcCe6218Cd0e97299331D2aa5d2e53da5cD", + "WitnetRequestBoard": "0xb58D05247d16b3F1BD6B59c52f7f61fFef02BeC8" + }, + "ethereum.rinkeby": { + "WitnetLib": "0xF4fE7fA5c5e6CCa213377F10fD98b6b0DC00cd70", + "WitnetPriceRouter": "0xa50b17C2fc373c247C3b603f83df6A7800cB0DC9", + "WitnetRandomness": "0x50F742Fbf9a445AE6B2136F5987414A4c5aeE921", + "WitnetRequestBoard": "0x6cE42a35C61ccfb42907EEE57eDF14Bb69C7fEF4" + }, + "ethereum.mainnet": { + "WitnetLib": "0xaD18Fd3CC724A11c2B0D8cc7f1B108d8A3388416", + "WitnetPriceRouter": "0x83A757eAe821Ad7B520D9A74952337138A80b2AF", + "WitnetRandomness": "0x894907c7Ab64C1092620B5c8Ba039BB6E611eba8", + "WitnetRequestBoard": "0x9E4fae1c7ac543a81E4E2a5486a0dDaad8194bdA" + } }, - "ethereum.rinkeby": { - "WitnetLib": "0xF4fE7fA5c5e6CCa213377F10fD98b6b0DC00cd70", - "WitnetPriceRouter": "0xa50b17C2fc373c247C3b603f83df6A7800cB0DC9", - "WitnetRandomness": "0x50F742Fbf9a445AE6B2136F5987414A4c5aeE921", - "WitnetRequestBoard": "0x6cE42a35C61ccfb42907EEE57eDF14Bb69C7fEF4" + "arbitrum": { + "arbitrum.goerli": { + "WitnetParserLib": "0x7d0a71D7D797b7b2637F682e4cB47f66602e59F3", + "WitnetPriceRouter": "0xAafb2D27E2E0f83dcE501a2879aaD710ec377403", + "WitnetRandomness": "0xB0C5d40A7658b2ab28360aFa6eB5bAeb2fFe86c3", + "WitnetRequestBoard": "0xa6a6b2cBE9Ce5B8fF94729F1eC402efc5042AbBF" + } }, - "ethereum.mainnet": { - "WitnetLib": "0xaD18Fd3CC724A11c2B0D8cc7f1B108d8A3388416", - "WitnetPriceRouter": "0x83A757eAe821Ad7B520D9A74952337138A80b2AF", - "WitnetRandomness": "0x894907c7Ab64C1092620B5c8Ba039BB6E611eba8", - "WitnetRequestBoard": "0x9E4fae1c7ac543a81E4E2a5486a0dDaad8194bdA" - } - }, - "arbitrum": { - "arbitrum.goerli": { - "WitnetParserLib": "0x7d0a71D7D797b7b2637F682e4cB47f66602e59F3", - "WitnetPriceRouter": "0xAafb2D27E2E0f83dcE501a2879aaD710ec377403", - "WitnetRandomness": "0xB0C5d40A7658b2ab28360aFa6eB5bAeb2fFe86c3", - "WitnetRequestBoard": "0xa6a6b2cBE9Ce5B8fF94729F1eC402efc5042AbBF" - } - }, - "avalanche": { - "avalanche.testnet": { - "WitnetLib": "0x62B1BB81E57E9c0E22A0dc6FdeE456146a7D7083", - "WitnetPriceRouter": "0x99Af0CF37d1C6b9Bdfe33cc0A89C00D97D3c42F4", - "WitnetRandomness": "0xD47fc24C99fD94f33bD2f33FE373b1447bB10724", - "WitnetRequestBoard": "0xD9465D38f50f364b3263Cb219e58d4dB2D584530" + "avalanche": { + "avalanche.testnet": { + "WitnetLib": "0x62B1BB81E57E9c0E22A0dc6FdeE456146a7D7083", + "WitnetPriceRouter": "0x99Af0CF37d1C6b9Bdfe33cc0A89C00D97D3c42F4", + "WitnetRandomness": "0xD47fc24C99fD94f33bD2f33FE373b1447bB10724", + "WitnetRequestBoard": "0xD9465D38f50f364b3263Cb219e58d4dB2D584530" + }, + "avalanche.mainnet": { + "WitnetLib": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", + "WitnetPriceRouter": "0xBaaF31F4AAc5ab5334b6E239a83bf4E855C55ea7", + "WitnetRandomness": "0xa4A73a2A32320282a4d7dDe6a7467AeFA3B7950F", + "WitnetRequestBoard": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079" + } }, - "avalanche.mainnet": { - "WitnetLib": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", - "WitnetPriceRouter": "0xBaaF31F4AAc5ab5334b6E239a83bf4E855C55ea7", - "WitnetRandomness": "0xa4A73a2A32320282a4d7dDe6a7467AeFA3B7950F", - "WitnetRequestBoard": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079" - } - }, - "boba": { - "boba.moonbeam.bobabase": { - "WitnetLib": "0x02Cd4089679EAA9431a88170fd784e7dE78A2425", - "WitnetPriceRouter": "0xD9465D38f50f364b3263Cb219e58d4dB2D584530", - "WitnetRandomness": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", - "WitnetRequestBoard": "0x0985FDe9f424fe4f5AC516F66bAf5591e18aCBEb" + "boba": { + "boba.moonbeam.bobabase": { + "WitnetLib": "0x02Cd4089679EAA9431a88170fd784e7dE78A2425", + "WitnetPriceRouter": "0xD9465D38f50f364b3263Cb219e58d4dB2D584530", + "WitnetRandomness": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", + "WitnetRequestBoard": "0x0985FDe9f424fe4f5AC516F66bAf5591e18aCBEb" + }, + "boba.ethereum.goerli": { + "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", + "WitnetPriceRouter": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", + "WitnetRandomness": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", + "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + }, + "boba.ethereum.rinkeby": { + "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", + "WitnetPriceRouter": "0x36928Aeedaaf7D85bcA39aDfB2A39ec529ce221a", + "WitnetRandomness": "0xeAcAcC48eDD5221EC7182E1789d8bFa9dF801dFF", + "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + }, + "boba.ethereum.mainnet": { + "WitnetLib": "0x6473063EBEabC0606A4159b7d9F79BB306ED0D2A", + "WitnetPriceRouter": "0x93f61D0D5F623144e7C390415B70102A9Cc90bA5", + "WitnetRandomness": "0x3D7Ccf6518deBE3d2fd20c4b6AEc3FD904c0Ad29", + "WitnetRequestBoard": "0xd3AD9a4b26527E3bA5Fc60B75Eb002D47D98e292" + } }, - "boba.ethereum.goerli": { - "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", - "WitnetPriceRouter": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", - "WitnetRandomness": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", - "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + "celo": { + "celo.alfajores": { + "WitnetLib": "0x1A58F1dAD4592814733913Dd59CcEbf55c45C6e1", + "WitnetPriceRouter": "0x6f8A7E2bBc1eDb8782145cD1089251f6e2C738AE", + "WitnetRequestBoard": "0x99a0B5eb260Fe3BfcF9d658850e3dD6d6B69183A", + "WitnetRandomness": "0xbD804467270bCD832b4948242453CA66972860F5" + }, + "celo.mainnet": { + "WitnetLib": "0x46FF6e05fd0a5fb4D794B99eCAE41c43f4D62d15", + "WitnetPriceRouter": "0x931673904eB6E69D775e35F522c0EA35575297Cb", + "WitnetRandomness": "0x3D7Ccf6518deBE3d2fd20c4b6AEc3FD904c0Ad29", + "WitnetRequestBoard": "0x03E709E6422E30C033456FCde38C70A12553E468" + } }, - "boba.ethereum.rinkeby": { - "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", - "WitnetPriceRouter": "0x36928Aeedaaf7D85bcA39aDfB2A39ec529ce221a", - "WitnetRandomness": "0xeAcAcC48eDD5221EC7182E1789d8bFa9dF801dFF", - "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + "conflux": { + "conflux.core.testnet": { + "WitnetLib": "0x88E443F2CB310B24dd505AeBECA23e7aBA562326", + "WitnetPriceRouter": "0x8F61C7b18F69bB87D6151B8a5D733E1945ea6c25", + "WitnetRandomness": "0x887bC8Db7D91413D1575071925Ee8d77fE2CBc81", + "WitnetRequestBoard": "0x8aB653B73a0e0552dDdce8c76F97c6AA826EFbD4" + }, + "conflux.core.mainnet": { + "WitnetLib": "0x8A026e6956B4DB3E81bb113401798e59cFBEA4C6", + "WitnetPriceRouter": "0x806c8dFd322EE2d52b188CC472e0814F64304C32", + "WitnetRandomness": "0x8C3824A9A6C3F5B0ac107E2c7dBc8d88c14aF6D9", + "WitnetRequestBoard": "0x84C708bfd79bBC83Ad8753dAb1852EfE9D6712CC" + }, + "conflux.espace.testnet": { + "WitnetLib": "0x2881F0106A1894add7600B4B147e715078Fded03", + "WitnetPriceRouter": "0x49C0BCce51a8B28f92d008394F06d5B259657F33", + "WitnetRandomness": "0xa784093826e2894Ab3Db315f4e05F0F26407BBfF", + "WitnetRequestBoard": "0x0C4be6AA667df48de54BA174bE7948875fdf152B" + }, + "conflux.espace.mainnet": { + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", + "WitnetRandomness": "0x1ebd93231a7fe551e1d6405404df34909eff4c2c", + "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" + } }, - "boba.ethereum.mainnet": { - "WitnetLib": "0x6473063EBEabC0606A4159b7d9F79BB306ED0D2A", - "WitnetPriceRouter": "0x93f61D0D5F623144e7C390415B70102A9Cc90bA5", - "WitnetRandomness": "0x3D7Ccf6518deBE3d2fd20c4b6AEc3FD904c0Ad29", - "WitnetRequestBoard": "0xd3AD9a4b26527E3bA5Fc60B75Eb002D47D98e292" - } - }, - "celo": { - "celo.alfajores": { - "WitnetLib": "0x1A58F1dAD4592814733913Dd59CcEbf55c45C6e1", - "WitnetPriceRouter": "0x6f8A7E2bBc1eDb8782145cD1089251f6e2C738AE", - "WitnetRequestBoard": "0x99a0B5eb260Fe3BfcF9d658850e3dD6d6B69183A", - "WitnetRandomness": "0xbD804467270bCD832b4948242453CA66972860F5" + "cronos": { + "cronos.testnet": { + "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", + "WitnetPriceRouter": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", + "WitnetRandomness": "0x0017A464A86f48B342Cae3b8Fe29cFCDaA7b0643", + "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + }, + "cronos.mainnet": { + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", + "WitnetRandomness": "0x3737be6FcFf5B3B0f9DCc9a9ae1Da56561D0d0d3", + "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" + } }, - "celo.mainnet": { - "WitnetLib": "0x46FF6e05fd0a5fb4D794B99eCAE41c43f4D62d15", - "WitnetPriceRouter": "0x931673904eB6E69D775e35F522c0EA35575297Cb", - "WitnetRandomness": "0x3D7Ccf6518deBE3d2fd20c4b6AEc3FD904c0Ad29", - "WitnetRequestBoard": "0x03E709E6422E30C033456FCde38C70A12553E468" - } - }, - "conflux": { - "conflux.core.testnet": { - "WitnetLib": "0x88E443F2CB310B24dd505AeBECA23e7aBA562326", - "WitnetPriceRouter": "0x8F61C7b18F69bB87D6151B8a5D733E1945ea6c25", - "WitnetRandomness": "0x887bC8Db7D91413D1575071925Ee8d77fE2CBc81", - "WitnetRequestBoard": "0x8aB653B73a0e0552dDdce8c76F97c6AA826EFbD4" + "cube": { + "cube.testnet": { + "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", + "WitnetPriceRouter": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", + "WitnetRandomness": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", + "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + }, + "cube.mainnet": { + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetPriceRouter": "0xE22f48DDdcb34BD34489fE224d7fFC1b0a361D87", + "WitnetRandomness": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", + "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" + } }, - "conflux.core.mainnet": { - "WitnetLib": "0x8A026e6956B4DB3E81bb113401798e59cFBEA4C6", - "WitnetPriceRouter": "0x806c8dFd322EE2d52b188CC472e0814F64304C32", - "WitnetRandomness": "0x8C3824A9A6C3F5B0ac107E2c7dBc8d88c14aF6D9", - "WitnetRequestBoard": "0x84C708bfd79bBC83Ad8753dAb1852EfE9D6712CC" + "dogechain": { + "dogechain.testnet": { + "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", + "WitnetPriceRouter": "0x9E943Ab1FD0D35B3BaDe31AA78D60C485EA1a604", + "WitnetRandomness": "0xD9465D38f50f364b3263Cb219e58d4dB2D584530", + "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + } }, - "conflux.espace.testnet": { - "WitnetLib": "0x2881F0106A1894add7600B4B147e715078Fded03", - "WitnetPriceRouter": "0x49C0BCce51a8B28f92d008394F06d5B259657F33", - "WitnetRandomness": "0xa784093826e2894Ab3Db315f4e05F0F26407BBfF", - "WitnetRequestBoard": "0x0C4be6AA667df48de54BA174bE7948875fdf152B" + "harmony": { + "harmony.testnet#0": { + "WitnetLib": "0x315cfa2F1108d1B490302d79AB4a5A99452e5800", + "WitnetPriceRouter": "0x08d479a544b05B297454e5CAc133abA3a584AB8E", + "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + } }, - "conflux.espace.mainnet": { - "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", - "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", - "WitnetRandomness": "0x1ebd93231a7fe551e1d6405404df34909eff4c2c", - "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" - } - }, - "cronos": { - "cronos.testnet": { - "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", - "WitnetPriceRouter": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", - "WitnetRandomness": "0x0017A464A86f48B342Cae3b8Fe29cFCDaA7b0643", - "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + "hsc": { + "hsc.testnet": { + "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", + "WitnetPriceRouter": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", + "WitnetRandomness": "0xe1330491bdC37fc4E8801843Bb3015815822F8A8", + "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + } }, - "cronos.mainnet": { - "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", - "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", - "WitnetRandomness": "0x3737be6FcFf5B3B0f9DCc9a9ae1Da56561D0d0d3", - "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" - } - }, - "cube": { - "cube.testnet": { - "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", - "WitnetPriceRouter": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", - "WitnetRandomness": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", - "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + "kava": { + "kava.testnet": { + "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", + "WitnetPriceRouter": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", + "WitnetRandomness": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", + "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + }, + "kava.mainnet": { + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", + "WitnetRandomness": "0x1ebD93231a7fE551E1d6405404Df34909eff4c2C", + "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" + } }, - "cube.mainnet": { - "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", - "WitnetPriceRouter": "0xE22f48DDdcb34BD34489fE224d7fFC1b0a361D87", - "WitnetRandomness": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", - "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" - } - }, - "dogechain": { - "dogechain.testnet": { - "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", - "WitnetPriceRouter": "0x9E943Ab1FD0D35B3BaDe31AA78D60C485EA1a604", - "WitnetRandomness": "0xD9465D38f50f364b3263Cb219e58d4dB2D584530", - "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" - } - }, - "harmony": { - "harmony.testnet#0": { - "WitnetLib": "0x315cfa2F1108d1B490302d79AB4a5A99452e5800", - "WitnetPriceRouter": "0x08d479a544b05B297454e5CAc133abA3a584AB8E", - "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" - } - }, - "hsc": { - "hsc.testnet": { - "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", - "WitnetPriceRouter": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", - "WitnetRandomness": "0xe1330491bdC37fc4E8801843Bb3015815822F8A8", - "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" - } - }, - "kava": { - "kava.testnet": { - "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", - "WitnetPriceRouter": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", - "WitnetRandomness": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", - "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + "kcc": { + "kcc.testnet": { + "WitnetLib": "0x351cEe820E3A393dCF126FbEE60928a80E99C2e1", + "WitnetPriceRouter": "0xba7CF62498340fa3734EC51Ca8A69928F0d9E03a", + "WitnetRandomness": "0x76c72518060952FAec3f90666F047e39E3333f7E", + "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + }, + "kcc.mainnet": { + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", + "WitnetRandomness": "0x1ebd93231a7fe551e1d6405404df34909eff4c2c", + "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" + } }, - "kava.mainnet": { - "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", - "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", - "WitnetRandomness": "0x1ebD93231a7fE551E1d6405404Df34909eff4c2C", - "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" - } - }, - "kcc": { - "kcc.testnet": { - "WitnetLib": "0x351cEe820E3A393dCF126FbEE60928a80E99C2e1", - "WitnetPriceRouter": "0xba7CF62498340fa3734EC51Ca8A69928F0d9E03a", - "WitnetRandomness": "0x76c72518060952FAec3f90666F047e39E3333f7E", - "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + "klaytn": { + "klaytn.testnet": { + "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", + "WitnetPriceRouter": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", + "WitnetRandomness": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", + "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + }, + "klaytn.mainnet": { + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", + "WitnetRandomness": "0x1ebD93231a7fE551E1d6405404Df34909eff4c2C", + "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" + } }, - "kcc.mainnet": { - "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", - "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", - "WitnetRandomness": "0x1ebd93231a7fe551e1d6405404df34909eff4c2c", - "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" - } - }, - "klaytn": { - "klaytn.testnet": { - "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", - "WitnetPriceRouter": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", - "WitnetRandomness": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", - "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + "meter": { + "meter.testnet": { + "WitnetLib": "0x3a46EF336f619e69dcDCa36A6772596E0fD800B3", + "WitnetPriceRouter": "0xBbDB82a16d7b66bb076879f766042b914F1C7572", + "WitnetRandomness": "0x89de43D6295D960Af1F2029a66CE680c7f798fC1", + "WitnetRequestBoard": "0xF99883aa51Fb76E37De6aC37854230d2337D2752" + }, + "meter.mainnet": { + "WitnetLib": "0x60507Ef497EC61d407cD6Fa1c65FE820620bfA88", + "WitnetPriceRouter": "0xA0Ea8C99159843afdAE9eD092E8eaec0368e8A20", + "WitnetRandomness": "0xE189B1D26dAAB45cd344452f29Db8E93B5C7FaF1", + "WitnetRequestBoard": "0x4e645446799776B9670D18E1ecBCad059987eaCa" + } }, - "klaytn.mainnet": { - "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", - "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", - "WitnetRandomness": "0x1ebD93231a7fE551E1d6405404Df34909eff4c2C", - "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" - } - }, - "meter": { - "meter.testnet": { - "WitnetLib": "0x3a46EF336f619e69dcDCa36A6772596E0fD800B3", - "WitnetPriceRouter": "0xBbDB82a16d7b66bb076879f766042b914F1C7572", - "WitnetRandomness": "0x89de43D6295D960Af1F2029a66CE680c7f798fC1", - "WitnetRequestBoard": "0xF99883aa51Fb76E37De6aC37854230d2337D2752" + "metis": { + "metis.rinkeby": { + "WitnetLib": "0xAec43b0DED0148B8456B355Ec2dA3930b42bFA08", + "WitnetPriceRouter": "0x5134EAF08bcf8cE1922991150AAad1774e93751f", + "WitnetRandomness": "0x7b0D67739b5B9480080817E5b921EbbA714236ca", + "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + }, + "metis.mainnet": { + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", + "WitnetRandomness": "0xB280e3B785f615C000A8BeBb55C35eCD2376F2eb", + "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" + } }, - "meter.mainnet": { - "WitnetLib": "0x60507Ef497EC61d407cD6Fa1c65FE820620bfA88", - "WitnetPriceRouter": "0xA0Ea8C99159843afdAE9eD092E8eaec0368e8A20", - "WitnetRandomness": "0xE189B1D26dAAB45cd344452f29Db8E93B5C7FaF1", - "WitnetRequestBoard": "0x4e645446799776B9670D18E1ecBCad059987eaCa" - } - }, - "metis": { - "metis.rinkeby": { - "WitnetLib": "0xAec43b0DED0148B8456B355Ec2dA3930b42bFA08", - "WitnetPriceRouter": "0x5134EAF08bcf8cE1922991150AAad1774e93751f", - "WitnetRandomness": "0x7b0D67739b5B9480080817E5b921EbbA714236ca", - "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + "moonbeam": { + "moonbeam.moonbase": { + "WitnetLib": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", + "WitnetPriceRouter": "0x56834Ff8D4b27db647Da97CA3bd8540f7fA0e89D", + "WitnetRandomness": "0x45111778a7db1356DaAB576cBe73681F0745182c", + "WitnetRequestBoard": "0x02Cd4089679EAA9431a88170fd784e7dE78A2425" + }, + "moonbeam.moonriver": { + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetPriceRouter": "0xE22f48DDdcb34BD34489fE224d7fFC1b0a361D87", + "WitnetRandomness": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", + "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" + }, + "moonbeam.mainnet": { + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", + "WitnetRandomness": "0x1ebD93231a7fE551E1d6405404Df34909eff4c2C", + "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" + } }, - "metis.mainnet": { - "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", - "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", - "WitnetRandomness": "0xB280e3B785f615C000A8BeBb55C35eCD2376F2eb", - "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" - } - }, - "moonbeam": { - "moonbeam.moonbase": { - "WitnetLib": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", - "WitnetPriceRouter": "0x56834Ff8D4b27db647Da97CA3bd8540f7fA0e89D", - "WitnetRandomness": "0x45111778a7db1356DaAB576cBe73681F0745182c", - "WitnetRequestBoard": "0x02Cd4089679EAA9431a88170fd784e7dE78A2425" + "okxchain": { + "okxchain.testnet": { + "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", + "WitnetPriceRouter": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", + "WitnetRandomness": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", + "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + }, + "okxchain.mainnet": { + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", + "WitnetRandomness": "0x1ebD93231a7fE551E1d6405404Df34909eff4c2C", + "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" + } }, - "moonbeam.moonriver": { - "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", - "WitnetPriceRouter": "0xE22f48DDdcb34BD34489fE224d7fFC1b0a361D87", - "WitnetRandomness": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", - "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" + "optimism": { + "optimism.goerli": { + "WitnetLib": "0x02Cd4089679EAA9431a88170fd784e7dE78A2425", + "WitnetPriceRouter": "0xD9465D38f50f364b3263Cb219e58d4dB2D584530", + "WitnetRandomness": "0x9E943Ab1FD0D35B3BaDe31AA78D60C485EA1a604", + "WitnetRequestBoard": "0x0985FDe9f424fe4f5AC516F66bAf5591e18aCBEb" + }, + "optimism.mainnet": { + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", + "WitnetRandomness": "0x1ebD93231a7fE551E1d6405404Df34909eff4c2C", + "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" + } }, - "moonbeam.mainnet": { - "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", - "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", - "WitnetRandomness": "0x1ebD93231a7fE551E1d6405404Df34909eff4c2C", - "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" - } - }, - "okxchain": { - "okxchain.testnet": { - "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", - "WitnetPriceRouter": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", - "WitnetRandomness": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", - "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + "polygon": { + "polygon.goerli": { + "WitnetBytecodes": "0xd35a147605821269D17238c422DfF321520cb041", + "WitnetBytecodesImplementation": "0xD14B4b442267ECF1Cf5cA51C942009Cf7Cf5b727", + "WitnetEncodingLib": "0xA21F3a5dEC0d8DcDdAcB3a53bb8a9Aac50A74e4B", + "WitnetLib": "0x70767887abBF23291B24620dE6bcEBe8d8F1e6F3", + "WitnetPriceRouter": "0x6d5544ca5b35bf2e7a78ace4E7B8d191fe5C9FAb", + "WitnetRandomness": "0x0178287DfECA2F13F342512282bc8B59e774aE43", + "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + }, + "polygon.mainnet": { + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetPriceRouter": "0x3806311c7138ddF2bAF2C2093ff3633E5A73AbD4", + "WitnetRandomness": "0xc8c0d4dB2D7801D6E2A863934597cFD31689f7D5", + "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" + } }, - "okxchain.mainnet": { - "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", - "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", - "WitnetRandomness": "0x1ebD93231a7fE551E1d6405404Df34909eff4c2C", - "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" - } - }, - "optimism": { - "optimism.goerli": { - "WitnetLib": "0x02Cd4089679EAA9431a88170fd784e7dE78A2425", - "WitnetPriceRouter": "0xD9465D38f50f364b3263Cb219e58d4dB2D584530", - "WitnetRandomness": "0x9E943Ab1FD0D35B3BaDe31AA78D60C485EA1a604", - "WitnetRequestBoard": "0x0985FDe9f424fe4f5AC516F66bAf5591e18aCBEb" + "reef": { + "reef.testnet": { + "WitnetLib": "0x5757040246996BFcDC890CD1CcdE6D414eAbFF74", + "WitnetPriceRouter": "0xB600e92DbA7CA66895Aa353d9128514ba47e7896", + "WitnetRandomness": "0x3f159F3bD5c27A936E0C897a4584Eb1647a62197", + "WitnetRequestBoard": "0x77d64ec18b0a14fefe673e3aa194c816c2383232" + }, + "reef.mainnet": { + "WitnetParserLib": "0xD3e5A6F4653C5D596d16c947cfA30973C5aa9f34", + "WitnetPriceRouter": "0xa22AbF47Fd1eDDf7C0967C9d7fF06FB3c42B26E2", + "WitnetRandomness": "0x03e82c280ae03af908edb250d3d187db0018e501", + "WitnetRequestBoard": "0x61E11e5f496936Ef7f9600f6D1E81b1E7c12b172" + } }, - "optimism.mainnet": { - "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", - "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", - "WitnetRandomness": "0x1ebD93231a7fE551E1d6405404Df34909eff4c2C", - "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" - } - }, - "polygon": { - "polygon.goerli": { - "WitnetBytecodes": "0x6156a1F312EdA146a2981cd5ed6B8688f1073F08", - "WitnetBytecodesImplementation": "0xf31E26b9f1E1ddb1e85814d52dF102b549cDD74B", - "WitnetEncodingLib": "0xCB1796E56d4246A013F12a24f2a23021b58019A0", - "WitnetLib": "0x70767887abBF23291B24620dE6bcEBe8d8F1e6F3", - "WitnetPriceRouter": "0x6d5544ca5b35bf2e7a78ace4E7B8d191fe5C9FAb", - "WitnetRandomness": "0x0178287DfECA2F13F342512282bc8B59e774aE43", - "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" - }, - "polygon.mainnet": { - "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", - "WitnetPriceRouter": "0x3806311c7138ddF2bAF2C2093ff3633E5A73AbD4", - "WitnetRandomness": "0xc8c0d4dB2D7801D6E2A863934597cFD31689f7D5", - "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" - } - }, - "reef": { - "reef.testnet": { - "WitnetLib": "0x5757040246996BFcDC890CD1CcdE6D414eAbFF74", - "WitnetPriceRouter": "0xB600e92DbA7CA66895Aa353d9128514ba47e7896", - "WitnetRandomness": "0x3f159F3bD5c27A936E0C897a4584Eb1647a62197", - "WitnetRequestBoard": "0x77d64ec18b0a14fefe673e3aa194c816c2383232" + "smartbch": { + "smartbch.amber": { + "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", + "WitnetPriceRouter": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", + "WitnetRandomness": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", + "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + }, + "smartbch.mainnet": { + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", + "WitnetRandomness": "0x1ebD93231a7fE551E1d6405404Df34909eff4c2C", + "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" + } }, - "reef.mainnet": { - "WitnetParserLib": "0xD3e5A6F4653C5D596d16c947cfA30973C5aa9f34", - "WitnetPriceRouter": "0xa22AbF47Fd1eDDf7C0967C9d7fF06FB3c42B26E2", - "WitnetRandomness": "0x03e82c280ae03af908edb250d3d187db0018e501", - "WitnetRequestBoard": "0x61E11e5f496936Ef7f9600f6D1E81b1E7c12b172" - } - }, - "scroll": { - "scroll.alpha": { - "WitnetParserLib": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", - "WitnetPriceRouter": "0xE2Efa3fe66352e63F118bB9165435C5BEDB777d0", - "WitnetRandomness": "0x225BAd150B9D5202DC805B34A0DF64B1a77459dF", - "WitnetRequestBoard": "0x02Cd4089679EAA9431a88170fd784e7dE78A2425" - } - }, - "smartbch": { - "smartbch.amber": { - "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", - "WitnetPriceRouter": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", - "WitnetRandomness": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", - "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" - }, - "smartbch.mainnet": { - "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", - "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", - "WitnetRandomness": "0x1ebD93231a7fE551E1d6405404Df34909eff4c2C", - "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" - } - }, - "syscoin": { - "syscoin.testnet": { - "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", - "WitnetPriceRouter": "0x9E943Ab1FD0D35B3BaDe31AA78D60C485EA1a604", - "WitnetRandomness": "0x56834Ff8D4b27db647Da97CA3bd8540f7fA0e89D", - "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" - }, - "syscoin.mainnet": { - "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", - "WitnetPriceRouter": "0xE22f48DDdcb34BD34489fE224d7fFC1b0a361D87", - "WitnetRandomness": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", - "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" - } - }, - "ultron": { - "ultron.testnet": { - "WitnetParserLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", - "WitnetPriceRouter": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", - "WitnetRandomness": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", - "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + "syscoin": { + "syscoin.testnet": { + "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", + "WitnetPriceRouter": "0x9E943Ab1FD0D35B3BaDe31AA78D60C485EA1a604", + "WitnetRandomness": "0x56834Ff8D4b27db647Da97CA3bd8540f7fA0e89D", + "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + }, + "syscoin.mainnet": { + "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetPriceRouter": "0xE22f48DDdcb34BD34489fE224d7fFC1b0a361D87", + "WitnetRandomness": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", + "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" + } }, - "ultron.mainnet": { - "WitnetParserLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", - "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", - "WitnetRandomness": "0x1ebD93231a7fE551E1d6405404Df34909eff4c2C", - "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" + "ultron": { + "ultron.testnet": { + "WitnetParserLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", + "WitnetPriceRouter": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", + "WitnetRandomness": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", + "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + }, + "ultron.mainnet": { + "WitnetParserLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", + "WitnetPriceRouter": "0xD39D4d972C7E166856c4eb29E54D3548B4597F53", + "WitnetRandomness": "0x1ebD93231a7fE551E1d6405404Df34909eff4c2C", + "WitnetRequestBoard": "0xd653fbd7c736838289262F0F41A458f35393C88a" + } } - } } \ No newline at end of file From 875afdd9ae033996d4b3bf4433550093ac4715f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 16 Feb 2023 10:44:27 +0100 Subject: [PATCH 064/119] refactor(bytecodes): simplify events --- contracts/impls/bytecodes/WitnetBytecodes.sol | 57 +++++++------------ contracts/interfaces/V2/IWitnetBytecodes.sol | 8 +-- 2 files changed, 26 insertions(+), 39 deletions(-) diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol index 5e5158ba1..c6a2ac377 100644 --- a/contracts/impls/bytecodes/WitnetBytecodes.sol +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -384,7 +384,7 @@ contract WitnetBytecodes uint16 _resultMinRank, uint16 _resultMaxRank, string memory _requestSchema, - string memory _requestFQDN, + string memory _requestAuthority, string memory _requestPath, string memory _requestQuery, string memory _requestBody, @@ -393,18 +393,18 @@ contract WitnetBytecodes ) external virtual override - returns (bytes32 _hash) + returns (bytes32 hash) { // lower case fqdn and schema, as they ought to be case-insenstive: _requestSchema = _requestSchema.toLowerCase(); - _requestFQDN = _requestFQDN.toLowerCase(); + _requestAuthority = _requestAuthority.toLowerCase(); // validate input params - _requestMethod.validate( + hash = _requestMethod.validate( _resultMinRank, _resultMaxRank, _requestSchema, - _requestFQDN, + _requestAuthority, _requestPath, _requestQuery, _requestBody, @@ -412,10 +412,8 @@ contract WitnetBytecodes _requestRadonScript ); - // compose data source struct in memory - WitnetV2.DataSource memory _source = WitnetV2.DataSource({ - method: - _requestMethod, + __database().sources[hash].method == WitnetV2.DataRequestMethods.Unknown + __database().sources[hash] = WitnetV2.DataSource({ resultDataType: WitnetEncodingLib.verifyRadonScriptResultDataType(_requestRadonScript), @@ -423,7 +421,7 @@ contract WitnetBytecodes url: string(abi.encodePacked( _requestSchema, - _requestFQDN, + _requestAuthority, bytes(_requestPath).length > 0 ? abi.encodePacked(bytes("/"), _requestPath) : bytes(""), @@ -444,23 +442,13 @@ contract WitnetBytecodes resultMinRank: _resultMinRank, - resultMaxRank: - _resultMaxRank - }); - - // generate unique hash based on source metadata: - _hash = keccak256(abi.encode(_source)); - - // add metadata to storage if new: - if (__database().sources[_hash].method == WitnetV2.DataRequestMethods.Unknown) { - __database().sources[_hash] = _source; - __pushDataProviderSource(_requestFQDN, _hash); - emit NewDataSourceHash(_hash); + __pushDataProviderSource(_requestAuthority, hash); + emit NewDataSourceHash(hash); } } function verifyRadonReducer(WitnetV2.RadonReducer memory _reducer) - external returns (bytes32 _hash) + external returns (bytes32 hash) { _reducer.validate(); bytes memory _bytecode = _reducer.encode(); @@ -469,7 +457,7 @@ contract WitnetBytecodes if (uint8(__reducer.opcode) == 0 && __reducer.filters.length == 0) { __reducer.opcode = _reducer.opcode; __pushRadonReducerFilters(__reducer, _reducer.filters); - emit NewRadonReducerHash(_hash, _bytecode); + emit NewRadonReducerHash(hash); } } @@ -483,7 +471,7 @@ contract WitnetBytecodes ) external virtual override - returns (bytes32 _hash) + returns (bytes32 hash) { // Check provided result type and result max size: // TODO: revisit @@ -535,10 +523,9 @@ contract WitnetBytecodes } // Calculate hash and add metadata to storage if new: - _hash = _bytecode.hash(); - - if (__database().retrievals[_hash].sources.length == 0) { - __database().retrievals[_hash] = RadonRetrieval({ + hash = _bytecode.hash(); + if (__database().retrievals[hash].sources.length == 0) { + __database().retrievals[hash] = RadonRetrieval({ resultDataType: _resultDataType, resultMaxSize: _resultMaxSize, args: _sourcesArgs, @@ -547,14 +534,14 @@ contract WitnetBytecodes tally: _tallyHash, weight: uint16(_bytecode.length) }); - emit NewRadonRetrievalHash(_hash, _bytecode); + emit NewRadHash(hash); } } function verifyRadonSLA(WitnetV2.RadonSLA calldata _sla) external virtual override - returns (bytes32 _hash) + returns (bytes32 hash) { // Validate SLA params: _sla.validate(); @@ -563,10 +550,10 @@ contract WitnetBytecodes bytes memory _bytecode = _sla.encode(); // Calculate hash and add to storage if new: - _hash = _bytecode.hash(); - if (__database().slas[_hash].numWitnesses == 0) { - __database().slas[_hash] = _sla; - emit NewRadonSLAHash(_hash, _bytecode); + hash = _bytecode.hash(); + if (__database().slas[hash].numWitnesses == 0) { + __database().slas[hash] = _sla; + emit NewSlaHash(hash); } } diff --git a/contracts/interfaces/V2/IWitnetBytecodes.sol b/contracts/interfaces/V2/IWitnetBytecodes.sol index ac29e3982..b64eb0b73 100644 --- a/contracts/interfaces/V2/IWitnetBytecodes.sol +++ b/contracts/interfaces/V2/IWitnetBytecodes.sol @@ -13,9 +13,9 @@ interface IWitnetBytecodes { event NewDataProvider(uint256 index); event NewDataSourceHash(bytes32 hash); - event NewRadonReducerHash(bytes32 hash, bytes bytecode); - event NewRadonRetrievalHash(bytes32 hash, bytes bytecode); - event NewRadonSLAHash(bytes32 hash, bytes bytecode); + event NewRadonReducerHash(bytes32 hash); + event NewRadHash(bytes32 hash); + event NewSlaHash(bytes32 hash); function bytecodeOf(bytes32 drRetrievalHash) external view returns (bytes memory); function bytecodeOf(bytes32 drRetrievalHash, bytes32 drSlaHash) external view returns (bytes memory); @@ -47,7 +47,7 @@ interface IWitnetBytecodes { uint16 resultMinRank, uint16 resultMaxRank, string calldata requestSchema, - string calldata requestFQDN, + string calldata requestAuthority, string calldata requestPath, string calldata requestQuery, string calldata requestBody, From 4c3b624c9e76b6613d3786a4e7da44d4114690e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 16 Feb 2023 10:51:48 +0100 Subject: [PATCH 065/119] feat(bytecodes): optimize unique hash verification on sources and reducers --- contracts/impls/bytecodes/WitnetBytecodes.sol | 73 +++++++++++-------- contracts/libs/WitnetEncodingLib.sol | 18 +++-- 2 files changed, 55 insertions(+), 36 deletions(-) diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol index c6a2ac377..72ea338df 100644 --- a/contracts/impls/bytecodes/WitnetBytecodes.sol +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -412,36 +412,45 @@ contract WitnetBytecodes _requestRadonScript ); + // should it be a new data source: + if ( __database().sources[hash].method == WitnetV2.DataRequestMethods.Unknown + ) { + // compose data source and save it in storage: __database().sources[hash] = WitnetV2.DataSource({ + method: + _requestMethod, - resultDataType: - WitnetEncodingLib.verifyRadonScriptResultDataType(_requestRadonScript), + resultDataType: + WitnetEncodingLib.verifyRadonScriptResultDataType(_requestRadonScript), - url: - string(abi.encodePacked( - _requestSchema, + url: + string(abi.encodePacked( + _requestSchema, _requestAuthority, - bytes(_requestPath).length > 0 - ? abi.encodePacked(bytes("/"), _requestPath) - : bytes(""), - bytes(_requestQuery).length > 0 - ? abi.encodePacked("?", _requestQuery) - : bytes("") - )), - - body: - _requestBody, - - headers: - _requestHeaders, - - script: - _requestRadonScript, - - resultMinRank: - _resultMinRank, - + bytes(_requestPath).length > 0 + ? abi.encodePacked(bytes("/"), _requestPath) + : bytes(""), + bytes(_requestQuery).length > 0 + ? abi.encodePacked("?", _requestQuery) + : bytes("") + )), + + body: + _requestBody, + + headers: + _requestHeaders, + + script: + _requestRadonScript, + + resultMinRank: + _resultMinRank, + + resultMaxRank: + _resultMaxRank + }); __pushDataProviderSource(_requestAuthority, hash); emit NewDataSourceHash(hash); } @@ -450,15 +459,17 @@ contract WitnetBytecodes function verifyRadonReducer(WitnetV2.RadonReducer memory _reducer) external returns (bytes32 hash) { - _reducer.validate(); - bytes memory _bytecode = _reducer.encode(); - _hash = _bytecode.hash(); - WitnetV2.RadonReducer storage __reducer = __database().reducers[_hash]; - if (uint8(__reducer.opcode) == 0 && __reducer.filters.length == 0) { + hash = keccak256(abi.encode(_reducer)); + WitnetV2.RadonReducer storage __reducer = __database().reducers[hash]; + if ( + uint8(__reducer.opcode) == 0 + && __reducer.filters.length == 0 + ) { + _reducer.validate(); __reducer.opcode = _reducer.opcode; __pushRadonReducerFilters(__reducer, _reducer.filters); emit NewRadonReducerHash(hash); - } + } } function verifyRadonRetrieval( diff --git a/contracts/libs/WitnetEncodingLib.sol b/contracts/libs/WitnetEncodingLib.sol index 30fc39840..1e6847aa7 100644 --- a/contracts/libs/WitnetEncodingLib.sol +++ b/contracts/libs/WitnetEncodingLib.sol @@ -347,6 +347,7 @@ library WitnetEncodingLib { bytes memory script ) public pure + returns (bytes32) { if (!( (method == WitnetV2.DataRequestMethods.HttpGet || method == WitnetV2.DataRequestMethods.HttpPost) @@ -378,11 +379,18 @@ library WitnetEncodingLib { resultMaxRank ); } - // Cannot perform formal validation of url parts, as they - // could templatized. - // NOP: validateUrlHost(host); - // NOP: validateUrlPath(path); - // NOP: validateUrlQuery(query); + return keccak256(abi.encode( + method, + resultMinRank, + resultMaxRank, + schema, + host, + path, + query, + body, + headers, + script + )); } function validate( From ac820ac83fc95d50d622a46838f997c56da1bbbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 16 Feb 2023 11:04:00 +0100 Subject: [PATCH 066/119] refactor(bytecodes): retrievalHash => radHash --- contracts/impls/bytecodes/WitnetBytecodes.sol | 62 +++++++++---------- contracts/interfaces/V2/IWitnetBytecodes.sol | 18 +++--- 2 files changed, 41 insertions(+), 39 deletions(-) diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol index 72ea338df..3be7ac97d 100644 --- a/contracts/impls/bytecodes/WitnetBytecodes.sol +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -158,15 +158,15 @@ contract WitnetBytecodes // ================================================================================================================ // --- Implementation of 'IWitnetBytecodes' ----------------------------------------------------------------------- - function bytecodeOf(bytes32 _hash) + function bytecodeOf(bytes32 _radHash) public view override returns (bytes memory) { - RadonRetrieval memory _retrieval = __retrievals(_hash); + RadonRetrieval memory _retrieval = __retrievals(_radHash); WitnetV2.DataSource[] memory _sources = new WitnetV2.DataSource[](_retrieval.sources.length); if (_sources.length == 0) { - revert IWitnetBytecodes.UnknownRadonRetrieval(_hash); + revert IWitnetBytecodes.UnknownRadonRetrieval(_radHash); } for (uint _ix = 0; _ix < _retrieval.sources.length; _ix ++) { _sources[_ix] = __database().sources[_retrieval.sources[_ix]]; @@ -179,7 +179,7 @@ contract WitnetBytecodes ); } - function bytecodeOf(bytes32 _retrievalHash, bytes32 _slaHash) + function bytecodeOf(bytes32 _radHash, bytes32 _slaHash) external view returns (bytes memory) { @@ -188,24 +188,24 @@ contract WitnetBytecodes revert IWitnetBytecodes.UnknownRadonSLA(_slaHash); } return abi.encodePacked( - bytecodeOf(_retrievalHash), + bytecodeOf(_radHash), __database().slas[_slaHash].encode() ); } - function hashOf(bytes32 _drRetrievalHash, bytes32 _drSlaHash) + function hashOf(bytes32 _radHash, bytes32 _slaHash) public pure virtual override returns (bytes32) { return sha256(abi.encode( - _drRetrievalHash, - _drSlaHash + _radHash, + _slaHash )); } function hashWeightWitsOf( - bytes32 _retrievalHash, + bytes32 _radHash, bytes32 _slaHash ) external view @@ -216,12 +216,12 @@ contract WitnetBytecodes if (__sla.numWitnesses == 0) { revert IWitnetBytecodes.UnknownRadonSLA(_slaHash); } - RadonRetrieval storage __retrieval = __retrievals(_retrievalHash); + RadonRetrieval storage __retrieval = __retrievals(_radHash); if (__retrieval.weight == 0) { - revert IWitnetBytecodes.UnknownRadonRetrieval(_retrievalHash); + revert IWitnetBytecodes.UnknownRadonRetrieval(_radHash); } return ( - hashOf(_retrievalHash, _slaHash), + hashOf(_radHash, _slaHash), uint32(__retrieval.weight + __sla.numWitnesses * 636 // + (8 + 2 + 8 + 4 + 8) @@ -307,75 +307,75 @@ contract WitnetBytecodes } } - function lookupRadonRetrievalAggregator(bytes32 _drRetrievalHash) + function lookupRadonRetrievalAggregator(bytes32 _radHash) external view override returns (WitnetV2.RadonReducer memory) { return __database().reducers[ - __retrievals(_drRetrievalHash).aggregator + __retrievals(_radHash).aggregator ]; } - function lookupRadonRetrievalResultDataType(bytes32 _drRetrievalHash) + function lookupRadonRetrievalResultDataType(bytes32 _radHash) external view override returns (WitnetV2.RadonDataTypes) { - return __retrievals(_drRetrievalHash).resultDataType; + return __retrievals(_radHash).resultDataType; } - function lookupRadonRetrievalResultMaxSize(bytes32 _drRetrievalHash) + function lookupRadonRetrievalResultMaxSize(bytes32 _radHash) external view override returns (uint256) { - return __retrievals(_drRetrievalHash).resultMaxSize; + return __retrievals(_radHash).resultMaxSize; } - function lookupRadonRetrievalSources(bytes32 _drRetrievalHash) + function lookupRadonRetrievalSources(bytes32 _radHash) external view override returns (bytes32[] memory) { - return __retrievals(_drRetrievalHash).sources; + return __retrievals(_radHash).sources; } - function lookupRadonRetrievalSourcesCount(bytes32 _drRetrievalHash) + function lookupRadonRetrievalSourcesCount(bytes32 _radHash) external view override returns (uint) { - return __retrievals(_drRetrievalHash).sources.length; + return __retrievals(_radHash).sources.length; } - function lookupRadonRetrievalTally(bytes32 _drRetrievalHash) + function lookupRadonRetrievalTally(bytes32 _radHash) external view override returns (WitnetV2.RadonReducer memory) { return __database().reducers[ - __retrievals(_drRetrievalHash).tally + __retrievals(_radHash).tally ]; } - function lookupRadonSLA(bytes32 _drSlaHash) + function lookupRadonSLA(bytes32 _slaHash) external view override - returns (WitnetV2.RadonSLA memory _sla) + returns (WitnetV2.RadonSLA memory sla) { - _sla = __database().slas[_drSlaHash]; - if (_sla.numWitnesses == 0) { - revert IWitnetBytecodes.UnknownRadonSLA(_drSlaHash); + sla = __database().slas[_slaHash]; + if (sla.numWitnesses == 0) { + revert IWitnetBytecodes.UnknownRadonSLA(_slaHash); } } - function lookupRadonSLAReward(bytes32 _drSlaHash) + function lookupRadonSLAReward(bytes32 _slaHash) public view override returns (uint64) { - WitnetV2.RadonSLA storage __sla = __database().slas[_drSlaHash]; + WitnetV2.RadonSLA storage __sla = __database().slas[_slaHash]; return __sla.numWitnesses * __sla.witnessReward; } diff --git a/contracts/interfaces/V2/IWitnetBytecodes.sol b/contracts/interfaces/V2/IWitnetBytecodes.sol index b64eb0b73..d489735a6 100644 --- a/contracts/interfaces/V2/IWitnetBytecodes.sol +++ b/contracts/interfaces/V2/IWitnetBytecodes.sol @@ -17,11 +17,11 @@ interface IWitnetBytecodes { event NewRadHash(bytes32 hash); event NewSlaHash(bytes32 hash); - function bytecodeOf(bytes32 drRetrievalHash) external view returns (bytes memory); - function bytecodeOf(bytes32 drRetrievalHash, bytes32 drSlaHash) external view returns (bytes memory); + function bytecodeOf(bytes32 radHash) external view returns (bytes memory); + function bytecodeOf(bytes32 radHash, bytes32 slahHash) external view returns (bytes memory); - function hashOf(bytes32 drRetrievalHash, bytes32 drSlaHash) external pure returns (bytes32 drQueryHash); - function hashWeightWitsOf(bytes32 drRetrievalHash, bytes32 drSlaHash) external view returns ( + function hashOf(bytes32 radHash, bytes32 slaHash) external pure returns (bytes32 drQueryHash); + function hashWeightWitsOf(bytes32 radHash, bytes32 slaHash) external view returns ( bytes32 drQueryHash, uint32 drQueryWeight, uint256 drQueryWits @@ -53,9 +53,10 @@ interface IWitnetBytecodes { string calldata requestBody, string[2][] calldata requestHeaders, bytes calldata requestRadonScript - ) external returns (bytes32); + ) external returns (bytes32 hash); - function verifyRadonReducer(WitnetV2.RadonReducer calldata reducer) external returns (bytes32); + function verifyRadonReducer(WitnetV2.RadonReducer calldata reducer) + external returns (bytes32 hash); function verifyRadonRetrieval( WitnetV2.RadonDataTypes resultDataType, @@ -64,9 +65,10 @@ interface IWitnetBytecodes { string[][] calldata sourcesArgs, bytes32 aggregatorHash, bytes32 tallyHash - ) external returns (bytes32); + ) external returns (bytes32 hash); - function verifyRadonSLA(WitnetV2.RadonSLA calldata drSLA) external returns (bytes32); + function verifyRadonSLA(WitnetV2.RadonSLA calldata sla) + external returns (bytes32 hash); function totalDataProviders() external view returns (uint); From b5867901894b0c3f4baac888b05e0fb5a80a1fb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 16 Feb 2023 11:05:05 +0100 Subject: [PATCH 067/119] refactor(bytecodes): fqdn => authority --- contracts/impls/bytecodes/WitnetBytecodes.sol | 23 ++++++++----------- contracts/interfaces/V2/IWitnetBytecodes.sol | 2 +- contracts/libs/WitnetEncodingLib.sol | 20 ++++++++-------- contracts/libs/WitnetV2.sol | 2 +- 4 files changed, 22 insertions(+), 25 deletions(-) diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol index 3be7ac97d..d7da87f76 100644 --- a/contracts/impls/bytecodes/WitnetBytecodes.sol +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -234,23 +234,20 @@ contract WitnetBytecodes function lookupDataProvider(uint256 _index) external view override - returns ( - string memory _fqdn, - uint256 _totalSources - ) + returns (string memory, uint256) { return ( - __database().providers[_index].fqdn, + __database().providers[_index].authority, __database().providers[_index].totalSources ); } - function lookupDataProviderIndex(string calldata _fqdn) + function lookupDataProviderIndex(string calldata _authority) external view override returns (uint256) { - return __database().providersIndex[keccak256(abi.encodePacked(_fqdn))]; + return __database().providersIndex[keccak256(abi.encodePacked(_authority))]; } function lookupDataProviderSources( @@ -395,7 +392,7 @@ contract WitnetBytecodes virtual override returns (bytes32 hash) { - // lower case fqdn and schema, as they ought to be case-insenstive: + // lower case authority and schema, as they ought to be case-insenstive: _requestSchema = _requestSchema.toLowerCase(); _requestAuthority = _requestAuthority.toLowerCase(); @@ -580,18 +577,18 @@ contract WitnetBytecodes // ================================================================================================================ // --- Internal state-modifying methods --------------------------------------------------------------------------- - function __pushDataProviderSource(string memory _fqdn, bytes32 _sourceHash) + function __pushDataProviderSource(string memory _authority, bytes32 _sourceHash) internal virtual returns (bytes32 _hash) { - if (bytes(_fqdn).length > 0) { - _hash = keccak256(abi.encodePacked(_fqdn)); + if (bytes(_authority).length > 0) { + _hash = keccak256(abi.encodePacked(_authority)); uint _index = __database().providersIndex[_hash]; if (_index == 0) { _index = ++ __bytecodes().totalDataProviders; - __database().providersIndex[keccak256(bytes(_fqdn))] = _index; - __database().providers[_index].fqdn = _fqdn; + __database().providersIndex[keccak256(bytes(_authority))] = _index; + __database().providers[_index].authority = _authority; emit NewDataProvider(_index); } __database().providers[_index].sources[ diff --git a/contracts/interfaces/V2/IWitnetBytecodes.sol b/contracts/interfaces/V2/IWitnetBytecodes.sol index d489735a6..bc01c1137 100644 --- a/contracts/interfaces/V2/IWitnetBytecodes.sol +++ b/contracts/interfaces/V2/IWitnetBytecodes.sol @@ -28,7 +28,7 @@ interface IWitnetBytecodes { ); function lookupDataProvider(uint256 index) external view returns (string memory, uint); - function lookupDataProviderIndex(string calldata fqdn) external view returns (uint); + function lookupDataProviderIndex(string calldata authority) external view returns (uint); function lookupDataProviderSources(uint256 index, uint256 offset, uint256 length) external view returns (bytes32[] memory); function lookupDataSource(bytes32 hash) external view returns (WitnetV2.DataSource memory); function lookupDataSourceResultDataType(bytes32 hash) external view returns (WitnetV2.RadonDataTypes); diff --git a/contracts/libs/WitnetEncodingLib.sol b/contracts/libs/WitnetEncodingLib.sol index 1e6847aa7..4aa69494a 100644 --- a/contracts/libs/WitnetEncodingLib.sol +++ b/contracts/libs/WitnetEncodingLib.sol @@ -37,9 +37,9 @@ library WitnetEncodingLib { bytes internal constant URL_QUERY_XALPHAS_CHARS = hex"000000000000000000000000000000000000000000000000000000000000000000ffff00ffffffffffffffffffffffffffffffffffffffffffffffff00ff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff0000ff00ffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; - error UrlBadHostIpv4(string fqdn, string part); - error UrlBadHostPort(string fqdn, string port); - error UrlBadHostXalphas(string fqdn, string part); + error UrlBadHostIpv4(string authority, string part); + error UrlBadHostPort(string authority, string port); + error UrlBadHostXalphas(string authority, string part); error UrlBadPathXalphas(string path, uint pos); error UrlBadQueryXalphas(string query, uint pos); @@ -499,25 +499,25 @@ library WitnetEncodingLib { } } - function validateUrlHost(string memory fqdn) + function validateUrlHost(string memory authority) public pure { unchecked { - if (bytes(fqdn).length > 0) { - strings.slice memory slice = fqdn.toSlice(); + if (bytes(authority).length > 0) { + strings.slice memory slice = authority.toSlice(); strings.slice memory host = slice.split(string(":").toSlice()); if (!_checkUrlHostPort(slice.toString())) { - revert UrlBadHostPort(fqdn, slice.toString()); + revert UrlBadHostPort(authority, slice.toString()); } strings.slice memory delim = string(".").toSlice(); string[] memory parts = new string[](host.count(delim) + 1); if (parts.length == 1) { - revert UrlBadHostXalphas(fqdn, fqdn); + revert UrlBadHostXalphas(authority, authority); } for (uint ix = 0; ix < parts.length; ix ++) { parts[ix] = host.split(delim).toString(); if (!_checkUrlHostXalphas(bytes(parts[ix]))) { - revert UrlBadHostXalphas(fqdn, parts[ix]); + revert UrlBadHostXalphas(authority, parts[ix]); } } if (parts.length == 4) { @@ -527,7 +527,7 @@ library WitnetEncodingLib { _prevDigits = true; } else { if (_prevDigits) { - revert UrlBadHostIpv4(fqdn, parts[ix - 1]); + revert UrlBadHostIpv4(authority, parts[ix - 1]); } else { break; } diff --git a/contracts/libs/WitnetV2.sol b/contracts/libs/WitnetV2.sol index 887f7c865..caf59e497 100644 --- a/contracts/libs/WitnetV2.sol +++ b/contracts/libs/WitnetV2.sol @@ -109,7 +109,7 @@ library WitnetV2 { } struct DataProvider { - string fqdn; + string authority; uint256 totalSources; mapping (uint256 => bytes32) sources; } From ecaf75f7f018fed009b048ac2921506e80f81b43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 16 Feb 2023 11:08:02 +0100 Subject: [PATCH 068/119] test(bytecodes): adapt to latest refactorings --- test/witnet_bytecodes.test.js | 41 ++++++++++------------------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/test/witnet_bytecodes.test.js b/test/witnet_bytecodes.test.js index 3e500d50f..d79cbbe09 100644 --- a/test/witnet_bytecodes.test.js +++ b/test/witnet_bytecodes.test.js @@ -10,8 +10,6 @@ const WitnetV2 = artifacts.require("WitnetV2") contract("WitnetBytecodes", (accounts) => { const creatorAddress = accounts[0] const firstOwnerAddress = accounts[1] - // const secondOwnerAddress = accounts[2] - // const externalAddress = accounts[3] const unprivilegedAddress = accounts[4] let bytecodes @@ -91,29 +89,21 @@ contract("WitnetBytecodes", (accounts) => { context("IWitnetBytecodes", async () => { let slaHash - let slaBytecode let concathashReducerHash - // let concathashReducerBytecode let modeNoFiltersReducerHash - // let modeNoFitlersReducerBytecode let stdev15ReducerHash let stdev25ReducerHash let rngSourceHash let binanceTickerHash - // let uniswapToken0PriceHash let uniswapToken1PriceHash let heavyRetrievalHash let heavyRetrievalBytecode let rngHash - // let rngBytecode let btcUsdPriceFeedHash - let btcUsdPriceFeedBytecode - // let fraxUsdtPriceFeedHash - // let fraxUsdtPriceFeedBytecode context("verifyDataSource(..)", async () => { context("WitnetV2.DataRequestMethods.Rng", async () => { @@ -391,7 +381,7 @@ contract("WitnetBytecodes", (accounts) => { assert(tx.logs.length === 1) expectEvent( tx.receipt, - "NewRadonRetrievalHash" + "NewRadHash" ) rngHash = tx.logs[0].args.hash // rngBytecode = tx.logs[0].args.bytecode @@ -482,10 +472,10 @@ contract("WitnetBytecodes", (accounts) => { assert(tx.logs.length === 1) expectEvent( tx.receipt, - "NewRadonRetrievalHash" + "NewRadHash" ) btcUsdPriceFeedHash = tx.logs[0].args.hash - btcUsdPriceFeedBytecode = tx.logs[0].args.bytecode + // btcUsdPriceFeedBytecode = tx.logs[0].args.bytecode }) it("verifying radon retrieval with repeated sources works", async () => { const tx = await bytecodes.verifyRadonRetrieval( @@ -505,7 +495,7 @@ contract("WitnetBytecodes", (accounts) => { assert(tx.logs.length === 1) expectEvent( tx.receipt, - "NewRadonRetrievalHash" + "NewRadHash" ) }) it("reverts if trying to verify radon retrieval w/ incompatible sources", async () => { @@ -548,10 +538,8 @@ contract("WitnetBytecodes", (accounts) => { assert(tx.logs.length === 1) expectEvent( tx.receipt, - "NewRadonRetrievalHash" + "NewRadHash" ) - // fraxUsdtPriceFeedHash = tx.logs[0].args.hash - // fraxUsdtPriceFeedBytecode = tx.logs[0].args.bytecode }) it("emits single event when verifying new radon retrieval w/ repeated http-post sources", async () => { const tx = await bytecodes.verifyRadonRetrieval( @@ -579,10 +567,10 @@ contract("WitnetBytecodes", (accounts) => { assert(tx.logs.length === 1) expectEvent( tx.receipt, - "NewRadonRetrievalHash" + "NewRadHash" ) heavyRetrievalHash = tx.logs[0].args.hash - heavyRetrievalBytecode = tx.logs[0].args.bytecode + heavyRetrievalBytecode = await bytecodes.bytecodeOf.call(heavyRetrievalHash) }) }) }) @@ -598,10 +586,9 @@ contract("WitnetBytecodes", (accounts) => { ]) expectEvent( tx.receipt, - "NewRadonSLAHash" + "NewSlaHash" ) slaHash = tx.logs[0].args.hash - slaBytecode = tx.logs[0].args.bytecode }) it("emits no event when verifying an already verified radon sla", async () => { const tx = await bytecodes.verifyRadonSLA([ @@ -717,9 +704,8 @@ contract("WitnetBytecodes", (accounts) => { it("works if trying to get bytecode onchain from known radon retrieval", async () => { await bytecodes.bytecodeOf(btcUsdPriceFeedHash) }) - it("returns expected bytecode if getting it offchain from known radon retrieval", async () => { - const bytecode = await bytecodes.bytecodeOf(btcUsdPriceFeedHash) - assert.equal(bytecode, btcUsdPriceFeedBytecode) + it("returns bytecode if getting it offchain from known radon retrieval", async () => { + await bytecodes.bytecodeOf(btcUsdPriceFeedHash) }) }) context("radon slas", async () => { @@ -733,12 +719,9 @@ contract("WitnetBytecodes", (accounts) => { it("works if trying to get bytecode onchain from known radon retrieval and sla", async () => { await bytecodes.bytecodeOf(btcUsdPriceFeedHash, slaHash) }) - it("returns expected bytecode if getting it offchain from known radon retrieval and sla", async () => { + it("returns full bytecode if getting it offchain from known radon retrieval and sla", async () => { const bytecode = await bytecodes.bytecodeOf.call(heavyRetrievalHash, slaHash) - assert.equal( - heavyRetrievalBytecode + slaBytecode.slice(2), - bytecode - ) + assert.equal(bytecode.startsWith(heavyRetrievalBytecode), true) }) }) }) From ba45d35578b369f44b084a9eaf30e013029595a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 16 Feb 2023 13:13:35 +0100 Subject: [PATCH 069/119] feat: enable proxiable implementations to be clonable as well --- contracts/apps/WitnetRandomness.sol | 12 +++- contracts/impls/WitnetProxy.sol | 6 +- contracts/patterns/Clonable.sol | 15 +---- contracts/patterns/Proxiable.sol | 24 ++++++- contracts/patterns/Upgradeable.sol | 2 +- .../requests/WitnetRequestMalleableBase.sol | 62 +++++++++++-------- 6 files changed, 72 insertions(+), 49 deletions(-) diff --git a/contracts/apps/WitnetRandomness.sol b/contracts/apps/WitnetRandomness.sol index 62c2d9ae0..8b3425030 100644 --- a/contracts/apps/WitnetRandomness.sol +++ b/contracts/apps/WitnetRandomness.sol @@ -276,6 +276,16 @@ contract WitnetRandomness return _afterClone(_cloneDeterministic(_salt)); } + /// @notice Initializes a cloned instance. + /// @dev Every cloned instance can only get initialized once. + function initializeClone(bytes memory _initData) + virtual external + initializer // => ensure a cloned instance can only be initialized once + onlyDelegateCalls // => this method can only be called upon cloned instances + { + _initialize(_initData); + } + /// @notice Tells whether this instance has been initialized. function initialized() override @@ -288,7 +298,7 @@ contract WitnetRandomness /// @notice Re-initialize contract's storage context upon a new upgrade from a proxy. /// @dev Must fail when trying to upgrade to same logic contract more than once. function _initialize(bytes memory _initData) - virtual override internal + virtual internal { witnetRandomnessRequest = WitnetRequestRandomness( abi.decode( diff --git a/contracts/impls/WitnetProxy.sol b/contracts/impls/WitnetProxy.sol index 281958976..d640592a8 100644 --- a/contracts/impls/WitnetProxy.sol +++ b/contracts/impls/WitnetProxy.sol @@ -9,10 +9,6 @@ import "../patterns/Upgradeable.sol"; /// @author The Witnet Foundation. contract WitnetProxy { - struct WitnetProxySlot { - address implementation; - } - /// Event emitted every time the implementation gets updated. event Upgraded(address indexed implementation); @@ -110,7 +106,7 @@ contract WitnetProxy { } /// @dev Complying with EIP-1967, retrieves storage struct containing proxy's current implementation address. - function __proxySlot() private pure returns (WitnetProxySlot storage _slot) { + function __proxySlot() private pure returns (Proxiable.ProxiableSlot storage _slot) { assembly { // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) _slot.slot := 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc diff --git a/contracts/patterns/Clonable.sol b/contracts/patterns/Clonable.sol index 9776f7384..daa33b0a4 100644 --- a/contracts/patterns/Clonable.sol +++ b/contracts/patterns/Clonable.sol @@ -12,7 +12,7 @@ abstract contract Clonable event Cloned(address indexed by, address indexed self, address indexed clone); - modifier onlyDelegateCalls { + modifier onlyDelegateCalls virtual { require(address(this) != _SELF, "Clonable: not a delegate call"); _; } @@ -32,19 +32,6 @@ abstract contract Clonable ); } - /// Internal virtual function for initializing contract's storage - function _initialize(bytes memory) virtual internal; - - /// @notice Initializes a cloned instance. - /// @dev Every cloned instance can only get initialized once. - function initializeClone(bytes memory _initData) - external - initializer // => ensure a cloned instance can only be initialized once - onlyDelegateCalls // => this method can only be called upon cloned instances - { - _initialize(_initData); - } - /// @notice Tells whether this instance has been initialized. function initialized() virtual public view returns (bool); diff --git a/contracts/patterns/Proxiable.sol b/contracts/patterns/Proxiable.sol index 3b5470746..966c5802d 100644 --- a/contracts/patterns/Proxiable.sol +++ b/contracts/patterns/Proxiable.sol @@ -2,8 +2,28 @@ pragma solidity >=0.6.0 <0.9.0; -interface Proxiable { +abstract contract Proxiable { /// @dev Complying with EIP-1822: Universal Upgradeable Proxy Standard (UUPS) /// @dev See https://eips.ethereum.org/EIPS/eip-1822. - function proxiableUUID() external view returns (bytes32); + function proxiableUUID() virtual external view returns (bytes32); + + struct ProxiableSlot { + address implementation; + address proxy; + } + + function __implementation() internal view returns (address) { + return __proxiable().implementation; + } + + function __proxy() internal view returns (address) { + return __proxiable().proxy; + } + + function __proxiable() internal pure returns (ProxiableSlot storage proxiable) { + assembly { + // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) + proxiable.slot := 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc + } + } } diff --git a/contracts/patterns/Upgradeable.sol b/contracts/patterns/Upgradeable.sol index 7384b9559..74f913462 100644 --- a/contracts/patterns/Upgradeable.sol +++ b/contracts/patterns/Upgradeable.sol @@ -13,7 +13,7 @@ abstract contract Upgradeable is Initializable, Proxiable { bytes32 internal immutable _CODEHASH; bool internal immutable _UPGRADABLE; - modifier onlyDelegateCalls { + modifier onlyDelegateCalls virtual { require( address(this) != _BASE, "Upgradeable: not a delegate call" diff --git a/contracts/requests/WitnetRequestMalleableBase.sol b/contracts/requests/WitnetRequestMalleableBase.sol index 7026c6223..70c9e142a 100644 --- a/contracts/requests/WitnetRequestMalleableBase.sol +++ b/contracts/requests/WitnetRequestMalleableBase.sol @@ -190,6 +190,16 @@ abstract contract WitnetRequestMalleableBase return _afterCloning(_cloneDeterministic(_salt)); } + /// @notice Initializes a cloned instance. + /// @dev Every cloned instance can only get initialized once. + function initializeClone(bytes memory _initData) + virtual external + initializer // => ensure a cloned instance can only be initialized once + onlyDelegateCalls // => this method can only be called upon cloned instances + { + _initialize(_initData); + } + /// @notice Tells whether this instance has been initialized. function initialized() override @@ -199,32 +209,6 @@ abstract contract WitnetRequestMalleableBase return __storage().template.length > 0; } - /// @dev Initializes witnessing params and template bytecode. - function _initialize(bytes memory _template) - virtual override internal - initializer - { - _transferOwnership(_msgSender()); - - assert(_template.length > 0); - __storage().template = _template; - - WitnetRequestWitnessingParams storage _params = __storage().params; - _params.numWitnesses = 2; - _params.minWitnessingConsensus = 51; - _params.witnessingCollateral = 10 ** 9; // 1 WIT - _params.witnessingReward = 5 * 10 ** 5; // 0.5 milliWITs - _params.witnessingUnitaryFee = 25 * 10 ** 4; // 0.25 milliWITs - - _malleateBytecode( - _params.numWitnesses, - _params.minWitnessingConsensus, - _params.witnessingCollateral, - _params.witnessingReward, - _params.witnessingUnitaryFee - ); - } - // ================================================================================================================ // --- 'Ownable' overriden functions ------------------------------------------------------------------------------ @@ -278,6 +262,32 @@ abstract contract WitnetRequestMalleableBase return WitnetRequestMalleableBase(_newInstance); } + /// @dev Initializes witnessing params and template bytecode. + function _initialize(bytes memory _template) + virtual internal + initializer + { + _transferOwnership(_msgSender()); + + assert(_template.length > 0); + __storage().template = _template; + + WitnetRequestWitnessingParams storage _params = __storage().params; + _params.numWitnesses = 2; + _params.minWitnessingConsensus = 51; + _params.witnessingCollateral = 10 ** 9; // 1 WIT + _params.witnessingReward = 5 * 10 ** 5; // 0.5 milliWITs + _params.witnessingUnitaryFee = 25 * 10 ** 4; // 0.25 milliWITs + + _malleateBytecode( + _params.numWitnesses, + _params.minWitnessingConsensus, + _params.witnessingCollateral, + _params.witnessingReward, + _params.witnessingUnitaryFee + ); + } + /// @dev Serializes new `bytecode` by combining immutable template with given parameters. function _malleateBytecode( uint8 _numWitnesses, From b5b98ae626580623df028e120dfe75f553a04f37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 17 Feb 2023 05:21:50 +0100 Subject: [PATCH 070/119] refactor(libs): eventually encode resultDataMaxSize occupying timelock's protobuf slot --- contracts/libs/WitnetEncodingLib.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contracts/libs/WitnetEncodingLib.sol b/contracts/libs/WitnetEncodingLib.sol index 4aa69494a..0cf6cbe98 100644 --- a/contracts/libs/WitnetEncodingLib.sol +++ b/contracts/libs/WitnetEncodingLib.sol @@ -231,15 +231,15 @@ library WitnetEncodingLib { encodedSources[ix] = encode(sources[ix]); } bytecode = abi.encodePacked( + (resultMaxSize > 0 + ? encode(uint64(resultMaxSize), 0x08) + : bytes("") + ), WitnetBuffer.concat(encodedSources), encode(uint64(aggregatorInnerBytecode.length), bytes1(0x1a)), aggregatorInnerBytecode, encode(uint64(tallyInnerBytecode.length), bytes1(0x22)), - tallyInnerBytecode, - (resultMaxSize > 0 - ? encode(uint64(resultMaxSize), 0x28) - : bytes("") - ) + tallyInnerBytecode ); return abi.encodePacked( encode(uint64(bytecode.length), bytes1(0x0a)), From 05923b2cc5da88cda10123b135d7bf4bd1035943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 17 Feb 2023 05:23:44 +0100 Subject: [PATCH 071/119] fix(libs): exclude header tag and body length when encoding rad bytes --- contracts/libs/WitnetEncodingLib.sol | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/contracts/libs/WitnetEncodingLib.sol b/contracts/libs/WitnetEncodingLib.sol index 0cf6cbe98..90f54fbe4 100644 --- a/contracts/libs/WitnetEncodingLib.sol +++ b/contracts/libs/WitnetEncodingLib.sol @@ -223,14 +223,14 @@ library WitnetEncodingLib { uint16 resultMaxSize ) public pure - returns (bytes memory bytecode) + returns (bytes memory) { bytes[] memory encodedSources = new bytes[](sources.length); for (uint ix = 0; ix < sources.length; ix ++) { replaceWildcards(sources[ix], args[ix]); encodedSources[ix] = encode(sources[ix]); } - bytecode = abi.encodePacked( + return abi.encodePacked( (resultMaxSize > 0 ? encode(uint64(resultMaxSize), 0x08) : bytes("") @@ -241,10 +241,6 @@ library WitnetEncodingLib { encode(uint64(tallyInnerBytecode.length), bytes1(0x22)), tallyInnerBytecode ); - return abi.encodePacked( - encode(uint64(bytecode.length), bytes1(0x0a)), - bytecode - ); } function encode(WitnetV2.RadonReducer memory reducer) From 2211bef075cf2d98afc1a7906ad0f633090f5216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 17 Feb 2023 05:36:59 +0100 Subject: [PATCH 072/119] fix(bytecodes): append rad's header tag and body length on bytecodeOf(bytes32, bytes32) --- contracts/impls/bytecodes/WitnetBytecodes.sol | 4 +++- test/witnet_bytecodes.test.js | 4 ---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol index d7da87f76..33df8ef0b 100644 --- a/contracts/impls/bytecodes/WitnetBytecodes.sol +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -187,8 +187,10 @@ contract WitnetBytecodes if (__sla.numWitnesses == 0) { revert IWitnetBytecodes.UnknownRadonSLA(_slaHash); } + bytes memory _radBytecode = bytecodeOf(_radHash); return abi.encodePacked( - bytecodeOf(_radHash), + WitnetEncodingLib.encode(uint64(_radBytecode.length), 0x0a), + _radBytecode, __database().slas[_slaHash].encode() ); } diff --git a/test/witnet_bytecodes.test.js b/test/witnet_bytecodes.test.js index d79cbbe09..b8a680df8 100644 --- a/test/witnet_bytecodes.test.js +++ b/test/witnet_bytecodes.test.js @@ -719,10 +719,6 @@ contract("WitnetBytecodes", (accounts) => { it("works if trying to get bytecode onchain from known radon retrieval and sla", async () => { await bytecodes.bytecodeOf(btcUsdPriceFeedHash, slaHash) }) - it("returns full bytecode if getting it offchain from known radon retrieval and sla", async () => { - const bytecode = await bytecodes.bytecodeOf.call(heavyRetrievalHash, slaHash) - assert.equal(bytecode.startsWith(heavyRetrievalBytecode), true) - }) }) }) From 9c676c1fb8ff44234ed203e22efc7494a2727a6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 17 Feb 2023 05:37:20 +0100 Subject: [PATCH 073/119] chore: move request template implementation to another branch --- contracts/requests/WitnetRequestTemplate.sol | 280 ------------------- 1 file changed, 280 deletions(-) delete mode 100644 contracts/requests/WitnetRequestTemplate.sol diff --git a/contracts/requests/WitnetRequestTemplate.sol b/contracts/requests/WitnetRequestTemplate.sol deleted file mode 100644 index 6cc7bc7da..000000000 --- a/contracts/requests/WitnetRequestTemplate.sol +++ /dev/null @@ -1,280 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.7.0 <0.9.0; -pragma experimental ABIEncoderV2; - -import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; - -import "../UsingWitnet.sol"; -import "../interfaces/V2/IWitnetBytecodes.sol"; -import "../interfaces/IWitnetRequest.sol"; -import "../libs/WitnetLib.sol"; -import "../patterns/Clonable.sol"; - -abstract contract WitnetRequestTemplate - is - IWitnetRequest, - Clonable, - UsingWitnet -{ - using ERC165Checker for address; - - struct InitData { - bytes32 slaHash; - string[][] args; - } - - /// @notice Witnet Data Request bytecode after inserting string arguments. - bytes public override bytecode; - - /// @notice SHA-256 hash of the Witnet Data Request bytecode. - bytes32 public override hash; - - /// @notice Unique id of last update attempt. - uint256 public lastAttemptId; - - /// @notice Unique id of last update that got solved successfully. - uint256 public lastId; - - /// @notice Reference to Witnet Data Requests Bytecode Registry - IWitnetBytecodes immutable public registry; - - /// @notice Result data type. - WitnetV2.RadonDataTypes immutable public resultDataType; - - /// @notice Result max size or rank (if variable type). - uint16 immutable public resultDataMaxSize; - - /// @notice Array of string arguments passed upon initialization. - string[][] public args; - - /// @notice Radon Retrieval hash. - bytes32 public retrievalHash; - - /// @notice Radon SLA hash. - bytes32 public slaHash; - - /// @notice Array of source hashes encoded as bytes. - bytes /*immutable*/ internal __sources; - - /// @notice Aggregator reducer hash. - bytes32 immutable internal _AGGREGATOR_HASH; - - /// @notice Tally reducer hash. - bytes32 immutable internal _TALLY_HASH; - - modifier notUpdating { - require(!updating(), "WitnetRequestTemplate: updating"); - _; - } - - constructor( - WitnetRequestBoard _wrb, - IWitnetBytecodes _registry, - bytes32[] memory _sources, - bytes32 _aggregator, - bytes32 _tally, - WitnetV2.RadonDataTypes _resultDataType, - uint16 _resultDataMaxSize - ) - UsingWitnet(_wrb) - { - { - require( - address(_registry).supportsInterface(type(IWitnetBytecodes).interfaceId), - "WitnetRequestTemplate: uncompliant registry" - ); - registry = _registry; - } - { - resultDataType = _resultDataType; - resultDataMaxSize = _resultDataMaxSize; - } - { - require( - _sources.length > 0, - "WitnetRequestTemplate: no sources" - ); - for (uint _i = 0; _i < _sources.length; _i ++) { - require( - _registry.lookupDataSourceResultDataType(_sources[_i]) == _resultDataType, - "WitnetRequestTemplate: mismatching sources" - ); - } - __sources = abi.encode(_sources); - } - { - // revert if the aggregator reducer is unknown - _registry.lookupRadonReducer(_aggregator); - _AGGREGATOR_HASH = _aggregator; - } - { - // revert if the tally reducer is unknown - _registry.lookupRadonReducer(_tally); - _TALLY_HASH = _tally; - } - } - - - /// ======================================================================= - /// --- WitnetRequestTemplate interface ----------------------------------- - - receive () virtual external payable {} - - function _parseWitnetResult(WitnetCBOR.CBOR memory) virtual internal view returns (bytes memory); - - function getRadonAggregator() - external view - returns (WitnetV2.RadonReducer memory) - { - return registry.lookupRadonRetrievalAggregator(_AGGREGATOR_HASH); - } - - function getRadonTally() - external view - returns (WitnetV2.RadonReducer memory) - { - return registry.lookupRadonRetrievalTally(_TALLY_HASH); - } - - function getRadonSLA() - external view - wasInitialized - returns (WitnetV2.RadonSLA memory) - { - return registry.lookupRadonSLA(slaHash); - } - - function sources() - external view - returns (bytes32[] memory) - { - return abi.decode(__sources, (bytes32[])); - } - - function update() - virtual - external payable - wasInitialized - returns (uint256 _usedFunds) - { - uint _lastAttempt = lastAttemptId; - if (updating()) { - _usedFunds = _witnetUpgradeReward(_lastAttempt); - } else { - if ( - _lastAttempt > 0 - && !witnet.isError(_witnetReadResult(_lastAttempt)) - ) { - lastId = _lastAttempt; - } - (lastAttemptId, _usedFunds) = _witnetPostRequest(this); - } - if (_usedFunds < msg.value) { - payable(msg.sender).transfer(msg.value - _usedFunds); - } - } - - function updating() - virtual - public view - returns (bool) - { - uint _lastAttempt = lastAttemptId; - return ( - _lastAttempt > 0 - && !_witnetCheckResultAvailability(_lastAttempt) - ); - } - - function lastValue() - virtual external view - returns ( - bytes memory value, - bytes32 witnetDrTxHash, - uint256 witnetTimestamp - ) - { - Witnet.Response memory _response; - Witnet.Result memory _result; - if ( - !updating() - && lastAttemptId > 0 - ) { - _response = witnet.readResponse(lastAttemptId); - _result = WitnetLib.resultFromCborBytes(_response.cborBytes); - } - if (WitnetLib.failed(_result)) { - if (lastId > 0) { - _response = witnet.readResponse(lastId); - _result = WitnetLib.resultFromCborBytes(_response.cborBytes); - } else { - revert("WitnetRequestTemplate: no value yet"); - } - } - value = _parseWitnetResult(_result.value); - witnetDrTxHash = _response.drTxHash; - witnetTimestamp = _response.timestamp; - } - - - // ================================================================================================================ - // --- 'Clonable' extension --------------------------------------------------------------------------------------- - - function clone(bytes memory _initData) - virtual public - returns (WitnetRequestTemplate) - { - return _afterCloning(_clone(), _initData); - } - - function cloneDeterministic(bytes32 _salt, bytes memory _initData) - virtual public - returns (WitnetRequestTemplate) - { - return _afterCloning(_cloneDeterministic(_salt), _initData); - } - - /// @notice Tells whether this instance has been initialized. - function initialized() - override - public view - returns (bool) - { - return retrievalHash != 0x0; - } - - /// @dev Internal virtual method containing actual initialization logic for every new clone. - function _initialize(bytes memory _initData) - virtual override internal - { - bytes32[] memory _sources = WitnetRequestTemplate(payable(self())).sources(); - InitData memory _init = abi.decode(_initData, (InitData)); - args = _init.args; - bytes32 _retrievalHash = registry.verifyRadonRetrieval( - resultDataType, - resultDataMaxSize, - _sources, - _init.args, - _AGGREGATOR_HASH, - _TALLY_HASH - ); - bytecode = registry.bytecodeOf(_retrievalHash, _init.slaHash); - hash = sha256(bytecode); - retrievalHash = _retrievalHash; - slaHash = _init.slaHash; - __sources = abi.encode(_sources); - } - - - /// =============================================================================================================== - /// --- Internal methods ------------------------------------------------------------------------------------------ - - function _afterCloning(address _newInstance, bytes memory _initData) - virtual internal - returns (WitnetRequestTemplate) - { - WitnetRequestTemplate(payable(_newInstance)).initializeClone(_initData); - return WitnetRequestTemplate(payable(_newInstance)); - } -} \ No newline at end of file From 799d0c3866bf4ffab8dc2eb88ef153d194943ffd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 17 Feb 2023 07:21:10 +0100 Subject: [PATCH 074/119] refactor(bytecodes): remove data source's result min/max range by now --- contracts/impls/bytecodes/WitnetBytecodes.sol | 14 ++-------- contracts/interfaces/V2/IWitnetBytecodes.sol | 2 -- contracts/libs/WitnetEncodingLib.sol | 26 ++++--------------- contracts/libs/WitnetV2.sol | 3 --- 4 files changed, 7 insertions(+), 38 deletions(-) diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol index 33df8ef0b..0a3c9a2ac 100644 --- a/contracts/impls/bytecodes/WitnetBytecodes.sol +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -380,8 +380,6 @@ contract WitnetBytecodes function verifyDataSource( WitnetV2.DataRequestMethods _requestMethod, - uint16 _resultMinRank, - uint16 _resultMaxRank, string memory _requestSchema, string memory _requestAuthority, string memory _requestPath, @@ -398,10 +396,8 @@ contract WitnetBytecodes _requestSchema = _requestSchema.toLowerCase(); _requestAuthority = _requestAuthority.toLowerCase(); - // validate input params + // validate data source params hash = _requestMethod.validate( - _resultMinRank, - _resultMaxRank, _requestSchema, _requestAuthority, _requestPath, @@ -442,13 +438,7 @@ contract WitnetBytecodes _requestHeaders, script: - _requestRadonScript, - - resultMinRank: - _resultMinRank, - - resultMaxRank: - _resultMaxRank + _requestRadonScript }); __pushDataProviderSource(_requestAuthority, hash); emit NewDataSourceHash(hash); diff --git a/contracts/interfaces/V2/IWitnetBytecodes.sol b/contracts/interfaces/V2/IWitnetBytecodes.sol index bc01c1137..d0c912749 100644 --- a/contracts/interfaces/V2/IWitnetBytecodes.sol +++ b/contracts/interfaces/V2/IWitnetBytecodes.sol @@ -44,8 +44,6 @@ interface IWitnetBytecodes { function verifyDataSource( WitnetV2.DataRequestMethods requestMethod, - uint16 resultMinRank, - uint16 resultMaxRank, string calldata requestSchema, string calldata requestAuthority, string calldata requestPath, diff --git a/contracts/libs/WitnetEncodingLib.sol b/contracts/libs/WitnetEncodingLib.sol index 90f54fbe4..8c8ef5a88 100644 --- a/contracts/libs/WitnetEncodingLib.sol +++ b/contracts/libs/WitnetEncodingLib.sol @@ -206,12 +206,7 @@ library WitnetEncodingLib { _encodedUrl, _encodedScript, _encodedBody, - _encodedHeaders, - source.resultMinRank > 0 || source.resultMaxRank > 0 - ? abi.encodePacked( - encode(uint64(source.resultMinRank), bytes1(0x30)), - encode(uint64(source.resultMaxRank), bytes1(0x38)) - ) : bytes("") + _encodedHeaders ); } @@ -332,10 +327,8 @@ library WitnetEncodingLib { function validate( WitnetV2.DataRequestMethods method, - uint16 resultMinRank, - uint16 resultMaxRank, string memory schema, - string memory host, + string memory authority, string memory path, string memory query, string memory body, @@ -347,14 +340,14 @@ library WitnetEncodingLib { { if (!( (method == WitnetV2.DataRequestMethods.HttpGet || method == WitnetV2.DataRequestMethods.HttpPost) - && bytes(host).length > 0 + && bytes(authority).length > 0 && ( keccak256(bytes(schema)) == keccak256(bytes("https://")) || keccak256(bytes(schema)) == keccak256(bytes("http://")) ) || method == WitnetV2.DataRequestMethods.Rng && bytes(schema).length == 0 - && bytes(host).length == 0 + && bytes(authority).length == 0 && bytes(path).length == 0 && bytes(query).length == 0 && bytes(body).length == 0 @@ -368,19 +361,10 @@ library WitnetEncodingLib { headers ); } - if (resultMinRank > resultMaxRank) { - revert WitnetV2.UnsupportedDataRequestMinMaxRanks( - uint8(method), - resultMinRank, - resultMaxRank - ); - } return keccak256(abi.encode( method, - resultMinRank, - resultMaxRank, schema, - host, + authority, path, query, body, diff --git a/contracts/libs/WitnetV2.sol b/contracts/libs/WitnetV2.sol index caf59e497..d64c5e3e7 100644 --- a/contracts/libs/WitnetV2.sol +++ b/contracts/libs/WitnetV2.sol @@ -26,7 +26,6 @@ library WitnetV2 { error RadonSlaLowCollateral(uint256 collateral); error UnsupportedDataRequestMethod(uint8 method, string schema, string body, string[2][] headers); - error UnsupportedDataRequestMinMaxRanks(uint8 method, uint16 min, uint16 max); error UnsupportedRadonDataType(uint8 datatype, uint256 maxlength); error UnsupportedRadonFilterOpcode(uint8 opcode); error UnsupportedRadonFilterArgs(uint8 opcode, bytes args); @@ -124,8 +123,6 @@ library WitnetV2 { struct DataSource { DataRequestMethods method; RadonDataTypes resultDataType; - uint16 resultMinRank; - uint16 resultMaxRank; string url; string body; string[2][] headers; From 3a62a31d1427ec8c08ea6ef4bf4e61c8072cc405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 17 Feb 2023 07:23:53 +0100 Subject: [PATCH 075/119] feat(bytecodes): add argsCount field to data sources --- contracts/impls/bytecodes/WitnetBytecodes.sol | 14 ++++++++ contracts/libs/WitnetBuffer.sol | 33 +++++++++++++++++++ contracts/libs/WitnetV2.sol | 1 + test/TestWitnetBuffer.sol | 6 ++++ 4 files changed, 54 insertions(+) diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol index 0a3c9a2ac..b576355dc 100644 --- a/contracts/impls/bytecodes/WitnetBytecodes.sol +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -407,12 +407,26 @@ contract WitnetBytecodes _requestRadonScript ); + // count data source implicit wildcards + uint8 _argsCount = WitnetBuffer.argsCountOf( + abi.encode( + _requestAuthority, bytes(" "), + _requestPath, bytes(" "), + _requestQuery, bytes(" "), + _requestBody, bytes(" "), + _requestHeaders + ) + ); + // should it be a new data source: if ( __database().sources[hash].method == WitnetV2.DataRequestMethods.Unknown ) { // compose data source and save it in storage: __database().sources[hash] = WitnetV2.DataSource({ + argsCount: + _argsCount, + method: _requestMethod, diff --git a/contracts/libs/WitnetBuffer.sol b/contracts/libs/WitnetBuffer.sol index 0296e1d94..b6984553c 100644 --- a/contracts/libs/WitnetBuffer.sol +++ b/contracts/libs/WitnetBuffer.sol @@ -382,6 +382,39 @@ library WitnetBuffer { buffer.cursor += 32; } + /// @notice Count number of required parameters for given bytes arrays + /// @dev Wildcard format: "\#\", with # in ["0".."9"]. + /// @param input Bytes array containing strings. + /// @param count Highest wildcard index found, plus 1. + function argsCountOf(bytes memory input) + internal pure + returns (uint8 count) + { + if (input.length < 3) { + return 0; + } + unchecked { + uint ix = 0; + 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 ax = uint8(uint8(input[ix + 1]) - uint8(bytes1("0")) + 1); + if (ax > count) { + count = ax; + } + ix += 3; + } else { + ix ++; + } + } + } + } + /// @notice Replace bytecode indexed wildcards by correspondent string. /// @dev Wildcard format: "\#\", with # in ["0".."9"]. /// @param input Bytes array containing strings. diff --git a/contracts/libs/WitnetV2.sol b/contracts/libs/WitnetV2.sol index d64c5e3e7..d37a1a453 100644 --- a/contracts/libs/WitnetV2.sol +++ b/contracts/libs/WitnetV2.sol @@ -121,6 +121,7 @@ library WitnetV2 { } struct DataSource { + uint8 argsCount; DataRequestMethods method; RadonDataTypes resultDataType; string url; diff --git a/test/TestWitnetBuffer.sol b/test/TestWitnetBuffer.sol index 6a2c17377..ac2240812 100644 --- a/test/TestWitnetBuffer.sol +++ b/test/TestWitnetBuffer.sol @@ -12,6 +12,12 @@ contract TestWitnetBuffer { event Log(string _topic, uint256 _value); + function testArgsCountsOf() external { + Assert.equal(WitnetBuffer.argsCountOf(bytes("aaaaaa\\0\\")), 1, "bad count if trailing wildcard"); + Assert.equal(WitnetBuffer.argsCountOf(bytes("\\9\\aaaaaaa\\1\\")), 10, "bad count if disordered wildcard indexes"); + Assert.equal(WitnetBuffer.argsCountOf(bytes("\\6\\\\6\\\\6\\")), 7, "bad count if repeated wildcard indexes"); + } + function testFork() external { WitnetBuffer.Buffer memory buff = WitnetBuffer.Buffer( hex"00000000000000000000000000000000000000000000635C305C0102030405060708090a", From f4a9382278b221bb3179b6add40e06c4fff2adb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 17 Feb 2023 07:26:15 +0100 Subject: [PATCH 076/119] fix(libs): allow data source's schema to be empty --- contracts/libs/WitnetEncodingLib.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/contracts/libs/WitnetEncodingLib.sol b/contracts/libs/WitnetEncodingLib.sol index 8c8ef5a88..0a8a526d2 100644 --- a/contracts/libs/WitnetEncodingLib.sol +++ b/contracts/libs/WitnetEncodingLib.sol @@ -342,8 +342,9 @@ library WitnetEncodingLib { (method == WitnetV2.DataRequestMethods.HttpGet || method == WitnetV2.DataRequestMethods.HttpPost) && bytes(authority).length > 0 && ( - keccak256(bytes(schema)) == keccak256(bytes("https://")) - || keccak256(bytes(schema)) == keccak256(bytes("http://")) + bytes(schema).length == 0 + || keccak256(bytes(schema)) == keccak256(bytes("https://")) + || keccak256(bytes(schema)) == keccak256(bytes("http://")) ) || method == WitnetV2.DataRequestMethods.Rng && bytes(schema).length == 0 From 443d6fa07c109b2ece99c298c6042eaa57f11e59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 17 Feb 2023 07:36:19 +0100 Subject: [PATCH 077/119] feat(bytecodes): implement lookupDataSourceArgsCount --- contracts/impls/bytecodes/WitnetBytecodes.sol | 11 +++++++++++ contracts/interfaces/V2/IWitnetBytecodes.sol | 1 + 2 files changed, 12 insertions(+) diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol index b576355dc..50a3e3eae 100644 --- a/contracts/impls/bytecodes/WitnetBytecodes.sol +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -284,6 +284,17 @@ contract WitnetBytecodes } } + function lookupDataSourceArgsCount(bytes32 _hash) + external view + override + returns (uint8) + { + if (__database().sources[_hash].method == WitnetV2.DataRequestMethods.Unknown) { + revert IWitnetBytecodes.UnknownDataSource(_hash); + } + return __database().sources[_hash].argsCount; + } + function lookupDataSourceResultDataType(bytes32 _hash) external view override diff --git a/contracts/interfaces/V2/IWitnetBytecodes.sol b/contracts/interfaces/V2/IWitnetBytecodes.sol index d0c912749..a89b0d628 100644 --- a/contracts/interfaces/V2/IWitnetBytecodes.sol +++ b/contracts/interfaces/V2/IWitnetBytecodes.sol @@ -31,6 +31,7 @@ interface IWitnetBytecodes { function lookupDataProviderIndex(string calldata authority) external view returns (uint); function lookupDataProviderSources(uint256 index, uint256 offset, uint256 length) external view returns (bytes32[] memory); function lookupDataSource(bytes32 hash) external view returns (WitnetV2.DataSource memory); + function lookupDataSourceArgsCount(bytes32 hash) external view returns (uint8); function lookupDataSourceResultDataType(bytes32 hash) external view returns (WitnetV2.RadonDataTypes); function lookupRadonReducer(bytes32 hash) external view returns (WitnetV2.RadonReducer memory); function lookupRadonRetrievalAggregator(bytes32 hash) external view returns (WitnetV2.RadonReducer memory); From cb717e1390fc136b5ae96533e159012530d72e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 17 Feb 2023 07:38:38 +0100 Subject: [PATCH 078/119] fix(bytecodes): avoid randomness rads w/ same reducers be stored more than once --- contracts/impls/bytecodes/WitnetBytecodes.sol | 2 +- migrations/witnet.addresses.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol index 50a3e3eae..701706fc0 100644 --- a/contracts/impls/bytecodes/WitnetBytecodes.sol +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -549,7 +549,7 @@ contract WitnetBytecodes // Calculate hash and add metadata to storage if new: hash = _bytecode.hash(); - if (__database().retrievals[hash].sources.length == 0) { + if (__database().retrievals[hash].weight == 0) { __database().retrievals[hash] = RadonRetrieval({ resultDataType: _resultDataType, resultMaxSize: _resultMaxSize, diff --git a/migrations/witnet.addresses.json b/migrations/witnet.addresses.json index ebcb4a513..5c95367f5 100644 --- a/migrations/witnet.addresses.json +++ b/migrations/witnet.addresses.json @@ -278,9 +278,9 @@ }, "polygon": { "polygon.goerli": { - "WitnetBytecodes": "0xd35a147605821269D17238c422DfF321520cb041", - "WitnetBytecodesImplementation": "0xD14B4b442267ECF1Cf5cA51C942009Cf7Cf5b727", - "WitnetEncodingLib": "0xA21F3a5dEC0d8DcDdAcB3a53bb8a9Aac50A74e4B", + "WitnetBytecodes": "", + "WitnetBytecodesImplementation": "", + "WitnetEncodingLib": "", "WitnetLib": "0x70767887abBF23291B24620dE6bcEBe8d8F1e6F3", "WitnetPriceRouter": "0x6d5544ca5b35bf2e7a78ace4E7B8d191fe5C9FAb", "WitnetRandomness": "0x0178287DfECA2F13F342512282bc8B59e774aE43", From 83d0070e4e1cdebf9eb5400cf0e6df7deb1c9e8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 17 Feb 2023 07:39:23 +0100 Subject: [PATCH 079/119] feat(bytecodes): check args count for every source upon rad verification --- contracts/impls/bytecodes/WitnetBytecodes.sol | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol index 701706fc0..860077f35 100644 --- a/contracts/impls/bytecodes/WitnetBytecodes.sol +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -534,6 +534,14 @@ contract WitnetBytecodes uint8(_resultDataType) ); } + // check enough args are provided for each source + if (_sourcesArgs[_ix].length < uint(_sources[_ix].argsCount)) { + revert WitnetV2.RadonRetrievalMissingArgs( + _ix, + _sources[_ix].argsCount, + _sourcesArgs[_ix].length + ); + } } // Build radon retrieval bytecode: From bca6569c704c2dad9d357411f3b6f37743c770f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 16 Feb 2023 13:24:54 +0100 Subject: [PATCH 080/119] refactor: force no receive() on WitnetUpgradableBase --- contracts/impls/WitnetUpgradableBase.sol | 4 +--- .../boards/trustable/WitnetRequestBoardTrustableBase.sol | 2 +- .../boards/trustless/WitnetRequestBoardTrustlessBase.sol | 2 +- contracts/impls/bytecodes/WitnetBytecodes.sol | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/contracts/impls/WitnetUpgradableBase.sol b/contracts/impls/WitnetUpgradableBase.sol index 87833d8c7..a7d4ce6ed 100644 --- a/contracts/impls/WitnetUpgradableBase.sol +++ b/contracts/impls/WitnetUpgradableBase.sol @@ -36,11 +36,9 @@ abstract contract WitnetUpgradableBase _WITNET_UPGRADABLE_VERSION = _versionTag; proxiableUUID = keccak256(bytes(_proxiableUUID)); } - - receive() external payable virtual; /// @dev Reverts if proxy delegatecalls to unexistent method. - fallback() external payable { + fallback() external { revert("WitnetUpgradableBase: not implemented"); } diff --git a/contracts/impls/boards/trustable/WitnetRequestBoardTrustableBase.sol b/contracts/impls/boards/trustable/WitnetRequestBoardTrustableBase.sol index d8c0a5611..b8d41ab0a 100644 --- a/contracts/impls/boards/trustable/WitnetRequestBoardTrustableBase.sol +++ b/contracts/impls/boards/trustable/WitnetRequestBoardTrustableBase.sol @@ -35,7 +35,7 @@ abstract contract WitnetRequestBoardTrustableBase WitnetUpgradableBase(_upgradable, _versionTag, "io.witnet.proxiable.board") {} - receive() external payable override { + receive() external payable { revert("WitnetRequestBoardTrustableBase: no transfers accepted"); } diff --git a/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol b/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol index 2a688c8c6..608f515bf 100644 --- a/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol +++ b/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol @@ -58,7 +58,7 @@ abstract contract WitnetRequestBoardTrustlessBase ) {} - receive() external payable override { + receive() external payable { revert("WitnetRequestBoardTrustlessBase: no transfers accepted"); } diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol index 860077f35..179eff711 100644 --- a/contracts/impls/bytecodes/WitnetBytecodes.sol +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -41,7 +41,7 @@ contract WitnetBytecodes ) {} - receive() external payable override { + receive() external payable { revert("WitnetBytecodes: no transfers"); } From 2bf5b07ad9c9fc1dd61c2e882637c07d97d917ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 16 Feb 2023 13:28:47 +0100 Subject: [PATCH 081/119] feat: abstract feeder login from WitnetRequestTemplate --- contracts/requests/WitnetRequestTemplate.sol | 26 ++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 contracts/requests/WitnetRequestTemplate.sol diff --git a/contracts/requests/WitnetRequestTemplate.sol b/contracts/requests/WitnetRequestTemplate.sol new file mode 100644 index 000000000..428cdf78b --- /dev/null +++ b/contracts/requests/WitnetRequestTemplate.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.7.0 <0.9.0; +pragma experimental ABIEncoderV2; + +import "../libs/WitnetV2.sol"; + + +abstract contract WitnetRequestTemplate + is + IWitnetRequest +{ + event WitnetRequestTemplateSettled(WitnetRequest indexed request, bytes32 indexed radHash, string[][] args); + + function getDataSources() virtual external view returns (bytes32[] memory); + function getDataSourcesArgsCount() virtual external view returns (uint8[] memory); + function getDataSourcesCount() virtual external view returns (uint256); + function getRadonAggregatorHash() virtual external view returns (bytes32); + function getRadonTallyHash() virtual external view returns (bytes32); + function getResultDataMaxSize() virtual external view returns (uint16); + function getResultDataType() virtual external view returns (WitnetV2.RadonDataTypes); + function lookupDataSourceByIndex(uint256) virtual external view returns (WitnetV2.DataSource memory); + function lookupRadonAggregator() virtual external view returns (WitnetV2.RadonReducer memory); + function lookupRadonTally() virtual external view returns (WitnetV2.RadonReducer memory); + function settleArgs(string[][] calldata args) virtual external returns (WitnetRequest); +} \ No newline at end of file From 46631e027cdcd0c5070e6bbf5b7a75f9bc7e4f9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 16 Feb 2023 13:29:37 +0100 Subject: [PATCH 082/119] refactor: WitnetRequest abstract contract --- contracts/requests/WitnetRequest.sol | 16 ---------------- contracts/requests/WitnetRequestBase.sol | 7 ++++++- contracts/requests/WitnetRequestTemplate.sol | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 17 deletions(-) delete mode 100644 contracts/requests/WitnetRequest.sol diff --git a/contracts/requests/WitnetRequest.sol b/contracts/requests/WitnetRequest.sol deleted file mode 100644 index db37e00da..000000000 --- a/contracts/requests/WitnetRequest.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.7.0 <0.9.0; - -import "./WitnetRequestBase.sol"; - -contract WitnetRequest - is - WitnetRequestBase -{ - using Witnet for bytes; - constructor(bytes memory _bytecode) { - bytecode = _bytecode; - hash = _bytecode.hash(); - } -} diff --git a/contracts/requests/WitnetRequestBase.sol b/contracts/requests/WitnetRequestBase.sol index ef6ea9c4d..ff5d1a864 100644 --- a/contracts/requests/WitnetRequestBase.sol +++ b/contracts/requests/WitnetRequestBase.sol @@ -4,7 +4,7 @@ pragma solidity >=0.7.0 <0.9.0; import "../libs/Witnet.sol"; -abstract contract WitnetRequestBase +contract WitnetRequestBase is IWitnetRequest { @@ -13,4 +13,9 @@ abstract contract WitnetRequestBase /// Returns SHA256 hash of Witnet Data Request as CBOR-encoded bytes. bytes32 public override hash; + + constructor(bytes memory _bytecode) { + bytecode = _bytecode; + hash = Witnet.hash(_bytecode); + } } diff --git a/contracts/requests/WitnetRequestTemplate.sol b/contracts/requests/WitnetRequestTemplate.sol index 428cdf78b..a2648a2cf 100644 --- a/contracts/requests/WitnetRequestTemplate.sol +++ b/contracts/requests/WitnetRequestTemplate.sol @@ -5,6 +5,20 @@ pragma experimental ABIEncoderV2; import "../libs/WitnetV2.sol"; +abstract contract WitnetRequest + is + IWitnetRequest +{ + event WitnetRequestSettled(WitnetV2.RadonSLA sla); + + function args() virtual external view returns (string[][] memory); + function getRadonSLA() virtual external view returns (WitnetV2.RadonSLA memory); + function initialized() virtual external view returns (bool); + function radHash() virtual external view returns (bytes32); + function slaHash() virtual external view returns (bytes32); + function template() virtual external view returns (WitnetRequestTemplate); + function modifySLA(WitnetV2.RadonSLA calldata sla) virtual external; +} abstract contract WitnetRequestTemplate is From 4961e7077b002bbfba3efbadee188cce5bb1a99d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 16 Feb 2023 13:37:04 +0100 Subject: [PATCH 083/119] feat: implement WitnetRequestFactory as Upgradeable and Clonable --- contracts/apps/WitnetRequestFactory.sol | 578 ++++++++++++++++++++ contracts/data/WitnetRequestFactoryData.sol | 81 +++ 2 files changed, 659 insertions(+) create mode 100644 contracts/apps/WitnetRequestFactory.sol create mode 100644 contracts/data/WitnetRequestFactoryData.sol diff --git a/contracts/apps/WitnetRequestFactory.sol b/contracts/apps/WitnetRequestFactory.sol new file mode 100644 index 000000000..0073616f2 --- /dev/null +++ b/contracts/apps/WitnetRequestFactory.sol @@ -0,0 +1,578 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.7.0 <0.9.0; +pragma experimental ABIEncoderV2; + +import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; + +import "../data/WitnetRequestFactoryData.sol"; +import "../impls/WitnetUpgradableBase.sol"; +import "../interfaces/V2/IWitnetBytecodes.sol"; +import "../patterns/Clonable.sol"; +import "../requests/WitnetRequestTemplate.sol"; + +interface IWitnetRequestFactory { + event WitnetRequestTemplateBuilt(WitnetRequestTemplate template); + function build( + bytes32[] memory sourcesIds, + bytes32 aggregatorId, + bytes32 tallyId, + uint16 resultDataMaxSize + ) external returns (WitnetRequestTemplate _template); +} + +contract WitnetRequestFactory + is + Clonable, + IWitnetRequestFactory, + WitnetRequest, + WitnetRequestFactoryData, + WitnetRequestTemplate, + WitnetUpgradableBase +{ + using ERC165Checker for address; + + /// @notice Reference to Witnet Data Requests Bytecode Registry + IWitnetBytecodes immutable public registry; + + modifier onlyDelegateCalls override(Clonable, Upgradeable) { + require( + address(this) != _BASE, + "WitnetRequestFactory: not a delegate call" + ); + _; + } + + modifier onlyOnFactory { + require( + address(this) == __proxy(), + "WitnetRequestFactory: not a factory" + ); + _; + } + + modifier onlyOnTemplates { + require( + __witnetRequestTemplate().tallyHash != bytes32(0), + "WitnetRequestFactory: not a template" + ); + _; + } + + constructor( + IWitnetBytecodes _registry, + bool _upgradable, + bytes32 _versionTag + ) + WitnetUpgradableBase( + _upgradable, + _versionTag, + "io.witnet.requests.factory" + ) + { + require( + address(_registry).supportsInterface(type(IWitnetBytecodes).interfaceId), + "WitnetRequestFactory: uncompliant registry" + ); + registry = _registry; + } + + + /// =============================================================================================================== + /// --- IWitnetRequestFactory implementation ---------------------------------------------------------------------- + + function build( + bytes32[] memory sourcesIds, + bytes32 aggregatorId, + bytes32 tallyId, + uint16 resultDataMaxSize + ) + virtual override + external + onlyOnFactory + returns (WitnetRequestTemplate _template) + { + _template = WitnetRequestFactory(_cloneDeterministic( + keccak256(abi.encodePacked( + sourcesIds, + aggregatorId, + tallyId, + resultDataMaxSize + )) + )).initializeWitnetRequestTemplate( + sourcesIds, + aggregatorId, + tallyId, + resultDataMaxSize + ); + emit WitnetRequestTemplateBuilt(_template); + } + + function initializeWitnetRequestTemplate( + bytes32[] calldata _sources, + bytes32 _aggregatorId, + bytes32 _tallyId, + uint16 _resultDataMaxSize + ) + virtual public + initializer + returns (WitnetRequestTemplate) + { + WitnetV2.RadonDataTypes _resultDataType; + require(_sources.length > 0, "WitnetRequestTemplate: no sources"); + // check all sources return the same data types + for (uint _ix = 0; _ix < _sources.length; _ix ++) { + if (_ix == 0) { + _resultDataType = registry.lookupDataSourceResultDataType(_sources[_ix]); + } else { + require( + _resultDataType == registry.lookupDataSourceResultDataType(_sources[_ix]), + "WitnetRequestTemplate: mismatching sources" + ); + } + } + // revert if the aggregator reducer is unknown + registry.lookupRadonReducer(_aggregatorId); + // revert if the tally reducer is unknown + registry.lookupRadonReducer(_tallyId); + { + WitnetRequestTemplateSlot storage __data = __witnetRequestTemplate(); + __data.sources = _sources; + __data.aggregatorHash = _aggregatorId; + __data.tallyHash = _tallyId; + __data.resultDataType = _resultDataType; + __data.resultDataMaxSize = _resultDataMaxSize; + } + return WitnetRequestTemplate(address(this)); + } + + function initializeWitnetRequest( + string[][] memory _args, + bytes32 _radHash + ) + virtual public + initializer + returns (WitnetRequest) + { + WitnetRequestSlot storage __data = __witnetRequest(); + __data.args = _args; + __data.radHash = _radHash; + __data.template = WitnetRequestTemplate(msg.sender); + return WitnetRequest(address(this)); + } + + + // ================================================================================================================ + // ---Overrides 'IERC165' ----------------------------------------------------------------------------------------- + + /// @dev See {IERC165-supportsInterface}. + function supportsInterface(bytes4 _interfaceId) + public view + virtual override + returns (bool) + { + if (address(this) == _SELF) { + return (_interfaceId == type(Upgradeable).interfaceId); + } else { + if (address(this) == __proxy()) { + return ( + _interfaceId == type(IWitnetRequestFactory).interfaceId + || super.supportsInterface(_interfaceId) + ); + } else if (__witnetRequest().radHash != bytes32(0)) { + return ( + _interfaceId == type(WitnetRequestTemplate).interfaceId + || _interfaceId == type(WitnetRequest).interfaceId + || _interfaceId == type(IWitnetRequest).interfaceId + ); + } else { + return (_interfaceId == type(WitnetRequestTemplate).interfaceId); + } + } + } + + + // ================================================================================================================ + // --- Overrides 'Ownable2Step' ----------------------------------------------------------------------------------- + + /// @notice Returns the address of the pending owner. + function pendingOwner() + public view + virtual override + onlyOnFactory + returns (address) + { + return __witnetRequestFactory().pendingOwner; + } + + /// @notice Returns the address of the current owner. + function owner() + public view + virtual override + onlyOnFactory + returns (address) + { + return __witnetRequestFactory().owner; + } + + /// @notice Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one. + /// @dev Can only be called by the current owner. + function transferOwnership(address _newOwner) + public + virtual override + onlyOnFactory + onlyOwner + { + __witnetRequestFactory().pendingOwner = _newOwner; + emit OwnershipTransferStarted(owner(), _newOwner); + } + + /// @dev Transfers ownership of the contract to a new account (`_newOwner`) and deletes any pending owner. + /// @dev Internal function without access restriction. + function _transferOwnership(address _newOwner) + internal + virtual override + { + delete __witnetRequestFactory().pendingOwner; + address _oldOwner = owner(); + if (_newOwner != _oldOwner) { + __witnetRequestFactory().owner = _newOwner; + emit OwnershipTransferred(_oldOwner, _newOwner); + } + } + + + // ================================================================================================================ + // --- Overrides 'Upgradeable' ------------------------------------------------------------------------------------- + + /// @notice Re-initialize contract's storage context upon a new upgrade from a proxy. + /// @dev Must fail when trying to upgrade to same logic contract more than once. + function initialize(bytes memory) + virtual override + public + initializer + onlyDelegateCalls + { + // WitnetRequest or WitnetRequestTemplate instances would already be initialized, + // so only callable from proxies, in practice. + + address _owner = __witnetRequestFactory().owner; + if (_owner == address(0)) { + // set owner if none set yet + _owner = msg.sender; + __witnetRequestFactory().owner = _owner; + } else { + // only owner can initialize the proxy + if (msg.sender != _owner) { + revert WitnetUpgradableBase.OnlyOwner(_owner); + } + } + + if (__proxiable().proxy == address(0)) { + // first initialization of the proxy + __proxiable().proxy = address(this); + } + + if (__proxiable().implementation != address(0)) { + // same implementation cannot be initialized more than once: + if(__proxiable().implementation == base()) { + revert WitnetUpgradableBase.AlreadyUpgraded(base()); + } + } + __proxiable().implementation = base(); + + emit Upgraded(msg.sender, base(), codehash(), version()); + } + + /// Tells whether provided address could eventually upgrade the contract. + function isUpgradableFrom(address _from) external view override returns (bool) { + address _owner = __witnetRequestFactory().owner; + return ( + // false if the WRB is intrinsically not upgradable, or `_from` is no owner + isUpgradable() + && _owner == _from + ); + } + + + // ================================================================================================================ + /// --- Clonable implementation and override ---------------------------------------------------------------------- + + /// @notice Tells whether a WitnetRequest instance has been fully initialized. + /// @dev True only on WitnetRequest instances with some Radon SLA set. + function initialized() + virtual override(Clonable, WitnetRequest) + public view + returns (bool) + { + return __witnetRequest().slaHash != bytes32(0); + } + + /// @notice Contract address to which clones will be re-directed. + function self() + virtual override + public view + returns (address) + { + return (__proxy() != address(0) + ? __implementation() + : base() + ); + } + + + /// =============================================================================================================== + /// --- IWitnetRequest implementation ----------------------------------------------------------------------------- + + function bytecode() override external view returns (bytes memory) { + return __witnetRequest().bytecode; + } + + function hash() override external view returns (bytes32) { + return __witnetRequest().hash; + } + + + /// =============================================================================================================== + /// --- WitnetRequest implementation ------------------------------------------------------------------------------ + + function args() + override + external view + onlyDelegateCalls + returns (string[][] memory) + { + return __witnetRequest().args; + } + + function getRadonSLA() + override + external view + onlyDelegateCalls + returns (WitnetV2.RadonSLA memory) + { + return registry.lookupRadonSLA( + __witnetRequest().slaHash + ); + } + + function radHash() + override + external view + onlyDelegateCalls + returns (bytes32) + { + return __witnetRequest().radHash; + } + + function slaHash() + override + external view + onlyDelegateCalls + returns (bytes32) + { + return __witnetRequest().slaHash; + } + + function template() + override + external view + onlyDelegateCalls + returns (WitnetRequestTemplate) + { + return __witnetRequest().template; + } + + function modifySLA(WitnetV2.RadonSLA memory _sla) + virtual override + external + onlyDelegateCalls + { + WitnetRequestTemplate _template = __witnetRequest().template; + require( + address(_template) != address(0), + "WitnetRequestFactory: not a request" + ); + bytes32 _slaHash = registry.verifyRadonSLA(_sla); + WitnetRequestSlot storage __data = __witnetRequest(); + bytes memory _bytecode = registry.bytecodeOf(__data.radHash, _slaHash); + __data.bytecode = _bytecode; + __data.hash = Witnet.hash(_bytecode); + __data.slaHash = _slaHash; + emit WitnetRequestSettled(_sla); + } + + + /// =============================================================================================================== + /// --- WitnetRequestTemplate implementation ---------------------------------------------------------------------- + + function getDataSources() + override + external view + onlyDelegateCalls + returns (bytes32[] memory) + { + WitnetRequestTemplate _template = __witnetRequest().template; + if (address(_template) != address(0)) { + return _template.getDataSources(); + } else { + return __witnetRequestTemplate().sources; + } + + } + + function getDataSourcesArgsCount() + override + external view + onlyDelegateCalls + returns (uint8[] memory) + { + revert("WitnetRequestFactory: not yet implemented"); + } + + function getDataSourcesCount() + override + external view + onlyDelegateCalls + returns (uint256) + { + WitnetRequestTemplate _template = __witnetRequest().template; + if (address(_template) != address(0)) { + return _template.getDataSourcesCount(); + } else { + return __witnetRequestTemplate().sources.length; + } + } + + function getRadonAggregatorHash() + override + external view + onlyDelegateCalls + returns (bytes32) + { + WitnetRequestTemplate _template = __witnetRequest().template; + if (address(_template) != address(0)) { + return _template.getRadonAggregatorHash(); + } else { + return __witnetRequestTemplate().aggregatorHash; + } + } + + function getRadonTallyHash() + override + external view + onlyDelegateCalls + returns (bytes32) + { + WitnetRequestTemplate _template = __witnetRequest().template; + if (address(_template) != address(0)) { + return _template.getRadonTallyHash(); + } else { + return __witnetRequestTemplate().tallyHash; + } + } + + function getResultDataMaxSize() + override + external view + onlyDelegateCalls + returns (uint16) + { + WitnetRequestTemplate _template = __witnetRequest().template; + if (address(_template) != address(0)) { + return _template.getResultDataMaxSize(); + } else { + return __witnetRequestTemplate().resultDataMaxSize; + } + } + + function getResultDataType() + override + external view + onlyDelegateCalls + returns (WitnetV2.RadonDataTypes) + { + WitnetRequestTemplate _template = __witnetRequest().template; + if (address(_template) != address(0)) { + return _template.getResultDataType(); + } else { + return __witnetRequestTemplate().resultDataType; + } + } + + function lookupDataSourceByIndex(uint256 _index) + override + external view + onlyDelegateCalls + returns (WitnetV2.DataSource memory) + { + WitnetRequestTemplate _template = __witnetRequest().template; + if (address(_template) != address(0)) { + return _template.lookupDataSourceByIndex(_index); + } else { + require( + _index < __witnetRequestTemplate().sources.length, + "WitnetRequestTemplate: out of range" + ); + return registry.lookupDataSource( + __witnetRequestTemplate().sources[_index] + ); + } + } + + function lookupRadonAggregator() + override + external view + onlyDelegateCalls + returns (WitnetV2.RadonReducer memory) + { + WitnetRequestTemplate _template = __witnetRequest().template; + if (address(_template) != address(0)) { + return _template.lookupRadonAggregator(); + } else { + return registry.lookupRadonReducer( + __witnetRequestTemplate().aggregatorHash + ); + } + } + + function lookupRadonTally() + override + external view + onlyDelegateCalls + returns (WitnetV2.RadonReducer memory) + { + WitnetRequestTemplate _template = __witnetRequest().template; + if (address(_template) != address(0)) { + return _template.lookupRadonTally(); + } else { + return registry.lookupRadonReducer( + __witnetRequestTemplate().tallyHash + ); + } + } + + function settleArgs(string[][] memory _args) + virtual override + external + onlyOnTemplates + returns (WitnetRequest request) + { + WitnetRequestTemplateSlot storage __data = __witnetRequestTemplate(); + bytes32 _radHash = registry.verifyRadonRetrieval( + __data.resultDataType, + __data.resultDataMaxSize, + __data.sources, + _args, + __data.aggregatorHash, + __data.tallyHash + ); + request = WitnetRequestFactory( + _cloneDeterministic(_radHash) + ).initializeWitnetRequest( + _args, + _radHash + ); + emit WitnetRequestTemplateSettled(request, _radHash, _args); + } +} \ No newline at end of file diff --git a/contracts/data/WitnetRequestFactoryData.sol b/contracts/data/WitnetRequestFactoryData.sol new file mode 100644 index 000000000..e52739cb6 --- /dev/null +++ b/contracts/data/WitnetRequestFactoryData.sol @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.7.0 <0.9.0; +pragma experimental ABIEncoderV2; + +import "../requests/WitnetRequestTemplate.sol"; + +contract WitnetRequestFactoryData { + + bytes32 internal constant _WITNET_REQUEST_SLOTHASH = + /* keccak256("io.witnet.data.request") */ + 0xbf9e297db5f64cdb81cd821e7ad085f56008e0c6100f4ebf5e41ef6649322034; + + bytes32 internal constant _WITNET_REQUEST_FACTORY_SLOTHASH = + /* keccak256("io.witnet.data.request.factory") */ + 0xfaf45a8ecd300851b566566df52ca7611b7a56d24a3449b86f4e21c71638e642; + + bytes32 internal constant _WITNET_REQUEST_TEMPLATE_SLOTHASH = + /* keccak256("io.witnet.data.request.template") */ + 0x50402db987be01ecf619cd3fb022cf52f861d188e7b779dd032a62d082276afb; + + struct Slot { + address owner; + address pendingOwner; + } + + struct WitnetRequestSlot { + /// Array of string arguments passed upon initialization. + string[][] args; + /// Witnet Data Request bytecode after inserting string arguments. + bytes bytecode; + /// SHA-256 hash of the Witnet Data Request bytecode. + bytes32 hash; + /// Radon RAD hash. + bytes32 radHash; + /// Radon SLA hash. + bytes32 slaHash; + /// Parent WitnetRequestTemplate contract. + WitnetRequestTemplate template; + } + + struct WitnetRequestTemplateSlot { + /// @notice Array of sources hashes passed upon construction. + bytes32[] sources; + /// @notice Result data type. + WitnetV2.RadonDataTypes resultDataType; + /// @notice Result max size or rank (if variable type). + uint16 resultDataMaxSize; + /// @notice Aggregator reducer hash. + bytes32 aggregatorHash; + /// @notice Tally reducer hash. + bytes32 tallyHash; + } + + function __witnetRequestFactory() + internal pure + returns (Slot storage ptr) + { + assembly { + ptr.slot := _WITNET_REQUEST_FACTORY_SLOTHASH + } + } + + function __witnetRequest() + internal pure + returns (WitnetRequestSlot storage ptr) + { + assembly { + ptr.slot := _WITNET_REQUEST_SLOTHASH + } + } + + function __witnetRequestTemplate() + internal pure + returns (WitnetRequestTemplateSlot storage ptr) + { + assembly { + ptr.slot := _WITNET_REQUEST_TEMPLATE_SLOTHASH + } + } +} \ No newline at end of file From 1b6536a5bb451540349cfddbf69e2d1668674381 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 16 Feb 2023 14:16:16 +0100 Subject: [PATCH 084/119] chore: add migration scripts for WitnetRequestFactory --- contracts/apps/WitnetRequestFactory.sol | 3 -- migrations/scripts/4_deploy_bytecodes.js | 32 +++++------ migrations/scripts/5_deploy_factory.js | 68 ++++++++++++++++++++++++ scripts/utils.js | 10 ++++ 4 files changed, 91 insertions(+), 22 deletions(-) create mode 100644 migrations/scripts/5_deploy_factory.js diff --git a/contracts/apps/WitnetRequestFactory.sol b/contracts/apps/WitnetRequestFactory.sol index 0073616f2..55b3c378f 100644 --- a/contracts/apps/WitnetRequestFactory.sol +++ b/contracts/apps/WitnetRequestFactory.sol @@ -199,7 +199,6 @@ contract WitnetRequestFactory function pendingOwner() public view virtual override - onlyOnFactory returns (address) { return __witnetRequestFactory().pendingOwner; @@ -209,7 +208,6 @@ contract WitnetRequestFactory function owner() public view virtual override - onlyOnFactory returns (address) { return __witnetRequestFactory().owner; @@ -220,7 +218,6 @@ contract WitnetRequestFactory function transferOwnership(address _newOwner) public virtual override - onlyOnFactory onlyOwner { __witnetRequestFactory().pendingOwner = _newOwner; diff --git a/migrations/scripts/4_deploy_bytecodes.js b/migrations/scripts/4_deploy_bytecodes.js index 7552109c6..f9323c588 100644 --- a/migrations/scripts/4_deploy_bytecodes.js +++ b/migrations/scripts/4_deploy_bytecodes.js @@ -1,9 +1,8 @@ -const fs = require("fs") - const addresses = require("../witnet.addresses") const thePackage = require("../../package") const utils = require("../../scripts/utils") +const WitnetBytecodes = artifacts.require("WitnetBytecodes") const WitnetBytecodesProxy = artifacts.require("WitnetProxy") const WitnetBytecodesImplementation = artifacts.require("WitnetBytecodes") const WitnetEncodingLib = artifacts.require("WitnetEncodingLib") @@ -25,12 +24,13 @@ module.exports = async function (deployer, network, accounts) { proxy = await WitnetBytecodesProxy.deployed() addresses[ecosystem][network].WitnetBytecodes = proxy.address if (!isDryRun) { - saveAddresses(addresses) + utils.saveAddresses(addresses) } } else { proxy = await WitnetBytecodesProxy.at(addresses[ecosystem][network].WitnetBytecodes) - console.info(` Skipped: 'WitnetBytecodesProxy' deployed at ${proxy.address}`) + console.info(` Skipped: 'WitnetBytecodes' deployed at ${proxy.address}`) } + WitnetBytecodes.address = proxy.address let bytecodes if ( @@ -41,7 +41,7 @@ module.exports = async function (deployer, network, accounts) { await deployer.deploy(WitnetEncodingLib) addresses[ecosystem][network].WitnetEncodingLib = WitnetEncodingLib.address if (!isDryRun) { - saveAddresses(addresses) + utils.saveAddresses(addresses) } } else { WitnetEncodingLib.address = addresses[ecosystem][network].WitnetEncodingLib @@ -60,7 +60,7 @@ module.exports = async function (deployer, network, accounts) { bytecodes = await WitnetBytecodesImplementation.deployed() addresses[ecosystem][network].WitnetBytecodesImplementation = bytecodes.address if (!isDryRun) { - saveAddresses(addresses) + utils.saveAddresses(addresses) } } else { bytecodes = await WitnetBytecodesImplementation.at(addresses[ecosystem][network].WitnetBytecodesImplementation) @@ -70,11 +70,13 @@ module.exports = async function (deployer, network, accounts) { const implementation = await proxy.implementation() if (implementation.toLowerCase() !== bytecodes.address.toLowerCase()) { console.info() + console.info(" > WitnetBytecodes proxy:", proxy.address) + console.info(" > WitnetBytecodes implementation:", implementation) console.info(" > WitnetBytecodesImplementation:", bytecodes.address, `(v${await bytecodes.version()})`) - console.info(" > WitnetBytecodesProxy:", proxy.address) - console.info(" > WitnetBytecodesProxy.implementation::", implementation) - const answer = await utils.prompt(" > Upgrade the proxy ? [y/N] ") - if (["y", "yes"].includes(answer.toLowerCase().trim())) { + if ( + isDryRun + || ["y", "yes"].includes((await utils.prompt(" > Upgrade the proxy ? [y/N] ")).toLowerCase().trim()) + ) { await proxy.upgradeTo(bytecodes.address, "0x") console.info(" > Done.") } else { @@ -82,12 +84,4 @@ module.exports = async function (deployer, network, accounts) { } } } -} - -function saveAddresses (addrs) { - fs.writeFileSync( - "./migrations/witnet.addresses.json", - JSON.stringify(addrs, null, 4), - { flag: "w+" } - ) -} +} \ No newline at end of file diff --git a/migrations/scripts/5_deploy_factory.js b/migrations/scripts/5_deploy_factory.js new file mode 100644 index 000000000..4e9ce4534 --- /dev/null +++ b/migrations/scripts/5_deploy_factory.js @@ -0,0 +1,68 @@ +const addresses = require("../witnet.addresses") +const thePackage = require("../../package") +const utils = require("../../scripts/utils") + +const WitnetBytecodes = artifacts.require("WitnetBytecodes") +const WitnetRequestFactory = artifacts.require("WitnetProxy") +const WitnetRequestFactoryImplementation = artifacts.require("WitnetRequestFactory") + +module.exports = async function (deployer, network, accounts) { + if (network !== "test") { + const isDryRun = network.split("-")[1] === "fork" || network.split("-")[0] === "develop" + const ecosystem = utils.getRealmNetworkFromArgs()[0] + network = network.split("-")[0] + + if (!addresses[ecosystem]) addresses[ecosystem] = {} + if (!addresses[ecosystem][network]) addresses[ecosystem][network] = {} + + console.info() + + let proxy + if (utils.isNullAddress(addresses[ecosystem][network]?.WitnetRequestFactory)) { + await deployer.deploy(WitnetRequestFactory) + proxy = await WitnetRequestFactory.deployed() + addresses[ecosystem][network].WitnetRequestFactory = proxy.address + if (!isDryRun) { + utils.saveAddresses(addresses) + } + } else { + proxy = await WitnetRequestFactory.at(addresses[ecosystem][network].WitnetRequestFactory) + console.info(` Skipped: 'WitnetRequestFactory' deployed at ${proxy.address}`) + } + + let factory + if (utils.isNullAddress(addresses[ecosystem][network]?.WitnetRequestFactoryImplementation)) { + await deployer.deploy( + WitnetRequestFactoryImplementation, + addresses[ecosystem][network].WitnetBytecodes || WitnetBytecodes.address, + true, + utils.fromAscii(thePackage.version) + ) + factory = await WitnetRequestFactoryImplementation.deployed() + addresses[ecosystem][network].WitnetRequestFactoryImplementation = factory.address + if (!isDryRun) { + utils.saveAddresses(addresses) + } + } else { + bytecodes = await WitnetRequestFactoryImplementation.at(addresses[ecosystem][network].WitnetRequestFactoryImplementation) + console.info(` Skipped: 'WitnetRequestFactoryImplementation' deployed at ${factory.address}`) + } + + const implementation = await proxy.implementation() + if (implementation.toLowerCase() !== factory.address.toLowerCase()) { + console.info() + console.info(" > WitnetRequestFactory proxy:", proxy.address) + console.info(" > WitnetRequestFactory implementation:", implementation) + console.info(" > WitnetRequestFactoryImplementation:", factory.address, `(v${await factory.version()})`) + if ( + isDryRun + || ["y", "yes"].includes((await utils.prompt(" > Upgrade the proxy ? [y/N] ")).toLowerCase().trim()) + ) { + await proxy.upgradeTo(factory.address, "0x") + console.info(" > Done.") + } else { + console.info(" > Not upgraded.") + } + } + } +} \ No newline at end of file diff --git a/scripts/utils.js b/scripts/utils.js index 6b9053fbb..4781c5ebc 100644 --- a/scripts/utils.js +++ b/scripts/utils.js @@ -1,3 +1,4 @@ +const fs = require("fs") require("dotenv").config() const readline = require("readline") const web3 = require("web3") @@ -8,6 +9,7 @@ module.exports = { getRealmNetworkFromString, isNullAddress, prompt, + saveAddresses, } function fromAscii (str) { @@ -92,3 +94,11 @@ async function prompt (text) { }) return answer } + +function saveAddresses (addrs) { + fs.writeFileSync( + "./migrations/witnet.addresses.json", + JSON.stringify(addrs, null, 4), + { flag: "w+" } + ) +} \ No newline at end of file From 16cf17c8802aab8217b20fbc7c043decf9be2f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 16 Feb 2023 14:30:08 +0100 Subject: [PATCH 085/119] chore: bump package version to 0.6.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 60b49eeb7..eea63a920 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "witnet-solidity-bridge", - "version": "0.6.3", + "version": "0.6.4", "description": "Witnet Solidity Bridge contracts for EVM-compatible chains", "main": "", "scripts": { From 6bbb6d4d4bc5e69e6ada6108c30ecf12459675d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 16 Feb 2023 14:30:58 +0100 Subject: [PATCH 086/119] chore: deploy WitnetRequestFactory v0.6.4 on polygon goerli --- migrations/witnet.addresses.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/migrations/witnet.addresses.json b/migrations/witnet.addresses.json index 5c95367f5..2921f748c 100644 --- a/migrations/witnet.addresses.json +++ b/migrations/witnet.addresses.json @@ -284,7 +284,9 @@ "WitnetLib": "0x70767887abBF23291B24620dE6bcEBe8d8F1e6F3", "WitnetPriceRouter": "0x6d5544ca5b35bf2e7a78ace4E7B8d191fe5C9FAb", "WitnetRandomness": "0x0178287DfECA2F13F342512282bc8B59e774aE43", - "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" + "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431", + "WitnetRequestFactory": "0xeDc3F1551d2BFFEB0CD38f51EBd41b6767C0E941", + "WitnetRequestFactoryImplementation": "0xa1EB9B4d24Be7BEea00A2678E384B4f48F7157D0" }, "polygon.mainnet": { "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", From 2d7d8f03a7e051f012f50e619256070336ea393f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 16 Feb 2023 14:38:49 +0100 Subject: [PATCH 087/119] chore: deploy WitnetBytecodes WitnetRequestFactory on moonbase --- migrations/witnet.addresses.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/migrations/witnet.addresses.json b/migrations/witnet.addresses.json index 2921f748c..d8a54cebc 100644 --- a/migrations/witnet.addresses.json +++ b/migrations/witnet.addresses.json @@ -233,7 +233,12 @@ "WitnetLib": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", "WitnetPriceRouter": "0x56834Ff8D4b27db647Da97CA3bd8540f7fA0e89D", "WitnetRandomness": "0x45111778a7db1356DaAB576cBe73681F0745182c", - "WitnetRequestBoard": "0x02Cd4089679EAA9431a88170fd784e7dE78A2425" + "WitnetRequestBoard": "0x02Cd4089679EAA9431a88170fd784e7dE78A2425", + "WitnetBytecodes": "0x329944fabe84b138F54aF0B43ca78bd6Cb54DC34", + "WitnetEncodingLib": "0x284981e5AF6023419bC089a3b2DdF4b354Cb506b", + "WitnetBytecodesImplementation": "0x3bc77B24b5fd54dfa08318cEF599a67aAAea28be", + "WitnetRequestFactory": "0x34F14Fa1b0a6B6FBd1D9d4A62c77C4D0F9d1E9a9", + "WitnetRequestFactoryImplementation": "0x2015B9853A546Dae191a87135B08c29357DD9377" }, "moonbeam.moonriver": { "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", From 45bcdc669bf1674743bae9be70fc82ced9bb4498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 17 Feb 2023 06:37:05 +0100 Subject: [PATCH 088/119] chore(factory): re-arrange interfaces --- contracts/apps/WitnetRequestFactory.sol | 19 +++--------- .../interfaces/V2/IWitnetRequestFactory.sol | 30 +++++++++++++++++++ contracts/requests/WitnetRequest.sol | 23 ++++++++++++++ contracts/requests/WitnetRequestTemplate.sol | 21 ++----------- 4 files changed, 60 insertions(+), 33 deletions(-) create mode 100644 contracts/interfaces/V2/IWitnetRequestFactory.sol create mode 100644 contracts/requests/WitnetRequest.sol diff --git a/contracts/apps/WitnetRequestFactory.sol b/contracts/apps/WitnetRequestFactory.sol index 55b3c378f..74226732c 100644 --- a/contracts/apps/WitnetRequestFactory.sol +++ b/contracts/apps/WitnetRequestFactory.sol @@ -7,33 +7,22 @@ import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; import "../data/WitnetRequestFactoryData.sol"; import "../impls/WitnetUpgradableBase.sol"; -import "../interfaces/V2/IWitnetBytecodes.sol"; import "../patterns/Clonable.sol"; -import "../requests/WitnetRequestTemplate.sol"; - -interface IWitnetRequestFactory { - event WitnetRequestTemplateBuilt(WitnetRequestTemplate template); - function build( - bytes32[] memory sourcesIds, - bytes32 aggregatorId, - bytes32 tallyId, - uint16 resultDataMaxSize - ) external returns (WitnetRequestTemplate _template); -} +import "../interfaces/V2/IWitnetRequestFactory.sol"; contract WitnetRequestFactory is Clonable, IWitnetRequestFactory, WitnetRequest, - WitnetRequestFactoryData, WitnetRequestTemplate, - WitnetUpgradableBase + WitnetRequestFactoryData, + WitnetUpgradableBase { using ERC165Checker for address; /// @notice Reference to Witnet Data Requests Bytecode Registry - IWitnetBytecodes immutable public registry; + IWitnetBytecodes immutable public override registry; modifier onlyDelegateCalls override(Clonable, Upgradeable) { require( diff --git a/contracts/interfaces/V2/IWitnetRequestFactory.sol b/contracts/interfaces/V2/IWitnetRequestFactory.sol new file mode 100644 index 000000000..9a3d19b01 --- /dev/null +++ b/contracts/interfaces/V2/IWitnetRequestFactory.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.7.0 <0.9.0; + +import "./IWitnetBytecodes.sol"; +import "../../requests/WitnetRequest.sol"; + +interface IWitnetRequestFactory { + + event WitnetRequestBuilt(WitnetRequest request); + event WitnetRequestTemplateBuilt(WitnetRequestTemplate template, bool parameterized); + + function buildRequest( + bytes32[] memory sourcesIds, + bytes32 aggregatorId, + bytes32 tallyId, + uint16 resultDataMaxSize + ) external returns (WitnetRequest request); + + function buildRequestTemplate( + bytes32[] memory sourcesIds, + bytes32 aggregatorId, + bytes32 tallyId, + uint16 resultDataMaxSize + ) external returns (WitnetRequestTemplate template); + + function class() external view returns (bytes4); + function registry() external view returns (IWitnetBytecodes); + +} \ No newline at end of file diff --git a/contracts/requests/WitnetRequest.sol b/contracts/requests/WitnetRequest.sol new file mode 100644 index 000000000..42c323972 --- /dev/null +++ b/contracts/requests/WitnetRequest.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.7.0 <0.9.0; +pragma experimental ABIEncoderV2; + +import "./WitnetRequestTemplate.sol"; +import "../libs/WitnetV2.sol"; + +abstract contract WitnetRequest + is + IWitnetRequest +{ + event WitnetRequestSettled(WitnetV2.RadonSLA sla); + + function args() virtual external view returns (string[][] memory); + function class() virtual external view returns (bytes4); + function getRadonSLA() virtual external view returns (WitnetV2.RadonSLA memory); + function initialized() virtual external view returns (bool); + function radHash() virtual external view returns (bytes32); + function slaHash() virtual external view returns (bytes32); + function template() virtual external view returns (WitnetRequestTemplate); + function modifySLA(WitnetV2.RadonSLA calldata sla) virtual external; +} \ No newline at end of file diff --git a/contracts/requests/WitnetRequestTemplate.sol b/contracts/requests/WitnetRequestTemplate.sol index a2648a2cf..b17982ea5 100644 --- a/contracts/requests/WitnetRequestTemplate.sol +++ b/contracts/requests/WitnetRequestTemplate.sol @@ -3,31 +3,15 @@ pragma solidity >=0.7.0 <0.9.0; pragma experimental ABIEncoderV2; +import "./WitnetRequest.sol"; import "../libs/WitnetV2.sol"; -abstract contract WitnetRequest - is - IWitnetRequest -{ - event WitnetRequestSettled(WitnetV2.RadonSLA sla); - - function args() virtual external view returns (string[][] memory); - function getRadonSLA() virtual external view returns (WitnetV2.RadonSLA memory); - function initialized() virtual external view returns (bool); - function radHash() virtual external view returns (bytes32); - function slaHash() virtual external view returns (bytes32); - function template() virtual external view returns (WitnetRequestTemplate); - function modifySLA(WitnetV2.RadonSLA calldata sla) virtual external; -} - abstract contract WitnetRequestTemplate - is - IWitnetRequest { event WitnetRequestTemplateSettled(WitnetRequest indexed request, bytes32 indexed radHash, string[][] args); + function class() virtual external view returns (bytes4); function getDataSources() virtual external view returns (bytes32[] memory); - function getDataSourcesArgsCount() virtual external view returns (uint8[] memory); function getDataSourcesCount() virtual external view returns (uint256); function getRadonAggregatorHash() virtual external view returns (bytes32); function getRadonTallyHash() virtual external view returns (bytes32); @@ -36,5 +20,6 @@ abstract contract WitnetRequestTemplate function lookupDataSourceByIndex(uint256) virtual external view returns (WitnetV2.DataSource memory); function lookupRadonAggregator() virtual external view returns (WitnetV2.RadonReducer memory); function lookupRadonTally() virtual external view returns (WitnetV2.RadonReducer memory); + function parameterized() virtual external view returns (bool); function settleArgs(string[][] calldata args) virtual external returns (WitnetRequest); } \ No newline at end of file From 8fea3980a618c70a21f245b9c4e1f0371ee24de7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 17 Feb 2023 06:41:44 +0100 Subject: [PATCH 089/119] feat(factory): support whether data sources require parameters or not --- contracts/apps/WitnetRequestFactory.sol | 90 ++++++++++++++++----- contracts/data/WitnetRequestFactoryData.sol | 6 +- 2 files changed, 72 insertions(+), 24 deletions(-) diff --git a/contracts/apps/WitnetRequestFactory.sol b/contracts/apps/WitnetRequestFactory.sol index 74226732c..a8e7a7207 100644 --- a/contracts/apps/WitnetRequestFactory.sol +++ b/contracts/apps/WitnetRequestFactory.sol @@ -70,31 +70,66 @@ contract WitnetRequestFactory /// =============================================================================================================== /// --- IWitnetRequestFactory implementation ---------------------------------------------------------------------- - function build( - bytes32[] memory sourcesIds, - bytes32 aggregatorId, - bytes32 tallyId, + function buildRequest( + bytes32[] memory sources, + bytes32 aggregator, + bytes32 tally, uint16 resultDataMaxSize ) virtual override external onlyOnFactory + returns (WitnetRequest _request) + { + WitnetRequestTemplate _template = buildRequestTemplate(sources, aggregator, tally, resultDataMaxSize); + require(!_template.parameterized(), "WitnetRequestFactory: parameterized sources"); + _request = _template.settleArgs(abi.decode(hex"",(string[][]))); + emit WitnetRequestBuilt(_request); + } + + function buildRequestTemplate( + bytes32[] memory sources, + bytes32 aggregator, + bytes32 tally, + uint16 resultDataMaxSize + ) + virtual override + public + onlyOnFactory returns (WitnetRequestTemplate _template) { _template = WitnetRequestFactory(_cloneDeterministic( keccak256(abi.encodePacked( - sourcesIds, - aggregatorId, - tallyId, + sources, + aggregator, + tally, resultDataMaxSize )) )).initializeWitnetRequestTemplate( - sourcesIds, - aggregatorId, - tallyId, + sources, + aggregator, + tally, resultDataMaxSize ); - emit WitnetRequestTemplateBuilt(_template); + emit WitnetRequestTemplateBuilt(_template, _template.parameterized()); + } + + function class() + virtual override(IWitnetRequestFactory, WitnetRequest, WitnetRequestTemplate) + external view + returns (bytes4) + { + if (address(this) == _SELF) { + return type(Upgradeable).interfaceId; + } else { + if (address(this) == __proxy()) { + return type(IWitnetRequestFactory).interfaceId; + } else if (__witnetRequest().radHash != bytes32(0)) { + return type(WitnetRequest).interfaceId; + } else { + return type(WitnetRequestTemplate).interfaceId; + } + } } function initializeWitnetRequestTemplate( @@ -109,7 +144,9 @@ contract WitnetRequestFactory { WitnetV2.RadonDataTypes _resultDataType; require(_sources.length > 0, "WitnetRequestTemplate: no sources"); - // check all sources return the same data types + // check all sources return the same data types, + // and whether any of them is parameterized + bool _parameterized; for (uint _ix = 0; _ix < _sources.length; _ix ++) { if (_ix == 0) { _resultDataType = registry.lookupDataSourceResultDataType(_sources[_ix]); @@ -119,6 +156,9 @@ contract WitnetRequestFactory "WitnetRequestTemplate: mismatching sources" ); } + if (!_parameterized) { + _parameterized = registry.lookupDataSourceArgsCount(_sources[_ix]) > 0; + } } // revert if the aggregator reducer is unknown registry.lookupRadonReducer(_aggregatorId); @@ -126,11 +166,12 @@ contract WitnetRequestFactory registry.lookupRadonReducer(_tallyId); { WitnetRequestTemplateSlot storage __data = __witnetRequestTemplate(); - __data.sources = _sources; + __data.parameterized = _parameterized; __data.aggregatorHash = _aggregatorId; __data.tallyHash = _tallyId; __data.resultDataType = _resultDataType; __data.resultDataMaxSize = _resultDataMaxSize; + __data.sources = _sources; } return WitnetRequestTemplate(address(this)); } @@ -407,15 +448,6 @@ contract WitnetRequestFactory } - function getDataSourcesArgsCount() - override - external view - onlyDelegateCalls - returns (uint8[] memory) - { - revert("WitnetRequestFactory: not yet implemented"); - } - function getDataSourcesCount() override external view @@ -538,6 +570,20 @@ contract WitnetRequestFactory } } + function parameterized() + override + external view + onlyDelegateCalls + returns (bool) + { + WitnetRequestTemplate _template = __witnetRequest().template; + if (address(_template) != address(0)) { + return _template.parameterized(); + } else { + return __witnetRequestTemplate().parameterized; + } + } + function settleArgs(string[][] memory _args) virtual override external diff --git a/contracts/data/WitnetRequestFactoryData.sol b/contracts/data/WitnetRequestFactoryData.sol index e52739cb6..11bb37147 100644 --- a/contracts/data/WitnetRequestFactoryData.sol +++ b/contracts/data/WitnetRequestFactoryData.sol @@ -40,8 +40,8 @@ contract WitnetRequestFactoryData { } struct WitnetRequestTemplateSlot { - /// @notice Array of sources hashes passed upon construction. - bytes32[] sources; + /// Whether any of the sources is parameterized. + bool parameterized; /// @notice Result data type. WitnetV2.RadonDataTypes resultDataType; /// @notice Result max size or rank (if variable type). @@ -50,6 +50,8 @@ contract WitnetRequestFactoryData { bytes32 aggregatorHash; /// @notice Tally reducer hash. bytes32 tallyHash; + /// @notice Array of sources hashes passed upon construction. + bytes32[] sources; } function __witnetRequestFactory() From d318e0ecf4db6e4aa53a30e289141727cf340d7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 17 Feb 2023 10:27:41 +0100 Subject: [PATCH 090/119] feat(factory): let unproxified factory be used as such --- contracts/apps/WitnetRequestFactory.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contracts/apps/WitnetRequestFactory.sol b/contracts/apps/WitnetRequestFactory.sol index a8e7a7207..be29b8952 100644 --- a/contracts/apps/WitnetRequestFactory.sol +++ b/contracts/apps/WitnetRequestFactory.sol @@ -64,6 +64,10 @@ contract WitnetRequestFactory "WitnetRequestFactory: uncompliant registry" ); registry = _registry; + // let logic contract be used as a factory, while avoiding further initializations: + __proxiable().proxy = address(this); + __proxiable().implementation = address(this); + __witnetRequestFactory().owner = address(0); } From 5ecaca202b69b29bd14b8bc4baa009c0f12d9920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 17 Feb 2023 14:09:25 +0100 Subject: [PATCH 091/119] refactor(requests): let WitnetRequest.modifySLA(sla) return IWitnetRequest reference to itself --- contracts/apps/WitnetRequestFactory.sol | 12 ++++++++---- contracts/requests/WitnetRequest.sol | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/contracts/apps/WitnetRequestFactory.sol b/contracts/apps/WitnetRequestFactory.sol index be29b8952..1b1cde76e 100644 --- a/contracts/apps/WitnetRequestFactory.sol +++ b/contracts/apps/WitnetRequestFactory.sol @@ -418,6 +418,7 @@ contract WitnetRequestFactory virtual override external onlyDelegateCalls + returns (IWitnetRequest) { WitnetRequestTemplate _template = __witnetRequest().template; require( @@ -427,10 +428,13 @@ contract WitnetRequestFactory bytes32 _slaHash = registry.verifyRadonSLA(_sla); WitnetRequestSlot storage __data = __witnetRequest(); bytes memory _bytecode = registry.bytecodeOf(__data.radHash, _slaHash); - __data.bytecode = _bytecode; - __data.hash = Witnet.hash(_bytecode); - __data.slaHash = _slaHash; - emit WitnetRequestSettled(_sla); + { + __data.bytecode = _bytecode; + __data.hash = Witnet.hash(_bytecode); + __data.slaHash = _slaHash; + emit WitnetRequestSettled(_sla); + } + return IWitnetRequest(address(this)); } diff --git a/contracts/requests/WitnetRequest.sol b/contracts/requests/WitnetRequest.sol index 42c323972..7605bfb97 100644 --- a/contracts/requests/WitnetRequest.sol +++ b/contracts/requests/WitnetRequest.sol @@ -19,5 +19,5 @@ abstract contract WitnetRequest function radHash() virtual external view returns (bytes32); function slaHash() virtual external view returns (bytes32); function template() virtual external view returns (WitnetRequestTemplate); - function modifySLA(WitnetV2.RadonSLA calldata sla) virtual external; + function modifySLA(WitnetV2.RadonSLA calldata sla) virtual external returns (IWitnetRequest); } \ No newline at end of file From e358cd8da36d1bedaa7242f70a7a045bb6695584 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 17 Feb 2023 15:46:25 +0100 Subject: [PATCH 092/119] chore: full upgrade of bytecodes and request factory on polygon goerli --- migrations/scripts/5_deploy_factory.js | 2 +- migrations/witnet.addresses.json | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/migrations/scripts/5_deploy_factory.js b/migrations/scripts/5_deploy_factory.js index 4e9ce4534..56504c758 100644 --- a/migrations/scripts/5_deploy_factory.js +++ b/migrations/scripts/5_deploy_factory.js @@ -44,7 +44,7 @@ module.exports = async function (deployer, network, accounts) { utils.saveAddresses(addresses) } } else { - bytecodes = await WitnetRequestFactoryImplementation.at(addresses[ecosystem][network].WitnetRequestFactoryImplementation) + factory = await WitnetRequestFactoryImplementation.at(addresses[ecosystem][network].WitnetRequestFactoryImplementation) console.info(` Skipped: 'WitnetRequestFactoryImplementation' deployed at ${factory.address}`) } diff --git a/migrations/witnet.addresses.json b/migrations/witnet.addresses.json index d8a54cebc..32fee07ff 100644 --- a/migrations/witnet.addresses.json +++ b/migrations/witnet.addresses.json @@ -283,15 +283,15 @@ }, "polygon": { "polygon.goerli": { - "WitnetBytecodes": "", - "WitnetBytecodesImplementation": "", - "WitnetEncodingLib": "", + "WitnetBytecodes": "0xFe2A44e118844023d453b433674b0933598FEF63", + "WitnetBytecodesImplementation": "0xEd94A03dC73D68124de1683A6474Cbe523416aeB", + "WitnetEncodingLib": "0x0fde3791E85448cACc2Bf1B612C2f07525cE7663", "WitnetLib": "0x70767887abBF23291B24620dE6bcEBe8d8F1e6F3", "WitnetPriceRouter": "0x6d5544ca5b35bf2e7a78ace4E7B8d191fe5C9FAb", "WitnetRandomness": "0x0178287DfECA2F13F342512282bc8B59e774aE43", "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431", - "WitnetRequestFactory": "0xeDc3F1551d2BFFEB0CD38f51EBd41b6767C0E941", - "WitnetRequestFactoryImplementation": "0xa1EB9B4d24Be7BEea00A2678E384B4f48F7157D0" + "WitnetRequestFactory": "0x3921cC3eBF8c8a9d0F788Fc6cD371287EEe636Ff", + "WitnetRequestFactoryImplementation": "0xB39D0710DE982C24fe3AF1F4371FaF6937033f18" }, "polygon.mainnet": { "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", From c7ec1ffc2c65255a6c116c4eb298a9abf5ae6a58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 17 Feb 2023 18:35:00 +0100 Subject: [PATCH 093/119] refactor(libs): RadonSLA param names --- contracts/libs/WitnetEncodingLib.sol | 8 ++++---- contracts/libs/WitnetV2.sol | 6 +++--- contracts/requests/WitnetRequestMalleableBase.sol | 2 +- migrations/jsons/witnet-radon-slas.json | 2 +- test/TestWitnetEncodingLib.sol | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/contracts/libs/WitnetEncodingLib.sol b/contracts/libs/WitnetEncodingLib.sol index 0a8a526d2..f725ca5a5 100644 --- a/contracts/libs/WitnetEncodingLib.sol +++ b/contracts/libs/WitnetEncodingLib.sol @@ -294,9 +294,9 @@ library WitnetEncodingLib { return abi.encodePacked( encode(uint64(sla.witnessReward), bytes1(0x10)), encode(uint64(sla.numWitnesses), bytes1(0x18)), - encode(uint64(sla.commitRevealFee), bytes1(0x20)), + encode(uint64(sla.minerCommitFee), bytes1(0x20)), encode(uint64(sla.minConsensusPercentage), bytes1(0x28)), - encode(uint64(sla.collateral), bytes1(0x30)) + encode(uint64(sla.witnessCollateral), bytes1(0x30)) ); } @@ -475,8 +475,8 @@ library WitnetEncodingLib { ) { revert WitnetV2.RadonSlaConsensusOutOfRange(sla.minConsensusPercentage); } - if (sla.collateral < 10 ** 9) { - revert WitnetV2.RadonSlaLowCollateral(sla.collateral); + if (sla.witnessCollateral < 10 ** 9) { + revert WitnetV2.RadonSlaLowCollateral(sla.witnessCollateral); } } diff --git a/contracts/libs/WitnetV2.sol b/contracts/libs/WitnetV2.sol index d37a1a453..765041ce1 100644 --- a/contracts/libs/WitnetV2.sol +++ b/contracts/libs/WitnetV2.sol @@ -23,7 +23,7 @@ library WitnetV2 { error RadonSlaNoWitnesses(); error RadonSlaTooManyWitnesses(uint256 numWitnesses); error RadonSlaConsensusOutOfRange(uint256 percentage); - error RadonSlaLowCollateral(uint256 collateral); + error RadonSlaLowCollateral(uint256 witnessCollateral); error UnsupportedDataRequestMethod(uint8 method, string schema, string body, string[2][] headers); error UnsupportedRadonDataType(uint8 datatype, uint256 maxlength); @@ -189,9 +189,9 @@ library WitnetV2 { struct RadonSLA { uint64 witnessReward; uint16 numWitnesses; - uint64 commitRevealFee; + uint64 minerCommitFee; uint32 minConsensusPercentage; - uint64 collateral; + uint64 witnessCollateral; } } \ No newline at end of file diff --git a/contracts/requests/WitnetRequestMalleableBase.sol b/contracts/requests/WitnetRequestMalleableBase.sol index 70c9e142a..23d2d8f74 100644 --- a/contracts/requests/WitnetRequestMalleableBase.sol +++ b/contracts/requests/WitnetRequestMalleableBase.sol @@ -313,7 +313,7 @@ abstract contract WitnetRequestMalleableBase ); require( _witnessingCollateral >= 10 ** 9, - "WitnetRequestMalleableBase: witnessing collateral below 1 WIT" + "WitnetRequestMalleableBase: witness collateral below 1 WIT" ); __storage().bytecode = abi.encodePacked( diff --git a/migrations/jsons/witnet-radon-slas.json b/migrations/jsons/witnet-radon-slas.json index 9e44e586b..d98e92397 100644 --- a/migrations/jsons/witnet-radon-slas.json +++ b/migrations/jsons/witnet-radon-slas.json @@ -3,7 +3,7 @@ "hash": "0x", "witnessReward": 1000000, "numWitnesses": 10, - "commitRevealFee": 1000000, + "minerCommitFee": 1000000, "minConsensusPercentage": 51, "collateral": 5000000000 } diff --git a/test/TestWitnetEncodingLib.sol b/test/TestWitnetEncodingLib.sol index b5d401b86..9178ba13a 100644 --- a/test/TestWitnetEncodingLib.sol +++ b/test/TestWitnetEncodingLib.sol @@ -54,11 +54,11 @@ contract TestWitnetEncodingLib { function testEncodeRadonSLA() external { bytes memory bytecode = WitnetEncodingLib.encode( WitnetV2.RadonSLA({ - witnessReward: 1000000, numWitnesses: 10, - commitRevealFee: 1000000, minConsensusPercentage: 51, - collateral: 5000000 + minerCommitFee: 1000000, + witnessCollateral: 5000000, + witnessReward: 1000000 }) ); // emit Log(bytecode); From 5cd1063548362308715c6a9079ca4de98332989b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 17 Feb 2023 19:08:17 +0100 Subject: [PATCH 094/119] fix(factory): use _clone instead of _cloneDeterministic for now --- contracts/apps/WitnetRequestFactory.sol | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/contracts/apps/WitnetRequestFactory.sol b/contracts/apps/WitnetRequestFactory.sol index 1b1cde76e..ea02f2323 100644 --- a/contracts/apps/WitnetRequestFactory.sol +++ b/contracts/apps/WitnetRequestFactory.sol @@ -102,14 +102,7 @@ contract WitnetRequestFactory onlyOnFactory returns (WitnetRequestTemplate _template) { - _template = WitnetRequestFactory(_cloneDeterministic( - keccak256(abi.encodePacked( - sources, - aggregator, - tally, - resultDataMaxSize - )) - )).initializeWitnetRequestTemplate( + _template = WitnetRequestFactory(_clone()).initializeWitnetRequestTemplate( sources, aggregator, tally, @@ -607,9 +600,7 @@ contract WitnetRequestFactory __data.aggregatorHash, __data.tallyHash ); - request = WitnetRequestFactory( - _cloneDeterministic(_radHash) - ).initializeWitnetRequest( + request = WitnetRequestFactory(_clone()).initializeWitnetRequest( _args, _radHash ); From 30c0ec03797843d8746ec2720f2620a67dec9edb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Sat, 18 Feb 2023 19:00:30 +0100 Subject: [PATCH 095/119] refactor(libs): turn all RadonSLA fields into uint --- contracts/impls/bytecodes/WitnetBytecodes.sol | 2 +- contracts/interfaces/V2/IWitnetBytecodes.sol | 2 +- contracts/libs/WitnetV2.sol | 10 +++++----- migrations/witnet.addresses.json | 14 +++++++------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol index 179eff711..97d519bef 100644 --- a/contracts/impls/bytecodes/WitnetBytecodes.sol +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -383,7 +383,7 @@ contract WitnetBytecodes function lookupRadonSLAReward(bytes32 _slaHash) public view override - returns (uint64) + returns (uint) { WitnetV2.RadonSLA storage __sla = __database().slas[_slaHash]; return __sla.numWitnesses * __sla.witnessReward; diff --git a/contracts/interfaces/V2/IWitnetBytecodes.sol b/contracts/interfaces/V2/IWitnetBytecodes.sol index a89b0d628..9733c20d1 100644 --- a/contracts/interfaces/V2/IWitnetBytecodes.sol +++ b/contracts/interfaces/V2/IWitnetBytecodes.sol @@ -41,7 +41,7 @@ interface IWitnetBytecodes { function lookupRadonRetrievalSourcesCount(bytes32 hash) external view returns (uint); function lookupRadonRetrievalTally(bytes32 hash) external view returns (WitnetV2.RadonReducer memory); function lookupRadonSLA(bytes32 hash) external view returns (WitnetV2.RadonSLA memory); - function lookupRadonSLAReward(bytes32 hash) external view returns (uint64); + function lookupRadonSLAReward(bytes32 hash) external view returns (uint); function verifyDataSource( WitnetV2.DataRequestMethods requestMethod, diff --git a/contracts/libs/WitnetV2.sol b/contracts/libs/WitnetV2.sol index 765041ce1..5a8a2c850 100644 --- a/contracts/libs/WitnetV2.sol +++ b/contracts/libs/WitnetV2.sol @@ -187,11 +187,11 @@ library WitnetV2 { } struct RadonSLA { - uint64 witnessReward; - uint16 numWitnesses; - uint64 minerCommitFee; - uint32 minConsensusPercentage; - uint64 witnessCollateral; + uint numWitnesses; + uint minConsensusPercentage; + uint witnessReward; + uint witnessCollateral; + uint minerCommitFee; } } \ No newline at end of file diff --git a/migrations/witnet.addresses.json b/migrations/witnet.addresses.json index 32fee07ff..9817ed830 100644 --- a/migrations/witnet.addresses.json +++ b/migrations/witnet.addresses.json @@ -230,15 +230,15 @@ }, "moonbeam": { "moonbeam.moonbase": { + "WitnetBytecodes": "", + "WitnetBytecodesImplementation": "", + "WitnetEncodingLib": "", "WitnetLib": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", "WitnetPriceRouter": "0x56834Ff8D4b27db647Da97CA3bd8540f7fA0e89D", "WitnetRandomness": "0x45111778a7db1356DaAB576cBe73681F0745182c", "WitnetRequestBoard": "0x02Cd4089679EAA9431a88170fd784e7dE78A2425", - "WitnetBytecodes": "0x329944fabe84b138F54aF0B43ca78bd6Cb54DC34", - "WitnetEncodingLib": "0x284981e5AF6023419bC089a3b2DdF4b354Cb506b", - "WitnetBytecodesImplementation": "0x3bc77B24b5fd54dfa08318cEF599a67aAAea28be", "WitnetRequestFactory": "0x34F14Fa1b0a6B6FBd1D9d4A62c77C4D0F9d1E9a9", - "WitnetRequestFactoryImplementation": "0x2015B9853A546Dae191a87135B08c29357DD9377" + "WitnetRequestFactoryImplementation": "" }, "moonbeam.moonriver": { "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", @@ -283,15 +283,15 @@ }, "polygon": { "polygon.goerli": { - "WitnetBytecodes": "0xFe2A44e118844023d453b433674b0933598FEF63", - "WitnetBytecodesImplementation": "0xEd94A03dC73D68124de1683A6474Cbe523416aeB", + "WitnetBytecodes": "", + "WitnetBytecodesImplementation": "", "WitnetEncodingLib": "0x0fde3791E85448cACc2Bf1B612C2f07525cE7663", "WitnetLib": "0x70767887abBF23291B24620dE6bcEBe8d8F1e6F3", "WitnetPriceRouter": "0x6d5544ca5b35bf2e7a78ace4E7B8d191fe5C9FAb", "WitnetRandomness": "0x0178287DfECA2F13F342512282bc8B59e774aE43", "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431", "WitnetRequestFactory": "0x3921cC3eBF8c8a9d0F788Fc6cD371287EEe636Ff", - "WitnetRequestFactoryImplementation": "0xB39D0710DE982C24fe3AF1F4371FaF6937033f18" + "WitnetRequestFactoryImplementation": "" }, "polygon.mainnet": { "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", From f9616db5a4054b4a544f280aa504be23de76326b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Sun, 19 Feb 2023 00:18:04 +0100 Subject: [PATCH 096/119] chore: drop alphas --- contracts/WitnetRequestBoardV2.sol | 25 - contracts/data/WitnetReporting1Data.sol | 112 ---- contracts/data/WitnetRequestBoardV2Data.sol | 185 ------ .../WitnetRequestBoardTrustlessBase.sol | 614 ------------------ .../WitnetRequestBoardTrustlessReporting1.sol | 280 -------- contracts/interfaces/V2/IWitnetBlocks.sol | 33 - contracts/interfaces/V2/IWitnetDecoder.sol | 103 --- contracts/interfaces/V2/IWitnetEncoder.sol | 12 - contracts/interfaces/V2/IWitnetReporting1.sol | 40 -- .../interfaces/V2/IWitnetReporting1Admin.sol | 13 - contracts/interfaces/V2/IWitnetRequests.sol | 67 -- .../interfaces/V2/IWitnetRequestsAdmin.sol | 17 - contracts/interfaces/V2/IWitnetTraps.sol | 30 - 13 files changed, 1531 deletions(-) delete mode 100644 contracts/WitnetRequestBoardV2.sol delete mode 100644 contracts/data/WitnetReporting1Data.sol delete mode 100644 contracts/data/WitnetRequestBoardV2Data.sol delete mode 100644 contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol delete mode 100644 contracts/impls/boards/trustless/WitnetRequestBoardTrustlessReporting1.sol delete mode 100644 contracts/interfaces/V2/IWitnetBlocks.sol delete mode 100644 contracts/interfaces/V2/IWitnetDecoder.sol delete mode 100644 contracts/interfaces/V2/IWitnetEncoder.sol delete mode 100644 contracts/interfaces/V2/IWitnetReporting1.sol delete mode 100644 contracts/interfaces/V2/IWitnetReporting1Admin.sol delete mode 100644 contracts/interfaces/V2/IWitnetRequests.sol delete mode 100644 contracts/interfaces/V2/IWitnetRequestsAdmin.sol delete mode 100644 contracts/interfaces/V2/IWitnetTraps.sol diff --git a/contracts/WitnetRequestBoardV2.sol b/contracts/WitnetRequestBoardV2.sol deleted file mode 100644 index f8df0246a..000000000 --- a/contracts/WitnetRequestBoardV2.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.8.0 <0.9.0; - -import "./interfaces/V2/IWitnetBlocks.sol"; -import "./interfaces/V2/IWitnetBytecodes.sol"; -import "./interfaces/V2/IWitnetDecoder.sol"; - -// import "./interfaces/V2/IWitnetReporting.sol"; -import "./interfaces/V2/IWitnetRequests.sol"; -//import "./interfaces/V2/IWitnetTraps.sol"; - -/// @title Witnet Request Board V2 functionality base contract. -/// @author The Witnet Foundation. -abstract contract WitnetRequestBoardV2 is - IWitnetRequests - //, IWitnetReporting - //, IWitnetTraps -{ - function blocks() virtual external view returns (IWitnetBlocks); - - function bytecodes() virtual external view returns (IWitnetBytecodes); - - function decoder() virtual external view returns (IWitnetDecoder); -} diff --git a/contracts/data/WitnetReporting1Data.sol b/contracts/data/WitnetReporting1Data.sol deleted file mode 100644 index 3fe7940e2..000000000 --- a/contracts/data/WitnetReporting1Data.sol +++ /dev/null @@ -1,112 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.8.0 <0.9.0; - -import "../interfaces/V2/IWitnetReporting1.sol"; -import "../interfaces/V2/IWitnetReporting1Admin.sol"; - -/// @title Witnet Request Board base data model. -/// @author The Witnet Foundation. -abstract contract WitnetReporting1Data - is - IWitnetReporting1, - IWitnetReporting1Admin -{ - - bytes32 internal constant _WITNET_REPORTING_1_DATA_SLOTHASH = - /* keccak256("io.witnet.boards.data.v2.reporting.1") */ - 0x32ecea6ea7fbc6d7e8c8041c5ecf898bf8d40bd92da1207bebee19461a94c7bd; - - struct Escrow { - uint256 index; - uint256 weiSignUpFee; - uint256 lastSignUpBlock; - uint256 lastSignOutBlock; - uint256 lastSlashBlock; - } - - struct Reporting { - uint256 totalReporters; - IWitnetReporting1.SignUpConfig settings; - address[] reporters; - mapping (address => Escrow) escrows; - } - - constructor() { - __reporting().settings = IWitnetReporting1.SignUpConfig({ - weiRejectionFee: 0.01 ether, - weiSignUpFee: 0.1 ether, - acceptanceBlocks: 32, - banningBlocks: 256, - exitBlocks: 64 - }); - } - - // --- Internal view functions - - - - // ================================================================================================================ - // --- Internal state-modifying functions ------------------------------------------------------------------------- - - function __deleteReporterAddressByIndex(uint _index) - internal - returns (uint256 _totalReporters) - { - _totalReporters = __reporting().totalReporters; - if (_index >= _totalReporters) { - revert WitnetV2.IndexOutOfBounds(_index, _totalReporters); - } - else if (_totalReporters > 1 && _index < _totalReporters - 1) { - address _latestReporterAddress = __reporting().reporters[_totalReporters - 1]; - Escrow storage __latestReporterEscrow = __reporting().escrows[_latestReporterAddress]; - __latestReporterEscrow.index = _index; - __reporting().reporters[_index] = _latestReporterAddress; - } - __reporting().reporters.pop(); - __reporting().totalReporters = -- _totalReporters; - } - - function __pushReporterAddress(address _reporterAddr) - internal - returns (uint256 _totalReporters) - { - __reporting().reporters.push(_reporterAddr); - return ++ __reporting().totalReporters; - } - - /// @dev Returns storage pointer to contents of 'Board' struct. - function __reporting() - internal pure - returns (Reporting storage _ptr) - { - assembly { - _ptr.slot := _WITNET_REPORTING_1_DATA_SLOTHASH - } - } - - /// @dev Slash given reporter, after checking slashing conditions for sender are met. - function __slashSignedUpReporter(address _reporter) - internal - virtual - returns (uint256 _weiValue) - { - WitnetReporting1Data.Escrow storage __escrow = __reporting().escrows[_reporter]; - if (__escrow.weiSignUpFee > 0) { - if ( - __escrow.lastSignOutBlock < __escrow.lastSignUpBlock - || block.number < __escrow.lastSignUpBlock + __reporting().settings.banningBlocks - ) { - _weiValue = __escrow.weiSignUpFee; - __escrow.weiSignUpFee = 0; - __escrow.lastSlashBlock = block.number; - emit Slashed( - _reporter, - _weiValue, - __deleteReporterAddressByIndex(__escrow.index) - ); - } - } - } - -} \ No newline at end of file diff --git a/contracts/data/WitnetRequestBoardV2Data.sol b/contracts/data/WitnetRequestBoardV2Data.sol deleted file mode 100644 index f28296b8c..000000000 --- a/contracts/data/WitnetRequestBoardV2Data.sol +++ /dev/null @@ -1,185 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.8.0 <0.9.0; - -import "@openzeppelin/contracts/utils/Strings.sol"; -import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; - -import "../interfaces/V2/IWitnetBlocks.sol"; -import "../interfaces/V2/IWitnetBytecodes.sol"; -import "../interfaces/V2/IWitnetDecoder.sol"; -import "../interfaces/V2/IWitnetRequests.sol"; -import "../interfaces/V2/IWitnetRequestsAdmin.sol"; -import "../patterns/Payable.sol"; - -/// @title Witnet Request Board base data model. -/// @author The Witnet Foundation. -abstract contract WitnetRequestBoardV2Data - is - ERC165, - Payable, - IWitnetRequestsAdmin -{ - using Strings for address; - using Strings for uint256; - - bytes32 internal constant _WITNET_REQUEST_BOARD_DATA_SLOTHASH = - /* keccak256("io.witnet.boards.data.v2") */ - 0xfeed002ff8a708dcba69bac2a8e829fd61fee551b9e9fc0317707d989cb0fe53; - - struct Board { - address base; - address owner; - address pendingOwner; - - IWitnetBlocks blocks; - IWitnetBytecodes bytecodes; - IWitnetDecoder decoder; - - IWitnetRequests.Stats serviceStats; - bytes4 serviceTag; - - mapping (bytes32 => WitnetV2.DrPost) posts; - } - - constructor() { - __board().owner = msg.sender; - } - - /// Asserts the given query is currently in the given status. - modifier drPostInStatus(bytes32 _drHash, WitnetV2.DrPostStatus _requiredStatus) { - WitnetV2.DrPostStatus _currentStatus = _getDrPostStatus(_drHash); - if (_currentStatus != _requiredStatus) { - revert IWitnetRequests.DrPostNotInStatus( - _drHash, - _currentStatus, - _requiredStatus - ); - } - _; - } - - /// Asserts the given query was previously posted and that it was not yet deleted. - modifier drPostNotDeleted(bytes32 _drHash) { - WitnetV2.DrPostStatus _currentStatus = __drPost(_drHash).status; - if (uint(_currentStatus) <= uint(WitnetV2.DrPostStatus.Deleted)) { - revert IWitnetRequests.DrPostBadMood(_drHash, _currentStatus); - } - _; - } - - - // ================================================================================================================ - // --- Internal functions ----------------------------------------------------------------------------------------- - - /// Returns storage pointer to contents of 'Board' struct. - function __board() - internal pure - returns (Board storage _ptr) - { - assembly { - _ptr.slot := _WITNET_REQUEST_BOARD_DATA_SLOTHASH - } - } - - function _canDrPostBeDeletedFrom(bytes32 _drHash, address _from) - internal view - virtual - returns (bool _itCanBeDeleted) - { - WitnetV2.DrPostStatus _currentStatus = _getDrPostStatus(_drHash); - if (_from == __drPostRequest(_drHash).requester) { - _itCanBeDeleted = ( - _currentStatus == WitnetV2.DrPostStatus.Finalized - || _currentStatus == WitnetV2.DrPostStatus.Expired - ); - } - } - - function __deleteDrPost(bytes32 _drHash) - internal - virtual - { - WitnetV2.DrPostRequest storage __request = __drPostRequest(_drHash); - uint _value = __request.weiReward; - address _to = __request.requester; - delete __board().posts[_drHash]; - if (address(this).balance < _value) { - revert WitnetV2.InsufficientBalance(address(this).balance, _value); - } - _safeTransferTo(payable(_to), _value); - } - - function __deleteDrPostRequest(bytes32 _drHash) - internal - virtual - { - delete __drPost(_drHash).request; - } - - function _getDrPostBlock(bytes32 _drHash) - internal view - virtual - returns (uint256) - { - return __drPost(_drHash).block; - } - - /// Gets current status of given query. - function _getDrPostStatus(bytes32 _drHash) - internal view - virtual - returns (WitnetV2.DrPostStatus _temporaryStatus) - { - uint256 _drPostBlock = _getDrPostBlock(_drHash); - _temporaryStatus = __drPost(_drHash).status; - if ( - _temporaryStatus == WitnetV2.DrPostStatus.Reported - || _temporaryStatus == WitnetV2.DrPostStatus.Disputed - || _temporaryStatus == WitnetV2.DrPostStatus.Accepted - ) { - if (block.number > _drPostBlock + 256 /* TODO: __drPostExpirationBlocks */) { - _temporaryStatus = WitnetV2.DrPostStatus.Expired; - } - } - } - - // /// Gets from of a given query. - function __drPost(bytes32 _drHash) - internal view - returns (WitnetV2.DrPost storage) - { - return __board().posts[_drHash]; - } - - /// Gets the WitnetV2.DrPostRequest part of a given post. - function __drPostRequest(bytes32 _drHash) - internal view - returns (WitnetV2.DrPostRequest storage) - { - return __board().posts[_drHash].request; - } - - /// Gets the Witnet.Result part of a given post. - function __drPostResponse(bytes32 _drHash) - internal view - returns (WitnetV2.DrPostResponse storage) - { - return __board().posts[_drHash].response; - } - - function __setServiceTag() - internal - virtual - returns (bytes4 _serviceTag) - { - _serviceTag = bytes4(keccak256(abi.encodePacked( - "evm::", - block.chainid.toString(), - "::", - address(this).toHexString() - ))); - __board().serviceTag = _serviceTag; - } - -} \ No newline at end of file diff --git a/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol b/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol deleted file mode 100644 index 608f515bf..000000000 --- a/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol +++ /dev/null @@ -1,614 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.8.4 <0.9.0; - -import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; - -import "../../WitnetUpgradableBase.sol"; -import "../../../WitnetRequestBoardV2.sol"; -import "../../../data/WitnetRequestBoardV2Data.sol"; - -/// @title Witnet Request Board "trustless" base implementation contract. -/// @notice Contract to bridge requests to Witnet Decentralized Oracle Network. -/// @dev This contract enables posting requests that Witnet bridges will insert into the Witnet network. -/// The result of the requests will be posted back to this contract by the bridge nodes too. -/// @author The Witnet Foundation -abstract contract WitnetRequestBoardTrustlessBase - is - WitnetUpgradableBase, - WitnetRequestBoardV2, - WitnetRequestBoardV2Data -{ - using ERC165Checker for address; - using WitnetV2 for bytes; - using WitnetV2 for uint256; - - modifier onlyDrPostReporter(bytes32 _drHash) { - address _expectedReporter = __drPostRequest(_drHash).reporter; - if ( - _msgSender() != _expectedReporter - && _expectedReporter != address(0) - ) { - revert IWitnetRequests.DrPostOnlyReporter( - _drHash, - _expectedReporter - ); - } - _; - } - - modifier onlyDrPostRequester(bytes32 _drHash) { - WitnetV2.DrPostRequest storage __post = __drPostRequest(_drHash); - address _requester = __drPostRequest(_drHash).requester; - if (_msgSender() != _requester) { - revert IWitnetRequests.DrPostOnlyRequester(_drHash, _requester); - } - _; - } - - constructor( - bool _upgradable, - bytes32 _versionTag - ) - Payable(address(0x0)) - WitnetUpgradableBase( - _upgradable, - _versionTag, - "io.witnet.proxiable.boards.v2" - ) - {} - - receive() external payable { - revert("WitnetRequestBoardTrustlessBase: no transfers accepted"); - } - - function blocks() override external view returns (IWitnetBlocks) { - return __board().blocks; - } - - function bytecodes() override external view returns (IWitnetBytecodes) { - return __board().bytecodes; - } - - function decoder() override external view returns (IWitnetDecoder) { - return __board().decoder; - } - - // ================================================================================================================ - // --- Overrides IERC165 interface -------------------------------------------------------------------------------- - - /// @dev See {IERC165-supportsInterface}. - function supportsInterface(bytes4 _interfaceId) - public view - virtual override(WitnetUpgradableBase, ERC165) - returns (bool) - { - return _interfaceId == type(WitnetRequestBoardV2).interfaceId - || _interfaceId == type(IWitnetRequestsAdmin).interfaceId - || super.supportsInterface(_interfaceId); - } - - - // ================================================================================================================ - // --- Internal virtual methods ----------------------------------------------------------------------------------- - - // function _cancelDeadline(uint256 _postEpoch) internal view virtual returns (uint256); - // function _reportDeadlineEpoch(uint256 _postEpoch) internal view virtual returns (uint256); - // function _selectReporter(bytes32 _drHash) internal virtual view returns (address); - - - // ================================================================================================================ - // --- Overrides 'Ownable2Step' ----------------------------------------------------------------------------------- - - /// Returns the address of the pending owner. - function pendingOwner() - public view - virtual override - returns (address) - { - return __board().pendingOwner; - } - - /// Returns the address of the current owner. - function owner() - public view - virtual override - returns (address) - { - return __board().owner; - } - - /// Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one. - /// @dev Can only be called by the current owner. - function transferOwnership(address _newOwner) - public - virtual override - onlyOwner - { - __board().pendingOwner = _newOwner; - emit OwnershipTransferStarted(owner(), _newOwner); - } - - /// @dev Transfers ownership of the contract to a new account (`_newOwner`) and deletes any pending owner. - /// @dev Internal function without access restriction. - function _transferOwnership(address _newOwner) - internal - virtual override - { - delete __board().pendingOwner; - address _oldOwner = owner(); - if (_newOwner != _oldOwner) { - __board().owner = _newOwner; - emit OwnershipTransferred(_oldOwner, _newOwner); - } - } - - - // ================================================================================================================ - // --- Overrides 'Payable' ---------------------------------------------------------------------------------------- - - /// Gets current transaction price. - function _getGasPrice() - internal view - virtual override - returns (uint256) - { - return tx.gasprice; - } - - /// Gets current payment value. - function _getMsgValue() - internal view - virtual override - returns (uint256) - { - return msg.value; - } - - /// Transfers ETHs to given address. - /// @param _to Recipient address. - /// @param _amount Amount of ETHs to transfer. - function _safeTransferTo(address payable _to, uint256 _amount) - internal - virtual override - nonReentrant - { - payable(_to).transfer(_amount); - } - - - // ================================================================================================================ - // --- Overrides 'Upgradeable' ------------------------------------------------------------------------------------- - - /// @notice Re-initialize contract's storage context upon a new upgrade from a proxy. - /// @dev Must fail when trying to upgrade to same logic contract more than once. - function initialize(bytes memory _initData) - public - override - { - address _owner = __board().owner; - if (_owner == address(0)) { - // set owner if none set yet - _owner = msg.sender; - __board().owner = _owner; - } else { - // only owner can initialize: - if (msg.sender != _owner) { - revert WitnetUpgradableBase.OnlyOwner(_owner); - } - } - - if (__board().serviceTag == bytes4(0)) { - __setServiceTag(); - } - - if (__board().base != address(0)) { - // current implementation cannot be initialized more than once: - if(__board().base == base()) { - revert WitnetUpgradableBase.AlreadyUpgraded(base()); - } - } - __board().base = base(); - - emit Upgraded(msg.sender, base(), codehash(), version()); - - // Parse optional input addresses array: - address[] memory _refs = abi.decode(_initData, (address[])); - if (_refs.length > 0 && _refs[0] != address(0)) setBlocks(_refs[0]); - if (_refs.length > 1 && _refs[1] != address(0)) setBytecodes(_refs[1]); - if (_refs.length > 2 && _refs[2] != address(0)) setDecoder(_refs[2]); - -// // All complying references must be provided: -// if (address(__board().blocks) == address(0)) { -// revert WitnetUpgradableBase.NotCompliant(type(IWitnetBlocks).interfaceId); -// } else if (address(__board().bytecodes) == address(0)) { -// revert WitnetUpgradableBase.NotCompliant(type(IWitnetBytecodes).interfaceId); -// } else if (address(__board().decoder) == address(0)) { -// revert WitnetUpgradableBase.NotCompliant(type(IWitnetDecoder).interfaceId); -// } - - // Set deliveryTag if not done yet: - if (__board().serviceTag == bytes4(0)) { - __setServiceTag(); - } - } - - /// Tells whether provided address could eventually upgrade the contract. - function isUpgradableFrom(address _from) external view override returns (bool) { - address _owner = __board().owner; - return ( - // false if the WRB is intrinsically not upgradable, or `_from` is no owner - isUpgradable() - && _owner == _from - ); - } - - - // ================================================================================================================ - // --- Base implementation of 'IWitnetRequestsAdmin' -------------------------------------------------------------- - - function setBlocks(address _contractAddr) - public - virtual override - onlyOwner - { - if (isUpgradable()) { - if (!_contractAddr.supportsInterface(type(IWitnetBlocks).interfaceId)) { - revert WitnetUpgradableBase.NotCompliant(type(IWitnetBlocks).interfaceId); - } - __board().blocks = IWitnetBlocks(_contractAddr); - emit SetBlocks(_msgSender(), _contractAddr); - } else { - revert WitnetUpgradableBase.NotUpgradable(address(this)); - } - } - - function setBytecodes(address _contractAddr) - public - virtual override - onlyOwner - { - if (!_contractAddr.supportsInterface(type(IWitnetBytecodes).interfaceId)) { - revert WitnetUpgradableBase.NotCompliant(type(IWitnetBytecodes).interfaceId); - } - __board().bytecodes = IWitnetBytecodes(_contractAddr); - emit SetBytecodes(_msgSender(), _contractAddr); - } - - function setDecoder(address _contractAddr) - public - virtual override - onlyOwner - { - if (!_contractAddr.supportsInterface(type(IWitnetDecoder).interfaceId)) { - revert WitnetUpgradableBase.NotCompliant(type(IWitnetDecoder).interfaceId); - } - __board().decoder = IWitnetDecoder(_contractAddr); - emit SetDecoder(_msgSender(), _contractAddr); - } - - - // ================================================================================================================ - // --- Base implementation of read-only methods in 'IWitnetRequests' ---------------------------------------------- - - function estimateBaseFee( - bytes32 _drRadHash, - uint256 _gasPrice, - bytes32, - uint256 - ) - public view - override - returns (uint256) - { - return ( - estimateReportFee(_drRadHash, _gasPrice) - // TODO: + __board().bytecodes.lookupDrSlaReward(_drSlaHash) * _witPrice - ); - } - - function estimateReportFee(bytes32, uint256) - public view - virtual override - returns (uint256) - { - revert("WitnetRequestBoardTrustlessBase: not yet implemented"); - } - - function getDrPost(bytes32 _drHash) - public view - virtual override - returns (WitnetV2.DrPost memory) - { - return __board().posts[_drHash]; - } - - function getDrPostEpoch(bytes32 _drHash) - public view - virtual override - returns (uint256) - { - return __drPost(_drHash).request.epoch; - } - - function getDrPostResponse(bytes32 _drHash) - public view - virtual override - returns (WitnetV2.DrPostResponse memory) - { - return __drPostResponse(_drHash); - } - - function getDrPostStatus(bytes32 _drHash) - public view - virtual override - returns (WitnetV2.DrPostStatus) - { - return _getDrPostStatus(_drHash); - } - - function readDrPostResultBytes(bytes32 _drHash) - public view - virtual override - drPostNotDeleted(_drHash) - returns (bytes memory) - { - return __drPostResponse(_drHash).drTallyResultCborBytes; - } - - function serviceStats() - public view - virtual override - returns (IWitnetRequests.Stats memory) - { - return __board().serviceStats; - } - - function serviceTag() - public view - virtual override - returns (bytes4) - { - return __board().serviceTag; - } - - - // ================================================================================================================ - // --- Base implementation of state-modifying methods in 'IWitnetRequests' ---------------------------------------- - - function deleteDrPost(bytes32 _drHash) - external - virtual override - { - if (!_canDrPostBeDeletedFrom(_drHash, _msgSender())){ - revert WitnetV2.Unauthorized(_msgSender()); - } - __deleteDrPost(_drHash); - emit DrPostDeleted(_msgSender(), _drHash); - } - - function deleteDrPostRequest(bytes32 _drHash) - public - virtual override - { - if (!_canDrPostBeDeletedFrom(_drHash, _msgSender())){ - revert WitnetV2.Unauthorized(_msgSender()); - } - __deleteDrPostRequest(_drHash); - } - - function disputeDrPost(bytes32 _drHash) - external payable - virtual override - // stakes(_disputeStake(), _disputeDeadlineBlock()) - { - // TODO - // WitnetV2.DrPostStatus _currentStatus = _getDrPostStatus(_drHash); - // WitnetV2.DrPostResponse storage __response = __drPostResponse(_drHash); - // if ( - // _currentStatus == WitnetV2.DrPostStatus.Posted - // || _currentStatus = WitnetV2.DrPostStatus.Reported - // ) { - // if (__response.reporter != address(0)) { - // if (_msgSender() == __response.reporter) { - // revert IWitnetRequests.DrPostBadDisputer( - // _drHash, - // _msgSender() - // ); - // } - // } - // // TODO: check escrow value - // __response = WitnetV2.DrPostResponse({ - // disputer: _msgSender(), - // reporter: __response.reporter, - // escrowed: _getMsgValue(), - // drCommitTxEpoch: 0, - // drTallyTxEpoch: 0, - // drTallyTxHash: bytes32(0), - // cborBytes: bytes("") // Witnet.Precompiled.NoWitnetResponse - // }); - // emit DrPostDisputed(_msgSender(), _drHash); - // } - // else { - // revert IWitnetRequests.DrPostBadMood( - // _drHash, - // _currentStatus - // ); - // } - // __board.serviceStats.totalDisputes ++; - } - - function postDr( - bytes32 _drRadHash, - bytes32 _drSlaHash, - uint256 - ) - external payable - returns (bytes32 _drHash) - { - // TODO - // Calculate current epoch in Witnet terms: - // solhint-disable-next-line - uint256 _currentEpoch = block.timestamp; // TODO: .toEpoch(); - - // Calculate data request delivery tag: - bytes8 _drDeliveryTag = bytes8(keccak256(abi.encode( - _msgSender(), - _drRadHash, - _drSlaHash, - _currentEpoch, - ++ __board().serviceStats.totalPosts - ))); - _drDeliveryTag |= bytes8(serviceTag()); - - // Calculate data request post hash: - _drHash = Witnet.hash(abi.encodePacked( - _drRadHash, - _drSlaHash, - _drDeliveryTag - )); - - // Check minimum base fee is covered: - // uint256 _minBaseFee = estimateBaseFee( - // _drRadHash, - // _getGasPrice(), - // _drSlaHash, - // ); - // if (_getMsgValue() < _minBaseFee) { - // revert IWitnetRequests.DrPostLowReward(_drHash, _minBaseFee, _getMsgValue()); - // } - - // Save DrPost in storage: - WitnetV2.DrPost storage __dr = __drPost(_drHash); - __dr.block = block.number; - __dr.status = WitnetV2.DrPostStatus.Posted; - __dr.request = WitnetV2.DrPostRequest({ - epoch: _currentEpoch, - requester: _msgSender(), - reporter: msg.sender, // TODO: _selectReporter(), - radHash: _drRadHash, - slaHash: _drSlaHash, - weiReward: _getMsgValue() - }); - emit DrPost(msg.sender, _drHash);//__drPostRequest(_drHash)); - } - - function reportDrPost( - bytes32 _drHash, - uint256 _drCommitTxEpoch, - uint256 _drTallyTxEpoch, - bytes32 _drTallyTxHash, - bytes calldata _drTallyResultCborBytes - ) - external payable - virtual override - drPostInStatus(_drHash, WitnetV2.DrPostStatus.Posted) - // stakes(_disputeStake(), _disputeDeadlineBlock()) - { - // TODO - // address _disputer = address(0); - // uint256 _currentEpoch = block.timestamp.toEpoch(); - // uint256 _drPostEpoch = _getDrPostEpoch(_drHash); - // if (_currentEpoch <= _reportDeadlineEpoch(_drHash)) { - // if (_msgSender() != __drPostRequest(_drHash).to) { - // revert DrPostOnlyReporter(__drPostRequest(_drHash).to); - // } - // } else { - // _disputer = _msgSender(); - // } - // if ( - // _drCommitTxEpoch <= _drPostEpoch - // || _drTallyTxEpoch <= _drCommitTxEpoch - // ) { - // revert DrPostBadEpochs( - // _drHash, - // _drPostEpoch, - // _drCommitTxEpoch, - // _drTallyTxEpoch - // ); - // } - // __drPostResponse(_drHash) = WitnetV2.DrPostResponse({ - // disputer: _disputer, - // reporter: _disputer == address(0) ? _msgSender() : address(0), - // escrowed: _getMsgValue(), - // drCommitTxEpoch: _drCommitTxEpoch, - // drTallyTxEpoch: _drTallyTxEpoch, - // drTallyTxHash: _drTallyTxHash, - // drTallyResultCborBytes: _drTallyResultCborBytes - // }); - // __board().serviceStats.totalReports ++; - } - - function verifyDrPost( - bytes32 _drHash, - uint256 _drCommitTxEpoch, - uint256 _drTallyTxEpoch, - uint256 _drTallyTxIndex, - bytes32 _blockDrTallyTxsRoot, - bytes32[] calldata _blockDrTallyTxHashes, - bytes calldata _drTallyTxBytes - ) - external payable - virtual override - { - // TODO - // WitnetV2.DrPostStatus _currentStatus = _getDrPostStatus(_drHash); - // WitnetV2.DrPostResponse storage __response = __drPostResponse(_drHash); - // address _bannedSender = _currentStatus == WitnetV2.DrPostStatus.Reported - // ? __response.reporter - // : _currentStatus == WitnetV2.DrPostStatus.Disputed - // ? __response.disputer - // : address(0) - // ; - // if (_bannedSender == address(0)) { - // revert IWitnetRequests.DrPostBadMood( - // _drHash, - // _currentStatus - // ); - // } else if (_msgSender() == _bannedSender) { - // revert IWitnetRequests.DrPostBadDisputer( - // _drHash, - // _msgSender() - // ); - // } else { - // bytes memory _drTallyTxResult; - // // TODO: _drTallyTxResult = _verifyDrPost(...); - // __response = WitnetV2.DrPostResponse({ - // disputer: _currentStatus == WitnetV2.DrPostStatus.Reported ? _msgSender() : __response.disputer, - // reporter: _currentStatus == WitnetV2.DrPostStatus.Disputed ? _msgSender() : __response.reporter, - // escrowed: __response.escrowed, - // drCommitTxEpoch: _drCommitTxEpoch, - // drTallyTxEpoch: _drTallyTxEpoch, - // drTallyTxHash: _blockDrTallyTxHashes[_drTallyTxIndex], - // drTallyTxResult: _drTallyTxResult - // }); - // emit DrPostVerified( - // _msgSender(), - // _drHash - // ); - // if (_currentStatus == WitnetV2.DrPostStatus.Reported) { - // __board().serviceStats.totalDisputes ++; - // } else { - // __board().serviceStats.totalReports ++; - // } - // } - // // TODO: __contextSlash(_bannedSender); - } - - function upgradeDrPostReward(bytes32 _drHash) - public payable - virtual override - drPostInStatus(_drHash, WitnetV2.DrPostStatus.Posted) - { - if (_getMsgValue() > 0) { - __drPostRequest(_drHash).weiReward += _getMsgValue(); - emit DrPostUpgraded( - _msgSender(), - _drHash, - __drPostRequest(_drHash).weiReward - ); - __board().serviceStats.totalUpgrades ++; - } - } - -} \ No newline at end of file diff --git a/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessReporting1.sol b/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessReporting1.sol deleted file mode 100644 index 8297ea0b6..000000000 --- a/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessReporting1.sol +++ /dev/null @@ -1,280 +0,0 @@ -// SPDX-License-Identifier: MIT -// solhint-disable var-name-mixedcase - -pragma solidity >=0.8.0 <0.9.0; - -import "./WitnetRequestBoardTrustlessBase.sol"; -import "../../../data/WitnetReporting1Data.sol"; - -/// @title Witnet Request Board "trustable" implementation contract. -/// @notice Contract to bridge requests to Witnet Decentralized Oracle Network. -/// @dev This contract enables posting requests that Witnet bridges will insert into the Witnet network. -/// The result of the requests will be posted back to this contract by the bridge nodes too. -/// @author The Witnet Foundation -contract WitnetRequestBoardTrustlessReporting1 - is - WitnetRequestBoardTrustlessBase, - WitnetReporting1Data -{ - modifier onlySignedUpReporters { - if (!isSignedUpReporter(_msgSender())) { - revert WitnetV2.Unauthorized(_msgSender()); - } - _; - } - - modifier onlyExpectedReporterFor(bytes32 _drHash) { - if (_msgSender() != __drPostRequest(_drHash).reporter) { - revert WitnetV2.Unauthorized(_msgSender()); - } - _; - } - - constructor( - bool _upgradable, - bytes32 _versionTag - ) - WitnetRequestBoardTrustlessBase(_upgradable, _versionTag) - {} - - // ================================================================================================================ - // --- Override WitnetRequestBoardV2Data -------------------------------------------------------------------------- - - function _canDrPostBeDeletedFrom(bytes32 _drHash, address _from) - internal view - virtual override - returns (bool) - { - WitnetV2.DrPostStatus _temporaryStatus = __drPost(_drHash).status; - return (_temporaryStatus == WitnetV2.DrPostStatus.Rejected - ? true - : super._canDrPostBeDeletedFrom(_drHash, _from) - ); - } - - function _getDrPostStatus(bytes32 _drHash) - internal view - virtual override - returns (WitnetV2.DrPostStatus _temporaryStatus) - { - _temporaryStatus = __drPost(_drHash).status; - if (_temporaryStatus == WitnetV2.DrPostStatus.Accepted) { - if (block.number > _getDrPostBlock(_drHash) + __reporting().settings.acceptanceBlocks) { - return WitnetV2.DrPostStatus.Expired; - } - } - return super._getDrPostStatus(_drHash); - } - - // ================================================================================================================ - // --- Overrides IERC165 interface -------------------------------------------------------------------------------- - - /// @dev See {IERC165-supportsInterface}. - function supportsInterface(bytes4 _interfaceId) - public view - virtual override - returns (bool) - { - return _interfaceId == type(IWitnetReporting1).interfaceId - || _interfaceId == type(IWitnetReporting1Admin).interfaceId - || super.supportsInterface(_interfaceId); - } - -function deleteDrPost(bytes32 _drHash) - external - virtual override - { - if (!_canDrPostBeDeletedFrom(_drHash, _msgSender())){ - revert WitnetV2.Unauthorized(_msgSender()); - } - uint _value; - if (__drPost(_drHash).status == WitnetV2.DrPostStatus.Posted) { - address _reporter = __drPostRequest(_drHash).reporter; - _value = __slashSignedUpReporter(_reporter); - if (_value < address(this).balance) { - revert WitnetV2.InsufficientBalance(address(this).balance, _value); - } - } - __deleteDrPost(_drHash); - if (_value > 0) { - _safeTransferTo(payable(_msgSender()), _value); - } - emit DrPostDeleted(_msgSender(), _drHash); - } - - // ================================================================================================================ - // --- IWitnetReporting1 implementation --------------------------------------------------------------------------- - - function getReportingAddressByIndex(uint _reporterIndex) - external view - override - returns (address) - { - if (_reporterIndex >= __reporting().totalReporters) { - revert WitnetV2.IndexOutOfBounds(_reporterIndex, __reporting().totalReporters); - } - return __reporting().reporters[_reporterIndex]; - } - - function getReportingAddresses() - external view - override - returns (address[] memory _addrs) - { - _addrs = new address[](__reporting().totalReporters); - address[] storage __addrs = __reporting().reporters; - for (uint _ix = 0; _ix < _addrs.length; ) { - _addrs[_ix] = __addrs[_ix]; - unchecked { - _ix ++; - } - } - } - - function getReportingSignUpConfig() - external view - override - returns (SignUpConfig memory) - { - return __reporting().settings; - } - - function isSignedUpReporter(address _reporter) - public view - virtual override - returns (bool) - { - WitnetReporting1Data.Escrow storage __escrow = __reporting().escrows[_reporter]; - return ( - __escrow.weiSignUpFee > 0 - && __escrow.lastSignOutBlock > __escrow.lastSignUpBlock - ); - } - - function totalSignedUpReporters() - external view - override - returns (uint256) - { - return __reporting().totalReporters; - } - - function signUp() - external payable - override - nonReentrant - returns (uint256 _index) - { - IWitnetReporting1.SignUpConfig storage __settings = __reporting().settings; - WitnetReporting1Data.Escrow storage __escrow = __reporting().escrows[_msgSender()]; - uint _fee = __settings.weiSignUpFee; - uint _value = _getMsgValue(); - // Check that it's not already signed up: - if (__escrow.weiSignUpFee > 0) { - revert IWitnetReporting1.AlreadySignedUp(_msgSender()); - } - // Check that it's not banned, or if so, enough blocks have elapsed since then: - if (__escrow.lastSlashBlock > 0) { - if ( - __settings.banningBlocks == 0 - || block.number < __escrow.lastSlashBlock + __settings.banningBlocks - ) { - revert WitnetV2.Unauthorized(_msgSender()); - } - } - // Check that enough sign-up fee is being provided: - if (_value < _fee) { - revert WitnetV2.InsufficientFee(_value, _fee); - } - // Update storage: - _index = __reporting().totalReporters; - __escrow.index = _index; - __escrow.weiSignUpFee = _fee; - __escrow.lastSignUpBlock = block.number; - emit SignedUp( - _msgSender(), - _fee, - __pushReporterAddress(_msgSender()) - ); - // Transfer unused funds back: - if (_value > _fee) { - _safeTransferTo(payable(_msgSender()), _value - _fee); - } - } - - function signOut() - external - override - onlySignedUpReporters - { - WitnetReporting1Data.Escrow storage __escrow = __reporting().escrows[_msgSender()]; - // Update storage: - __escrow.lastSignOutBlock = block.number; - emit SigningOut( - _msgSender(), - __escrow.weiSignUpFee, - __deleteReporterAddressByIndex(__escrow.index) - ); - } - - function acceptDrPost(bytes32 _drHash) - external - override - drPostInStatus(_drHash, WitnetV2.DrPostStatus.Posted) - // onlySignedUpReporters - // onlyExpectedReporterFor(_drHash) - { - if (_msgSender() != __drPostRequest(_drHash).requester) { - revert WitnetV2.Unauthorized(_msgSender()); - } - __drPost(_drHash).status = WitnetV2.DrPostStatus.Accepted; - emit DrPostAccepted(_msgSender(), _drHash); - } - - function rejectDrPost(bytes32 _drHash, Witnet.ErrorCodes _reason) - external payable - override - drPostInStatus(_drHash, WitnetV2.DrPostStatus.Posted) - onlyExpectedReporterFor(_drHash) - nonReentrant - { - uint _value = _getMsgValue(); - uint _fee = __reporting().settings.weiRejectionFee; - // Check enough value is provided as to pay for rejection fee, if any - if (_value < _fee) { - revert WitnetV2.InsufficientFee(_value, _fee); - } - // Transfer back income funds exceeding rejection fee, if any - if (_value > _fee) { - _safeTransferTo( - payable(_msgSender()), - _value - _fee - ); - } - // Transfer reporter as much deposited drPost reward as possible: - WitnetV2.DrPost storage __post = __drPost(_drHash); - _value = __post.request.weiReward; - if (_value > address(this).balance) { - _value = address(this).balance; - } - __post.request.weiReward -= _value; - __post.status = WitnetV2.DrPostStatus.Rejected; - _safeTransferTo( - payable(__post.request.requester), - _value + _fee - ); - emit DrPostRejected(_msgSender(), _drHash, _reason); - } - - - // ================================================================================================================ - // --- IWitnetReporting1Admin implementation ---------------------------------------------------------------------- - - function setSignUpConfig(IWitnetReporting1.SignUpConfig calldata) - virtual override - external - { - revert("WitnetRequestBoardTrustlessReporting1: not yet implemented"); - } - -} \ No newline at end of file diff --git a/contracts/interfaces/V2/IWitnetBlocks.sol b/contracts/interfaces/V2/IWitnetBlocks.sol deleted file mode 100644 index 07c4d75b7..000000000 --- a/contracts/interfaces/V2/IWitnetBlocks.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.8.0 <0.9.0; - -import "./IWitnetRequests.sol"; - -interface IWitnetBlocks { - - event Hooked(address indexed from); - event Rollup(address indexed from, uint256 index, uint256 prevIndex); - event Slashed(address indexed from, uint256 index, uint256 prevIndex); - - function getBlockDrTxsRoot(uint256 _witnetEpoch) external view returns (bytes32 _blockHash, bytes32 _txsRoot); - function getBlockDrTallyTxsRoot(uint256 _witnetEpoch) external view returns (bytes32 _blockHash, bytes32 _txsRoot); - function getBlockStatus(uint256 _witnetEpoch) external view returns (WitnetV2.BlockStatus); - - function getLastBeacon() external view returns (WitnetV2.Beacon memory); - function getLastBeaconIndex() external view returns (uint256); - function getLastBeaconStatus() external view returns (WitnetV2.BeaconStatus); - - function getNextBeaconEvmBlock() external view returns (uint256); - function getNextBeaconIndex() external view returns (uint256); - - function rollupNext() external payable; - function rollupForward() external payable; - - function setupForward() external payable; - - function verifyNext() external; - function verifyForward() external; - - function hook(IWitnetRequests) external; -} \ No newline at end of file diff --git a/contracts/interfaces/V2/IWitnetDecoder.sol b/contracts/interfaces/V2/IWitnetDecoder.sol deleted file mode 100644 index 9698d4e23..000000000 --- a/contracts/interfaces/V2/IWitnetDecoder.sol +++ /dev/null @@ -1,103 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.8.0 <0.9.0; - -import "../../libs/Witnet.sol"; - -/// @title The Witnet interface for decoding Witnet-provided request to Data Requests. -/// This interface exposes functions to check for the success/failure of -/// a Witnet-provided result, as well as to parse and convert result into -/// Solidity types suitable to the application level. -/// @author The Witnet Foundation. -interface IWitnetDecoder { - /// Decode raw CBOR bytes into a Witnet.Result instance. - /// @param _cborBytes Raw bytes representing a CBOR-encoded value. - /// @return A `Witnet.Result` instance. - function toWitnetResult(bytes memory _cborBytes) external pure returns (Witnet.Result memory); - - /// Tell if a Witnet.Result contains a successful result, or not. - /// @param _result An instance of Witnet.Result. - /// @return `true` if successful, `false` if errored. - function succeeded(Witnet.Result memory _result) external pure returns (bool); - - /// Decode a boolean value from a Witnet.Result as an `bool` value. - /// @param _result An instance of Witnet.Result. - /// @return The `bool` decoded from the Witnet.Result. - function asBool(Witnet.Result memory _result) external pure returns (bool); - - /// Decode a bytes value from a Witnet.Result as a `bytes` value. - /// @param _result An instance of Witnet.Result. - /// @return The `bytes` decoded from the Witnet.Result. - function asBytes(Witnet.Result memory _result) external pure returns (bytes memory); - - /// Decode a bytes value from a Witnet.Result as a `bytes32` value. - /// @param _result An instance of Witnet.Result. - /// @return The `bytes32` decoded from the Witnet.Result. - function asBytes32(Witnet.Result memory _result) external pure returns (bytes32); - - /// Decode a fixed16 (half-precision) numeric value from a Witnet.Result as an `int32` value. - /// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values. - /// by 5 decimal orders so as to get a fixed precision of 5 decimal positions, which should be OK for most `fixed16`. - /// use cases. In other words, the output of this method is 10,000 times the actual value, encoded into an `int32`. - /// @param _result An instance of Witnet.Result. - /// @return The `int32` decoded from the Witnet.Result. - function asFixedFloat16(Witnet.Result memory _result) external pure returns (int32); - - /// Decode an array of fixed16 values from a Witnet.Result as an `int32[]` value. - /// @param _result An instance of Witnet.Result. - /// @return The `int32[]` decoded from the Witnet.Result. - function asFixedFloat16Array(Witnet.Result memory _result) external pure returns (int32[] memory); - - /// Decode a integer numeric value from a Witnet.Result as an `int` value. - /// @param _result An instance of Witnet.Result. - /// @return The `int` decoded from the Witnet.Result. - function toInt(Witnet.Result memory _result) external pure returns (int); - - /// Decode an array of integer numeric values from a Witnet.Result as an `int[]` value. - /// @param _result An instance of Witnet.Result. - /// @return The `int[]` decoded from the Witnet.Result. - function toIntArray(Witnet.Result memory _result) external pure returns (int[] memory); - - /// Decode a string value from a Witnet.Result as a `string` value. - /// @param _result An instance of Witnet.Result. - /// @return The `string` decoded from the Witnet.Result. - function toString(Witnet.Result memory _result) external pure returns (string memory); - - /// Decode an array of string values from a Witnet.Result as a `string[]` value. - /// @param _result An instance of Witnet.Result. - /// @return The `string[]` decoded from the Witnet.Result. - function toStringArray(Witnet.Result memory _result) external pure returns (string[] memory); - - /// Decode a natural numeric value from a Witnet.Result as a `uint` value. - /// @param _result An instance of Witnet.Result. - /// @return The `uint` decoded from the Witnet.Result. - function toUint(Witnet.Result memory _result) external pure returns(uint); - - /// Decode an array of natural numeric values from a Witnet.Result as a `uint[]` value. - /// @param _result An instance of Witnet.Result. - /// @return The `uint[]` decoded from the Witnet.Result. - function toUintArray(Witnet.Result memory _result) external pure returns (uint64[] memory); - - /// Decode an error code from a Witnet.Result as a member of `WitnetV2.ErrorCodes`. - /// @param _result An instance of `Witnet.Result`. - /// @return The `CBORValue.Error memory` decoded from the Witnet.Result. - function getErrorCode(Witnet.Result memory _result) external pure returns (Witnet.ErrorCodes); - - /// Generate a suitable error message for a member of `WitnetV2.ErrorCodes` and its corresponding arguments. - /// @dev WARN: Note that client contracts should wrap this function into a try-catch foreseing potential errors generated in this function - /// @param _result An instance of `Witnet.Result`. - /// @return A tuple containing the `CBORValue.Error memory` decoded from the `Witnet.Result`, plus a loggable error message. - function getErrorMessage(Witnet.Result memory _result) external pure returns (Witnet.ErrorCodes, string memory); - - - // function isArray(Witnet.Result memory _result) external pure returns (bool); - // function getArrayLength(Witnet.Result memory _result) external pure returns (bool); - // function getTypeAndSize(Witnet.Result memory _result) external pure returns (WitnetV2.RadonDataTypes, uint); - // function getAddressAt(Witnet.Result memory _result, uint _indexes) external pure returns (address); - // function getBoolAt(Witnet.Result memory _result, uint _indexes) external pure returns (bool); - // function getBytesAt(Witnet.Result memory _result, uint _indexes) external pure returns (bytes memory); - // function getInt256At(Witnet.Result memory _result, uint _indexes) external pure returns (int256); - // function getStringAt(Witnet.Result memory _result, uint _indexes) external pure returns (string memory); - // function getUint256At(Witnet.Result memory _result, uint _indexes) external pure returns (uint256); - // function toAddress(Witnet.Result memory _result) external pure returns (address); -} diff --git a/contracts/interfaces/V2/IWitnetEncoder.sol b/contracts/interfaces/V2/IWitnetEncoder.sol deleted file mode 100644 index 5037d5f0c..000000000 --- a/contracts/interfaces/V2/IWitnetEncoder.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.8.0 <0.9.0; - -interface IWitnetEncoder { - function encode(address) external pure returns (bytes memory); - function encode(bool) external pure returns (bytes memory); - function encode(bytes calldata) external pure returns (bytes memory); - function encode(int256) external pure returns (bytes memory); - function encode(uint256) external pure returns (bytes memory); - function encode(string calldata) external pure returns (bytes memory); -} diff --git a/contracts/interfaces/V2/IWitnetReporting1.sol b/contracts/interfaces/V2/IWitnetReporting1.sol deleted file mode 100644 index a85c311cf..000000000 --- a/contracts/interfaces/V2/IWitnetReporting1.sol +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.7.0 <0.9.0; - -import "../../libs/WitnetV2.sol"; - -/// @title Witnet Request Board emitting events interface. -/// @author The Witnet Foundation. -interface IWitnetReporting1 { - - error AlreadySignedUp(address reporter); - - event DrPostAccepted(address indexed from, bytes32 drHash); - event DrPostRejected(address indexed from, bytes32 drHash, Witnet.ErrorCodes reason); - - event SignedUp(address indexed reporter, uint256 weiValue, uint256 totalReporters); - event SigningOut(address indexed reporter, uint256 weiValue, uint256 totalReporters); - event Slashed(address indexed reporter, uint256 weiValue, uint256 totalReporters); - - struct SignUpConfig { - uint256 weiRejectionFee; - uint256 weiSignUpFee; - uint256 acceptanceBlocks; - uint256 banningBlocks; - uint256 exitBlocks; - } - event SignUpConfigSet(address indexed from, SignUpConfig config); - - function getReportingAddressByIndex(uint256) external view returns (address); - function getReportingAddresses() external view returns (address[] memory); - function getReportingSignUpConfig() external view returns (SignUpConfig memory); - function isSignedUpReporter(address) external view returns (bool); - function totalSignedUpReporters() external view returns (uint256); - - function signUp() external payable returns (uint256 _index); - function signOut() external; - - function acceptDrPost(bytes32) external; - function rejectDrPost(bytes32, Witnet.ErrorCodes) external payable; -} \ No newline at end of file diff --git a/contracts/interfaces/V2/IWitnetReporting1Admin.sol b/contracts/interfaces/V2/IWitnetReporting1Admin.sol deleted file mode 100644 index 4999b4b72..000000000 --- a/contracts/interfaces/V2/IWitnetReporting1Admin.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.7.0 <0.9.0; - -import "./IWitnetReporting1.sol"; - -/// @title Witnet Request Board emitting events interface. -/// @author The Witnet Foundation. -interface IWitnetReporting1Admin { - - function setSignUpConfig(IWitnetReporting1.SignUpConfig calldata) external; - -} \ No newline at end of file diff --git a/contracts/interfaces/V2/IWitnetRequests.sol b/contracts/interfaces/V2/IWitnetRequests.sol deleted file mode 100644 index 1e795bf79..000000000 --- a/contracts/interfaces/V2/IWitnetRequests.sol +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.8.0 <0.9.0; - -import "../../libs/WitnetV2.sol"; - -/// @title Witnet Request Board emitting events interface. -/// @author The Witnet Foundation. -interface IWitnetRequests { - - struct Stats { - uint256 totalDisputes; - uint256 totalPosts; - uint256 totalReports; - uint256 totalUpgrades; - } - - error DrPostBadDisputer(bytes32 drHash, address disputer); - error DrPostBadEpochs(bytes32 drHash, uint256 drPostEpoch, uint256 drCommitTxEpoch, uint256 drTallyTxEpoch); - error DrPostBadMood(bytes32 drHash, WitnetV2.DrPostStatus currentStatus); - error DrPostLowReward(bytes32 drHash, uint256 minBaseFee, uint256 weiValue); - error DrPostNotInStatus(bytes32 drHash, WitnetV2.DrPostStatus currentStatus, WitnetV2.DrPostStatus requiredStatus); - error DrPostOnlyRequester(bytes32 drHash, address requester); - error DrPostOnlyReporter(bytes32 drHash, address reporter); - - event DrPost(address indexed reporter, bytes32 drHash); - event DrPostDeleted (address indexed from, bytes32 drHash); - event DrPostDisputed(address indexed from, bytes32 drHash); - event DrPostReported(address indexed from, bytes32 drHash); - event DrPostUpgraded(address indexed from, bytes32 drHash, uint256 weiReward); - event DrPostVerified(address indexed from, bytes32 drHash); - - function estimateBaseFee(bytes32 _drRadHash, uint256 _gasPrice, bytes32 _drSlaHash, uint256 _witPrice) external view returns (uint256); - function estimateReportFee(bytes32 _drRadHash, uint256 _gasPrice) external view returns (uint256); - - function getDrPost(bytes32 _drHash) external view returns (WitnetV2.DrPost memory); - function getDrPostEpoch(bytes32 _drHash) external view returns (uint256); - function getDrPostResponse(bytes32 _drHash) external view returns (WitnetV2.DrPostResponse memory); - function getDrPostStatus(bytes32 _drHash) external view returns (WitnetV2.DrPostStatus); - function readDrPostResultBytes(bytes32 _drHash) external view returns (bytes memory); - function serviceStats() external view returns (Stats memory); - function serviceTag() external view returns (bytes4); - - function postDr(bytes32 _drRadHash, bytes32 _drSlaHash, uint256 _witPrice) external payable returns (bytes32 _drHash); - - function deleteDrPost(bytes32 _drHash) external; - function deleteDrPostRequest(bytes32 _drHash) external; - function disputeDrPost(bytes32 _drHash) external payable; - function reportDrPost( - bytes32 _drHash, - uint256 _drCommitTxEpoch, - uint256 _drTallyTxEpoch, - bytes32 _drTallyTxHash, - bytes calldata _drTallyResultCborBytes - ) external payable; - function upgradeDrPostReward(bytes32 _drHash) external payable; - function verifyDrPost( - bytes32 _drHash, - uint256 _drCommitTxEpoch, - uint256 _drTallyTxEpoch, - uint256 _drTallyTxIndex, - bytes32 _blockDrTallyTxsRoot, - bytes32[] calldata _blockDrTallyTxHashes, - bytes calldata _drTallyTxBytes - ) external payable; - -} \ No newline at end of file diff --git a/contracts/interfaces/V2/IWitnetRequestsAdmin.sol b/contracts/interfaces/V2/IWitnetRequestsAdmin.sol deleted file mode 100644 index 0ff538b83..000000000 --- a/contracts/interfaces/V2/IWitnetRequestsAdmin.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.8.4 <0.9.0; - -/// @title Witnet Request Board emitting events interface. -/// @author The Witnet Foundation. -interface IWitnetRequestsAdmin { - - event SetBlocks(address indexed from, address contractAddr); - event SetBytecodes(address indexed from, address contractAddr); - event SetDecoder(address indexed from, address contractAddr); - - function setBlocks(address) external; - function setBytecodes(address) external; - function setDecoder(address) external; - -} \ No newline at end of file diff --git a/contracts/interfaces/V2/IWitnetTraps.sol b/contracts/interfaces/V2/IWitnetTraps.sol deleted file mode 100644 index 67042475c..000000000 --- a/contracts/interfaces/V2/IWitnetTraps.sol +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.8.0 <0.9.0; - -import "../../libs/WitnetV2.sol"; - -/// @title Witnet Request Board emitting events interface. -/// @author The Witnet Foundation. -interface IWitnetTraps { - - event Trap(address indexed from, bytes32 _trapHash); - event TrapIn(address indexed from, bytes32 _trapHash, uint256 _amount); - event TrapOut(address indexed from, bytes32 _trapHash); - - function getTrapBalanceOf(address from, bytes32 _trapHash) external view returns (uint256); - // function getTrapData(bytes32 _trapHash) external view returns (WitnetV2.TrapData memory); - // function getTrapStatus(bytes32 _trapHash) external view returns (WitnetV2.TrapStatus); - - function trap(bytes32 _drQueryHash, uint256 _pushInterval, uint256 _pushReward) external returns (bytes32 _trapHash); - function push(bytes32 _drQueryHash, bytes32 _drHash/*, **/) external payable; - function verifyPush(bytes32 _drQueryHash, bytes32 _drHash/*, **/) external payable; - - function trapIn(bytes32 _trapHash) external payable; - function trapOut(bytes32 _trapHash) external; - - function totalTraps() external view returns (uint256); - function totalPushes() external view returns (uint256); - function totalDisputes() external view returns (uint256); - -} \ No newline at end of file From 4b99ef55071d1726d3182ffbfb23f1bad0758c87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Sun, 19 Feb 2023 00:37:35 +0100 Subject: [PATCH 097/119] refactor(bytecodes): RadonRetrieval => RadonRequest --- contracts/data/WitnetBytecodesData.sol | 8 +- contracts/impls/bytecodes/WitnetBytecodes.sol | 67 +++--- contracts/interfaces/V2/IWitnetBytecodes.sol | 25 ++- contracts/libs/WitnetV2.sol | 10 +- test/witnet_bytecodes.test.js | 201 +++++++----------- 5 files changed, 137 insertions(+), 174 deletions(-) diff --git a/contracts/data/WitnetBytecodesData.sol b/contracts/data/WitnetBytecodesData.sol index 4386aae44..e682e4339 100644 --- a/contracts/data/WitnetBytecodesData.sol +++ b/contracts/data/WitnetBytecodesData.sol @@ -27,7 +27,7 @@ abstract contract WitnetBytecodesData // ... } - struct RadonRetrieval { + struct RadonRequest { WitnetV2.RadonDataTypes resultDataType; uint16 resultMaxSize; string[][] args; @@ -42,7 +42,7 @@ abstract contract WitnetBytecodesData mapping (bytes32 => uint256) providersIndex; mapping (bytes32 => WitnetV2.RadonReducer) reducers; - mapping (bytes32 => RadonRetrieval) retrievals; + mapping (bytes32 => RadonRequest) requests; mapping (bytes32 => WitnetV2.RadonSLA) slas; mapping (bytes32 => WitnetV2.DataSource) sources; } @@ -76,9 +76,9 @@ abstract contract WitnetBytecodesData function __retrievals(bytes32 _drRetrievalHash) internal view - returns (RadonRetrieval storage _ptr) + returns (RadonRequest storage _ptr) { - return __database().retrievals[_drRetrievalHash]; + return __database().requests[_drRetrievalHash]; } } \ No newline at end of file diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol index 97d519bef..2a8fda072 100644 --- a/contracts/impls/bytecodes/WitnetBytecodes.sol +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -163,10 +163,10 @@ contract WitnetBytecodes override returns (bytes memory) { - RadonRetrieval memory _retrieval = __retrievals(_radHash); + RadonRequest memory _retrieval = __retrievals(_radHash); WitnetV2.DataSource[] memory _sources = new WitnetV2.DataSource[](_retrieval.sources.length); if (_sources.length == 0) { - revert IWitnetBytecodes.UnknownRadonRetrieval(_radHash); + revert IWitnetBytecodes.UnknownRadonRequest(_radHash); } for (uint _ix = 0; _ix < _retrieval.sources.length; _ix ++) { _sources[_ix] = __database().sources[_retrieval.sources[_ix]]; @@ -218,9 +218,9 @@ contract WitnetBytecodes if (__sla.numWitnesses == 0) { revert IWitnetBytecodes.UnknownRadonSLA(_slaHash); } - RadonRetrieval storage __retrieval = __retrievals(_radHash); + RadonRequest storage __retrieval = __retrievals(_radHash); if (__retrieval.weight == 0) { - revert IWitnetBytecodes.UnknownRadonRetrieval(_radHash); + revert IWitnetBytecodes.UnknownRadonRequest(_radHash); } return ( hashOf(_radHash, _slaHash), @@ -317,7 +317,7 @@ contract WitnetBytecodes } } - function lookupRadonRetrievalAggregator(bytes32 _radHash) + function lookupRadonRequestAggregator(bytes32 _radHash) external view override returns (WitnetV2.RadonReducer memory) @@ -327,7 +327,7 @@ contract WitnetBytecodes ]; } - function lookupRadonRetrievalResultDataType(bytes32 _radHash) + function lookupRadonRequestResultDataType(bytes32 _radHash) external view override returns (WitnetV2.RadonDataTypes) @@ -335,7 +335,7 @@ contract WitnetBytecodes return __retrievals(_radHash).resultDataType; } - function lookupRadonRetrievalResultMaxSize(bytes32 _radHash) + function lookupRadonRequestResultMaxSize(bytes32 _radHash) external view override returns (uint256) @@ -343,7 +343,7 @@ contract WitnetBytecodes return __retrievals(_radHash).resultMaxSize; } - function lookupRadonRetrievalSources(bytes32 _radHash) + function lookupRadonRequestSources(bytes32 _radHash) external view override returns (bytes32[] memory) @@ -351,7 +351,7 @@ contract WitnetBytecodes return __retrievals(_radHash).sources; } - function lookupRadonRetrievalSourcesCount(bytes32 _radHash) + function lookupRadonRequestSourcesCount(bytes32 _radHash) external view override returns (uint) @@ -359,7 +359,7 @@ contract WitnetBytecodes return __retrievals(_radHash).sources.length; } - function lookupRadonRetrievalTally(bytes32 _radHash) + function lookupRadonRequestTally(bytes32 _radHash) external view override returns (WitnetV2.RadonReducer memory) @@ -486,32 +486,27 @@ contract WitnetBytecodes } } - function verifyRadonRetrieval( - WitnetV2.RadonDataTypes _resultDataType, - uint16 _resultMaxSize, + function verifyRadonRequest( bytes32[] memory _sourcesHashes, - string[][] memory _sourcesArgs, bytes32 _aggregatorHash, - bytes32 _tallyHash + bytes32 _tallyHash, + uint16 _resultMaxSize, + string[][] memory _args ) external virtual override returns (bytes32 hash) { - // Check provided result type and result max size: - // TODO: revisit - _resultMaxSize = _resultDataType.validate(_resultMaxSize); - // Check that at least one source is provided; if (_sourcesHashes.length == 0) { - revert WitnetV2.RadonRetrievalNoSources(); + revert WitnetV2.RadonRequestNoSources(); } // Check that number of args arrays matches the number of sources: - if ( _sourcesHashes.length != _sourcesArgs.length) { - revert WitnetV2.RadonRetrievalSourcesArgsMismatch( + if ( _sourcesHashes.length != _args.length) { + revert WitnetV2.RadonRequestSourcesArgsMismatch( _sourcesHashes.length, - _sourcesArgs.length + _args.length ); } @@ -523,45 +518,51 @@ contract WitnetBytecodes } // Check result type consistency among all sources: + WitnetV2.RadonDataTypes _resultDataType; WitnetV2.DataSource[] memory _sources = new WitnetV2.DataSource[](_sourcesHashes.length); for (uint _ix = 0; _ix < _sources.length; _ix ++) { _sources[_ix] = __database().sources[_sourcesHashes[_ix]]; // Check all sources return same Radon data type: - if (_sources[_ix].resultDataType != _resultDataType) { - revert WitnetV2.RadonRetrievalResultsMismatch( + if (_ix == 0) { + _resultDataType = _sources[0].resultDataType; + } else if (_sources[_ix].resultDataType != _resultDataType) { + revert WitnetV2.RadonRequestResultsMismatch( _ix, uint8(_sources[_ix].resultDataType), uint8(_resultDataType) ); } // check enough args are provided for each source - if (_sourcesArgs[_ix].length < uint(_sources[_ix].argsCount)) { - revert WitnetV2.RadonRetrievalMissingArgs( + if (_args[_ix].length < uint(_sources[_ix].argsCount)) { + revert WitnetV2.RadonRequestMissingArgs( _ix, _sources[_ix].argsCount, - _sourcesArgs[_ix].length + _args[_ix].length ); } } + + // Check provided result type and result max size: + _resultMaxSize = _resultDataType.validate(_resultMaxSize); // Build radon retrieval bytecode: bytes memory _bytecode = _sources.encode( - _sourcesArgs, + _args, _aggregator.encode(), _tally.encode(), _resultMaxSize ); if (_bytecode.length > 65535) { - revert WitnetV2.RadonRetrievalTooHeavy(_bytecode, _bytecode.length); + revert WitnetV2.RadonRequestTooHeavy(_bytecode, _bytecode.length); } // Calculate hash and add metadata to storage if new: hash = _bytecode.hash(); - if (__database().retrievals[hash].weight == 0) { - __database().retrievals[hash] = RadonRetrieval({ + if (__database().requests[hash].weight == 0) { + __database().requests[hash] = RadonRequest({ resultDataType: _resultDataType, resultMaxSize: _resultMaxSize, - args: _sourcesArgs, + args: _args, sources: _sourcesHashes, aggregator: _aggregatorHash, tally: _tallyHash, diff --git a/contracts/interfaces/V2/IWitnetBytecodes.sol b/contracts/interfaces/V2/IWitnetBytecodes.sol index 9733c20d1..12987b1ab 100644 --- a/contracts/interfaces/V2/IWitnetBytecodes.sol +++ b/contracts/interfaces/V2/IWitnetBytecodes.sol @@ -8,7 +8,7 @@ interface IWitnetBytecodes { error UnknownDataSource(bytes32 hash); error UnknownRadonReducer(bytes32 hash); - error UnknownRadonRetrieval(bytes32 hash); + error UnknownRadonRequest(bytes32 hash); error UnknownRadonSLA(bytes32 hash); event NewDataProvider(uint256 index); @@ -34,12 +34,12 @@ interface IWitnetBytecodes { function lookupDataSourceArgsCount(bytes32 hash) external view returns (uint8); function lookupDataSourceResultDataType(bytes32 hash) external view returns (WitnetV2.RadonDataTypes); function lookupRadonReducer(bytes32 hash) external view returns (WitnetV2.RadonReducer memory); - function lookupRadonRetrievalAggregator(bytes32 hash) external view returns (WitnetV2.RadonReducer memory); - function lookupRadonRetrievalResultMaxSize(bytes32 hash) external view returns (uint256); - function lookupRadonRetrievalResultDataType(bytes32 hash) external view returns (WitnetV2.RadonDataTypes); - function lookupRadonRetrievalSources(bytes32 hash) external view returns (bytes32[] memory); - function lookupRadonRetrievalSourcesCount(bytes32 hash) external view returns (uint); - function lookupRadonRetrievalTally(bytes32 hash) external view returns (WitnetV2.RadonReducer memory); + function lookupRadonRequestAggregator(bytes32 hash) external view returns (WitnetV2.RadonReducer memory); + function lookupRadonRequestResultMaxSize(bytes32 hash) external view returns (uint256); + function lookupRadonRequestResultDataType(bytes32 hash) external view returns (WitnetV2.RadonDataTypes); + function lookupRadonRequestSources(bytes32 hash) external view returns (bytes32[] memory); + function lookupRadonRequestSourcesCount(bytes32 hash) external view returns (uint); + function lookupRadonRequestTally(bytes32 hash) external view returns (WitnetV2.RadonReducer memory); function lookupRadonSLA(bytes32 hash) external view returns (WitnetV2.RadonSLA memory); function lookupRadonSLAReward(bytes32 hash) external view returns (uint); @@ -57,13 +57,12 @@ interface IWitnetBytecodes { function verifyRadonReducer(WitnetV2.RadonReducer calldata reducer) external returns (bytes32 hash); - function verifyRadonRetrieval( - WitnetV2.RadonDataTypes resultDataType, - uint16 resultMaxSize, + function verifyRadonRequest( bytes32[] calldata sources, - string[][] calldata sourcesArgs, - bytes32 aggregatorHash, - bytes32 tallyHash + bytes32 aggregator, + bytes32 tally, + uint16 resultMaxSize, + string[][] calldata args ) external returns (bytes32 hash); function verifyRadonSLA(WitnetV2.RadonSLA calldata sla) diff --git a/contracts/libs/WitnetV2.sol b/contracts/libs/WitnetV2.sol index 5a8a2c850..20cffda70 100644 --- a/contracts/libs/WitnetV2.sol +++ b/contracts/libs/WitnetV2.sol @@ -13,11 +13,11 @@ library WitnetV2 { error RadonFilterMissingArgs(uint8 opcode); - error RadonRetrievalNoSources(); - error RadonRetrievalSourcesArgsMismatch(uint expected, uint actual); - error RadonRetrievalMissingArgs(uint index, uint expected, uint actual); - error RadonRetrievalResultsMismatch(uint index, uint8 read, uint8 expected); - error RadonRetrievalTooHeavy(bytes bytecode, uint weight); + error RadonRequestNoSources(); + error RadonRequestSourcesArgsMismatch(uint expected, uint actual); + error RadonRequestMissingArgs(uint index, uint expected, uint actual); + error RadonRequestResultsMismatch(uint index, uint8 read, uint8 expected); + error RadonRequestTooHeavy(bytes bytecode, uint weight); error RadonSlaNoReward(); error RadonSlaNoWitnesses(); diff --git a/test/witnet_bytecodes.test.js b/test/witnet_bytecodes.test.js index b8a680df8..c118cbd40 100644 --- a/test/witnet_bytecodes.test.js +++ b/test/witnet_bytecodes.test.js @@ -110,8 +110,6 @@ contract("WitnetBytecodes", (accounts) => { it("emits appropiate single event when verifying randomness data source for the first time", async () => { const tx = await bytecodes.verifyDataSource( 2, // requestMethod - 0, // resultMinRank - 0, // resultMaxRank "", // requestSchema "", // requestFQDN "", // requestPath @@ -129,8 +127,6 @@ contract("WitnetBytecodes", (accounts) => { it("emits no event when verifying already existing randomness data source", async () => { const tx = await bytecodes.verifyDataSource( 2, // requestMethod - 0, // resultMinRank - 0, // resultMaxRank "", // requestSchema "", // requestFQDN "", // requestPath @@ -144,8 +140,6 @@ contract("WitnetBytecodes", (accounts) => { it("generates proper hash upon offchain verification of already existing randmoness source", async () => { const hash = await bytecodes.verifyDataSource.call( 2, // requestMethod - 0, // resultMinRank - 0, // resultMaxRank "", // requestSchema "", // requestFQDN "", // requestPath @@ -163,8 +157,6 @@ contract("WitnetBytecodes", (accounts) => { "emits new data provider and source events when verifying a new http-get source for the first time", async () => { const tx = await bytecodes.verifyDataSource( 1, // requestMethod - 0, // resultMinRank - 0, // resultMaxRank "HTTPs://", // requestSchema "api.binance.US", // requestFQDN "api/v3/ticker/price", // requestPath @@ -196,8 +188,6 @@ contract("WitnetBytecodes", (accounts) => { it("emits one single event when verifying new http-get endpoint to already existing provider", async () => { const tx = await bytecodes.verifyDataSource( 1, // requestMethod - 0, // resultMinRank - 0, // resultMaxRank "http://", // requestSchema "api.binance.us", // requestFQDN "api/v3/ticker/24hr", // requestPath @@ -218,8 +208,6 @@ contract("WitnetBytecodes", (accounts) => { "emits new data provider and source events when verifying a new http-post source for the first time", async () => { const tx = await bytecodes.verifyDataSource( 3, // requestMethod - 0, // resultMinRank - 0, // resultMaxRank "HTTPs://", // requestSchema "api.thegraph.com", // requestFQDN "subgraphs/name/uniswap/uniswap-v3", // requestPath @@ -354,9 +342,9 @@ contract("WitnetBytecodes", (accounts) => { }) }) - context("verifyRadonRetrieval(..)", async () => { + context("verifyRadonRequest(..)", async () => { context("Use case: Randomness", async () => { - it("emits single event when verifying new radomness retrieval", async () => { + it("emits single event when verifying new radomness request", async () => { let tx = await bytecodes.verifyRadonReducer([ 2, // Mode [], // no filters @@ -368,15 +356,13 @@ contract("WitnetBytecodes", (accounts) => { ) modeNoFiltersReducerHash = tx.logs[0].args.hash // modeNoFiltersReducerBytecode = tx.logs[0].args.bytecode - tx = await bytecodes.verifyRadonRetrieval( - 0, // resultDataType - 0, // resultMaxVariableSize + tx = await bytecodes.verifyRadonRequest( [ // sources rngSourceHash, ], - [[]], // sourcesArgs modeNoFiltersReducerHash, // aggregator concathashReducerHash, // tally + 0, [[]], // sourcesArgs ) assert(tx.logs.length === 1) expectEvent( @@ -384,90 +370,71 @@ contract("WitnetBytecodes", (accounts) => { "NewRadHash" ) rngHash = tx.logs[0].args.hash - // rngBytecode = tx.logs[0].args.bytecode }) - it("emits no event when verifying same randomness retrieval", async () => { - const tx = await bytecodes.verifyRadonRetrieval( - 0, // resultDataType - 0, // resultMaxVariableSize + it("emits no event when verifying same randomness request", async () => { + const tx = await bytecodes.verifyRadonRequest( [ // sources rngSourceHash, ], - [[]], // sourcesArgs modeNoFiltersReducerHash, // aggregator concathashReducerHash, // tally + 0, [[]], // sourcesArgs ) assert(tx.logs.length === 0) }) - it("generates same hash when verifying same randomness retrieval offchain", async () => { - const hash = await bytecodes.verifyRadonRetrieval.call( - 0, // resultDataType - 0, // resultMaxVariableSize + it("generates same hash when verifying same randomness request offchain", async () => { + const hash = await bytecodes.verifyRadonRequest.call( [ // sources rngSourceHash, ], - [[]], // sourcesArgs modeNoFiltersReducerHash, // aggregator concathashReducerHash, // tally + 0, // resultMaxVariableSize + [[]], // sourcesArgs ) assert.equal(hash, rngHash) }) }) context("Use case: Price feeds", async () => { - it("reverts custom error if trying to verify retrieval w/ templated source and 0 args out of 2", async () => { - await expectRevertCustomError( - WitnetBuffer, - bytecodes.verifyRadonRetrieval( - 4, // resultDataType - 0, // resultMaxVariableSize + it("reverts custom error if trying to verify request w/ templated source and 0 args out of 2", async () => { + await expectRevert.unspecified( + bytecodes.verifyRadonRequest( [ // sources binanceTickerHash, ], - [ // sourcesArgs - [], - ], stdev15ReducerHash, // aggregator stdev25ReducerHash, // tally - ), - "MissingArgs", [ - 1, // expected - 0, // given - ] + 0, // resultMaxVariableSize + [ [], ], + ) ) }) - it("reverts custom error if trying to verify retrieval w/ templated source and 1 args out of 2", async () => { - await expectRevertCustomError( - WitnetBuffer, - bytecodes.verifyRadonRetrieval( - 4, // resultDataType - 0, // resultMaxVariableSize + it("reverts custom error if trying to verify request w/ templated source and 1 args out of 2", async () => { + await expectRevert.unspecified( + bytecodes.verifyRadonRequest( [ // sources binanceTickerHash, ], + stdev15ReducerHash, // aggregator + stdev25ReducerHash, // tally + 0, // resultMaxVariableSize [ // sourcesArgs ["BTC"], ], - stdev15ReducerHash, // aggregator - stdev25ReducerHash, // tally - ), - "MissingArgs", [ - 2, // expected - 1, // given - ] + ) ) }) - it("emits single event when verifying new price feed retrieval for the first time", async () => { - const tx = await bytecodes.verifyRadonRetrieval( - 4, // resultDataType - 0, // resultMaxVariableSize, - [ // sources + it("emits single event when verifying new price feed request for the first time", async () => { + const tx = await bytecodes.verifyRadonRequest( + [ // source binanceTickerHash, ], + stdev15ReducerHash, // aggregator + stdev25ReducerHash, // tally + 0, // resultMaxVariableSize, [ ["BTC", "USD"], // binance ticker args ], - stdev15ReducerHash, // aggregator - stdev25ReducerHash, // tally ) assert(tx.logs.length === 1) expectEvent( @@ -477,20 +444,19 @@ contract("WitnetBytecodes", (accounts) => { btcUsdPriceFeedHash = tx.logs[0].args.hash // btcUsdPriceFeedBytecode = tx.logs[0].args.bytecode }) - it("verifying radon retrieval with repeated sources works", async () => { - const tx = await bytecodes.verifyRadonRetrieval( - 4, // resultDataType - 0, // resultMaxVariableSize, + it("verifying radon request with repeated sources works", async () => { + const tx = await bytecodes.verifyRadonRequest( [ // sources binanceTickerHash, binanceTickerHash, ], + stdev15ReducerHash, // aggregator + stdev25ReducerHash, // tally + 0, // resultMaxVariableSize, [ ["BTC", "USD"], // binance ticker args ["BTC", "USD"], // binance ticker args ], - stdev15ReducerHash, // aggregator - stdev25ReducerHash, // tally ) assert(tx.logs.length === 1) expectEvent( @@ -498,42 +464,40 @@ contract("WitnetBytecodes", (accounts) => { "NewRadHash" ) }) - it("reverts if trying to verify radon retrieval w/ incompatible sources", async () => { + it("reverts if trying to verify radon request w/ incompatible sources", async () => { await expectRevertCustomError( WitnetV2, - bytecodes.verifyRadonRetrieval( - 4, // resultDataType - 0, // resultMaxVariableSize, + bytecodes.verifyRadonRequest( [ // sources binanceTickerHash, rngSourceHash, ], + stdev15ReducerHash, // aggregator + stdev25ReducerHash, // tally + 0, // resultMaxVariableSize, [ ["BTC", "USD"], // binance ticker args [], ], - stdev15ReducerHash, // aggregator - stdev25ReducerHash, // tally ), - "RadonRetrievalResultsMismatch", [ + "RadonRequestResultsMismatch", [ 1, // index 0, // read 4, // expected ] ) }) - it("emits single event when verifying new radon retrieval w/ http-post source", async () => { - const tx = await bytecodes.verifyRadonRetrieval( - 4, // resultDataType - 0, // resultMaxVariableSize, + it("emits single event when verifying new radon request w/ http-post source", async () => { + const tx = await bytecodes.verifyRadonRequest( [ // sources uniswapToken1PriceHash, ], + stdev15ReducerHash, // aggregator + stdev25ReducerHash, // tally + 0, // resultMaxVariableSize, [ ["0xc2a856c3aff2110c1171b8f942256d40e980c726"], // pair id ], - stdev15ReducerHash, // aggregator - stdev25ReducerHash, // tally ) assert(tx.logs.length === 1) expectEvent( @@ -541,10 +505,8 @@ contract("WitnetBytecodes", (accounts) => { "NewRadHash" ) }) - it("emits single event when verifying new radon retrieval w/ repeated http-post sources", async () => { - const tx = await bytecodes.verifyRadonRetrieval( - 4, // resultDataType - 0, // resultMaxVariableSize, + it("emits single event when verifying new radon request w/ repeated http-post sources", async () => { + const tx = await bytecodes.verifyRadonRequest( [ // sources uniswapToken1PriceHash, uniswapToken1PriceHash, @@ -553,6 +515,9 @@ contract("WitnetBytecodes", (accounts) => { uniswapToken1PriceHash, uniswapToken1PriceHash, ], + stdev15ReducerHash, // aggregator + stdev25ReducerHash, // tally + 0, // resultMaxVariableSize, [ ["0xc2a856c3aff2110c1171b8f942256d40e980c726"], // pair id ["0xc2a856c3aff2110c1171b8f942256d40e980c726"], // pair id @@ -561,8 +526,6 @@ contract("WitnetBytecodes", (accounts) => { ["0xc2a856c3aff2110c1171b8f942256d40e980c726"], // pair id ["0xc2a856c3aff2110c1171b8f942256d40e980c726"], // pair id ], - stdev15ReducerHash, // aggregator - stdev25ReducerHash, // tally ) assert(tx.logs.length === 1) expectEvent( @@ -578,11 +541,11 @@ contract("WitnetBytecodes", (accounts) => { context("verifyRadonSLA(..)", async () => { it("emits event when verifying new radon sla", async () => { const tx = await bytecodes.verifyRadonSLA([ - 10 ** 6, 10, - 10 ** 6, 51, + 10 ** 9, 5 * 10 ** 9, + 10 ** 6, ]) expectEvent( tx.receipt, @@ -592,11 +555,11 @@ contract("WitnetBytecodes", (accounts) => { }) it("emits no event when verifying an already verified radon sla", async () => { const tx = await bytecodes.verifyRadonSLA([ - 10 ** 6, 10, - 10 ** 6, 51, + 10 ** 9, 5 * 10 ** 9, + 10 ** 6, ]) assert.equal( tx.logs.length, @@ -606,11 +569,11 @@ contract("WitnetBytecodes", (accounts) => { }) it("generates proper hash upon offchain call", async () => { const hash = await bytecodes.verifyRadonSLA.call([ - 10 ** 6, 10, - 10 ** 6, 51, + 10 ** 9, 5 * 10 ** 9, + 10 ** 6, ]) assert.equal(hash, slaHash) }) @@ -618,11 +581,11 @@ contract("WitnetBytecodes", (accounts) => { await expectRevertCustomError( WitnetV2, bytecodes.verifyRadonSLA([ - 0, 10, - 10 ** 6, 51, + 0, 5 * 10 ** 9, + 10 ** 6, ]), "RadonSlaNoReward" ) @@ -631,11 +594,11 @@ contract("WitnetBytecodes", (accounts) => { await expectRevertCustomError( WitnetV2, bytecodes.verifyRadonSLA([ - 10 ** 6, 0, - 10 ** 6, 51, + 10 ** 9, 5 * 10 ** 9, + 10 ** 6 ]), "RadonSlaNoWitnesses" ) @@ -644,11 +607,11 @@ contract("WitnetBytecodes", (accounts) => { await expectRevertCustomError( WitnetV2, bytecodes.verifyRadonSLA([ - 10 ** 6, 500, - 10 ** 6, 51, - 5 * 10 ** 9, + 10 ** 9, + 15 * 10 ** 9, + 10 ** 6, ]), "RadonSlaTooManyWitnesses" ) @@ -657,22 +620,22 @@ contract("WitnetBytecodes", (accounts) => { await expectRevertCustomError( WitnetV2, bytecodes.verifyRadonSLA([ - 10 ** 6, 10, - 10 ** 6, 50, - 5 * 10 ** 9, + 10 ** 9, + 15 * 10 ** 9, + 10 ** 6, ]), "RadonSlaConsensusOutOfRange" ) await expectRevertCustomError( WitnetV2, bytecodes.verifyRadonSLA([ - 10 ** 6, 10, - 10 ** 6, 100, + 10 ** 9, 5 * 10 ** 9, + 10 ** 6, ]), "RadonSlaConsensusOutOfRange" ) @@ -681,11 +644,11 @@ contract("WitnetBytecodes", (accounts) => { await expectRevertCustomError( WitnetV2, bytecodes.verifyRadonSLA([ - 10 ** 6, 10, - 10 ** 6, 51, 10 ** 6, + 10 ** 6, + 10 ** 6, ]), "RadonSlaLowCollateral" ) @@ -693,18 +656,18 @@ contract("WitnetBytecodes", (accounts) => { }) context("bytecodeOf(..)", async () => { - context("radon retrievals", async () => { - it("reverts if trying to get bytecode from unknown radon retrieval", async () => { + context("radon requests", async () => { + it("reverts if trying to get bytecode from unknown radon request", async () => { await expectRevertCustomError( WitnetBytecodes, bytecodes.bytecodeOf("0x0"), - "UnknownRadonRetrieval" + "UnknownRadonRequest" ) }) - it("works if trying to get bytecode onchain from known radon retrieval", async () => { + it("works if trying to get bytecode onchain from known radon request", async () => { await bytecodes.bytecodeOf(btcUsdPriceFeedHash) }) - it("returns bytecode if getting it offchain from known radon retrieval", async () => { + it("returns bytecode if getting it offchain from known radon request", async () => { await bytecodes.bytecodeOf(btcUsdPriceFeedHash) }) }) @@ -716,30 +679,30 @@ contract("WitnetBytecodes", (accounts) => { "UnknownRadonSLA" ) }) - it("works if trying to get bytecode onchain from known radon retrieval and sla", async () => { + it("works if trying to get bytecode onchain from known radon request and sla", async () => { await bytecodes.bytecodeOf(btcUsdPriceFeedHash, slaHash) }) }) }) context("hashOf(..)", async () => { - it("hashing unknown radon retrieval doesn't revert", async () => { + it("hashing unknown radon request doesn't revert", async () => { await bytecodes.hashOf("0x", slaHash) }) it("hashing unknown radon sla doesn't revert", async () => { await bytecodes.hashOf(btcUsdPriceFeedHash, "0x0") }) - it("hashing of known radon retrieval and sla works", async () => { + it("hashing of known radon request and sla works", async () => { await bytecodes.hashOf(btcUsdPriceFeedHash, slaHash) }) }) context("hashWeightRewardOf(..)", async () => { - it("hashing unknown radon retrieval reverts", async () => { + it("hashing unknown radon request reverts", async () => { await expectRevertCustomError( WitnetBytecodes, bytecodes.hashWeightWitsOf("0x0", slaHash), - "UnknownRadonRetrieval" + "UnknownRadonRequest" ) }) it("hashing unknown radon sla reverts", async () => { @@ -749,7 +712,7 @@ contract("WitnetBytecodes", (accounts) => { "UnknownRadonSLA" ) }) - it("hashing of known radon retrieval and sla works", async () => { + it("hashing of known radon request and sla works", async () => { await bytecodes.hashWeightWitsOf( heavyRetrievalHash, slaHash ) From 7e9f4da1f640553e35cccc4249c20db0702dc291 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Sun, 19 Feb 2023 12:24:39 +0100 Subject: [PATCH 098/119] feat(factory): repetead templates or requests return same address --- contracts/apps/WitnetRequestFactory.sol | 109 +++++++++++++++++------- contracts/patterns/Clonable.sol | 20 ++++- 2 files changed, 94 insertions(+), 35 deletions(-) diff --git a/contracts/apps/WitnetRequestFactory.sol b/contracts/apps/WitnetRequestFactory.sol index ea02f2323..8828fac25 100644 --- a/contracts/apps/WitnetRequestFactory.sol +++ b/contracts/apps/WitnetRequestFactory.sol @@ -75,40 +75,72 @@ contract WitnetRequestFactory /// --- IWitnetRequestFactory implementation ---------------------------------------------------------------------- function buildRequest( - bytes32[] memory sources, - bytes32 aggregator, - bytes32 tally, - uint16 resultDataMaxSize + bytes32[] memory _sources, + bytes32 _aggregator, + bytes32 _tally, + uint16 _resultDataMaxSize ) virtual override external onlyOnFactory returns (WitnetRequest _request) { - WitnetRequestTemplate _template = buildRequestTemplate(sources, aggregator, tally, resultDataMaxSize); - require(!_template.parameterized(), "WitnetRequestFactory: parameterized sources"); + WitnetRequestTemplate _template = buildRequestTemplate( + _sources, + _aggregator, + _tally, + _resultDataMaxSize + ); + require( + !_template.parameterized(), + "WitnetRequestFactory: parameterized sources" + ); _request = _template.settleArgs(abi.decode(hex"",(string[][]))); emit WitnetRequestBuilt(_request); } function buildRequestTemplate( - bytes32[] memory sources, - bytes32 aggregator, - bytes32 tally, - uint16 resultDataMaxSize + bytes32[] memory _sources, + bytes32 _aggregator, + bytes32 _tally, + uint16 _resultDataMaxSize ) virtual override public onlyOnFactory returns (WitnetRequestTemplate _template) { - _template = WitnetRequestFactory(_clone()).initializeWitnetRequestTemplate( - sources, - aggregator, - tally, - resultDataMaxSize - ); - emit WitnetRequestTemplateBuilt(_template, _template.parameterized()); + bytes32 _salt = keccak256(abi.encodePacked( + bytes3(0xfabada), // avoids address collisions between templates w/ no args and actual requests + _sources, + _aggregator, + _tally, + _resultDataMaxSize + )); + address _address = address(uint160(uint256(keccak256( + abi.encodePacked( + bytes1(0xff), + address(this), + _salt, + keccak256(_cloneBytecode()) + ) + )))); + if (_address.code.length > 0) { + _template = WitnetRequestTemplate(_address); + } else { + _template = WitnetRequestFactory( + _cloneDeterministic(_salt) + ).initializeWitnetRequestTemplate( + _sources, + _aggregator, + _tally, + _resultDataMaxSize + ); + emit WitnetRequestTemplateBuilt( + _template, + _template.parameterized() + ); + } } function class() @@ -140,7 +172,10 @@ contract WitnetRequestFactory returns (WitnetRequestTemplate) { WitnetV2.RadonDataTypes _resultDataType; - require(_sources.length > 0, "WitnetRequestTemplate: no sources"); + require( + _sources.length > 0, + "WitnetRequestTemplate: no sources" + ); // check all sources return the same data types, // and whether any of them is parameterized bool _parameterized; @@ -174,8 +209,8 @@ contract WitnetRequestFactory } function initializeWitnetRequest( - string[][] memory _args, - bytes32 _radHash + bytes32 _radHash, + string[][] memory _args ) virtual public initializer @@ -589,21 +624,33 @@ contract WitnetRequestFactory virtual override external onlyOnTemplates - returns (WitnetRequest request) + returns (WitnetRequest _request) { WitnetRequestTemplateSlot storage __data = __witnetRequestTemplate(); - bytes32 _radHash = registry.verifyRadonRetrieval( - __data.resultDataType, - __data.resultDataMaxSize, + bytes32 _radHash = registry.verifyRadonRequest( __data.sources, - _args, __data.aggregatorHash, - __data.tallyHash - ); - request = WitnetRequestFactory(_clone()).initializeWitnetRequest( - _args, - _radHash + __data.tallyHash, + __data.resultDataMaxSize, + _args ); - emit WitnetRequestTemplateSettled(request, _radHash, _args); + address _address = address(uint160(uint256(keccak256( + abi.encodePacked( + bytes1(0xff), + address(this), + _radHash, + keccak256(_cloneBytecode()) + ) + )))); + if (_address.code.length > 0) { + _request = WitnetRequest(_address); + } else { + _request = WitnetRequestFactory(_cloneDeterministic(_radHash)) + .initializeWitnetRequest( + _radHash, + _args + ); + emit WitnetRequestTemplateSettled(_request, _radHash, _args); + } } } \ No newline at end of file diff --git a/contracts/patterns/Clonable.sol b/contracts/patterns/Clonable.sol index daa33b0a4..5a30e9fd8 100644 --- a/contracts/patterns/Clonable.sol +++ b/contracts/patterns/Clonable.sol @@ -50,7 +50,7 @@ abstract contract Clonable internal returns (address _instance) { - bytes memory ptr = _cloneBytecode(); + bytes memory ptr = _cloneBytecodePtr(); assembly { // CREATE new instance: _instance := create(0, ptr, 0x37) @@ -61,7 +61,19 @@ abstract contract Clonable /// @notice Returns minimal proxy's deploy bytecode. function _cloneBytecode() - virtual internal + virtual internal view + returns (bytes memory) + { + return abi.encodePacked( + hex"3d602d80600a3d3981f3363d3d373d3d3d363d73", + bytes20(self()), + hex"5af43d82803e903d91602b57fd5bf3" + ); + } + + /// @notice Returns mem pointer to minimal proxy's deploy bytecode. + function _cloneBytecodePtr() + virtual internal view returns (bytes memory ptr) { address _base = self(); @@ -88,7 +100,7 @@ abstract contract Clonable internal returns (address _instance) { - bytes memory ptr = _cloneBytecode(); + bytes memory ptr = _cloneBytecodePtr(); assembly { // CREATE2 new instance: _instance := create2(0, ptr, 0x37, _salt) @@ -96,4 +108,4 @@ abstract contract Clonable require(_instance != address(0), "Clonable: CREATE2 failed"); emit Cloned(msg.sender, self(), _instance); } -} +} \ No newline at end of file From b818914f043abf28a64110e202d1e3e33912239f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Sun, 19 Feb 2023 19:07:06 +0100 Subject: [PATCH 099/119] chore: upgrade to WB v0.6.4 on polygon goerli --- migrations/witnet.addresses.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/migrations/witnet.addresses.json b/migrations/witnet.addresses.json index 9817ed830..ffdefd41f 100644 --- a/migrations/witnet.addresses.json +++ b/migrations/witnet.addresses.json @@ -283,8 +283,8 @@ }, "polygon": { "polygon.goerli": { - "WitnetBytecodes": "", - "WitnetBytecodesImplementation": "", + "WitnetBytecodes": "0xB01A55f7D4324C92E16FC8AAf63d8060DE3CC21c", + "WitnetBytecodesImplementation": "0x32B57F0a4680384Ae9118A74bf0f7A657f8dF39a", "WitnetEncodingLib": "0x0fde3791E85448cACc2Bf1B612C2f07525cE7663", "WitnetLib": "0x70767887abBF23291B24620dE6bcEBe8d8F1e6F3", "WitnetPriceRouter": "0x6d5544ca5b35bf2e7a78ace4E7B8d191fe5C9FAb", From 37f3197971a8be0d2ca217f979fd202085f3809a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Mon, 20 Feb 2023 08:50:58 +0100 Subject: [PATCH 100/119] feat: add curatorship to WitnetRequest instances --- contracts/apps/WitnetRequestFactory.sol | 21 +++++++++++++++++++-- contracts/data/WitnetRequestFactoryData.sol | 2 ++ contracts/requests/WitnetRequest.sol | 1 + 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/contracts/apps/WitnetRequestFactory.sol b/contracts/apps/WitnetRequestFactory.sol index 8828fac25..1b6cb524d 100644 --- a/contracts/apps/WitnetRequestFactory.sol +++ b/contracts/apps/WitnetRequestFactory.sol @@ -209,6 +209,7 @@ contract WitnetRequestFactory } function initializeWitnetRequest( + address _from, bytes32 _radHash, string[][] memory _args ) @@ -218,6 +219,7 @@ contract WitnetRequestFactory { WitnetRequestSlot storage __data = __witnetRequest(); __data.args = _args; + __data.curator = _from; __data.radHash = _radHash; __data.template = WitnetRequestTemplate(msg.sender); return WitnetRequest(address(this)); @@ -404,6 +406,15 @@ contract WitnetRequestFactory return __witnetRequest().args; } + function curator() + override + external view + onlyDelegateCalls + returns (address) + { + return __witnetRequest().curator; + } + function getRadonSLA() override external view @@ -453,6 +464,10 @@ contract WitnetRequestFactory address(_template) != address(0), "WitnetRequestFactory: not a request" ); + require( + msg.sender == __witnetRequest().curator, + "WitnetRequest: not the curator" + ); bytes32 _slaHash = registry.verifyRadonSLA(_sla); WitnetRequestSlot storage __data = __witnetRequest(); bytes memory _bytecode = registry.bytecodeOf(__data.radHash, _slaHash); @@ -634,11 +649,12 @@ contract WitnetRequestFactory __data.resultDataMaxSize, _args ); + bytes32 _salt = keccak256(abi.encodePacked(_radHash, msg.sender)); address _address = address(uint160(uint256(keccak256( abi.encodePacked( bytes1(0xff), address(this), - _radHash, + _salt, keccak256(_cloneBytecode()) ) )))); @@ -647,7 +663,8 @@ contract WitnetRequestFactory } else { _request = WitnetRequestFactory(_cloneDeterministic(_radHash)) .initializeWitnetRequest( - _radHash, + msg.sender, + _salt, _args ); emit WitnetRequestTemplateSettled(_request, _radHash, _args); diff --git a/contracts/data/WitnetRequestFactoryData.sol b/contracts/data/WitnetRequestFactoryData.sol index 11bb37147..c5e614077 100644 --- a/contracts/data/WitnetRequestFactoryData.sol +++ b/contracts/data/WitnetRequestFactoryData.sol @@ -29,6 +29,8 @@ contract WitnetRequestFactoryData { string[][] args; /// Witnet Data Request bytecode after inserting string arguments. bytes bytecode; + /// Address from which the request's SLA can be modifies. + address curator; /// SHA-256 hash of the Witnet Data Request bytecode. bytes32 hash; /// Radon RAD hash. diff --git a/contracts/requests/WitnetRequest.sol b/contracts/requests/WitnetRequest.sol index 7605bfb97..e5704fad7 100644 --- a/contracts/requests/WitnetRequest.sol +++ b/contracts/requests/WitnetRequest.sol @@ -14,6 +14,7 @@ abstract contract WitnetRequest function args() virtual external view returns (string[][] memory); function class() virtual external view returns (bytes4); + function curator() virtual external view returns (address); function getRadonSLA() virtual external view returns (WitnetV2.RadonSLA memory); function initialized() virtual external view returns (bool); function radHash() virtual external view returns (bytes32); From b0dcfcf64b402f11468dd6d1158b5e5d4784aa90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Mon, 20 Feb 2023 10:00:17 +0100 Subject: [PATCH 101/119] fix(factory): avoid address collision from templates w/ different version tags --- contracts/apps/WitnetRequestFactory.sol | 39 ++++++++++++++++---- contracts/impls/WitnetUpgradableBase.sol | 2 +- contracts/requests/WitnetRequest.sol | 3 +- contracts/requests/WitnetRequestTemplate.sol | 1 + 4 files changed, 35 insertions(+), 10 deletions(-) diff --git a/contracts/apps/WitnetRequestFactory.sol b/contracts/apps/WitnetRequestFactory.sol index 1b6cb524d..b6ef08599 100644 --- a/contracts/apps/WitnetRequestFactory.sol +++ b/contracts/apps/WitnetRequestFactory.sol @@ -110,13 +110,18 @@ contract WitnetRequestFactory onlyOnFactory returns (WitnetRequestTemplate _template) { - bytes32 _salt = keccak256(abi.encodePacked( - bytes3(0xfabada), // avoids address collisions between templates w/ no args and actual requests - _sources, - _aggregator, - _tally, - _resultDataMaxSize - )); + bytes32 _salt = keccak256( + // As to avoid template address collisions from: + abi.encodePacked( + // - different factory versions + _WITNET_UPGRADABLE_VERSION, + // - different templates + _sources, + _aggregator, + _tally, + _resultDataMaxSize + ) + ); address _address = address(uint160(uint256(keccak256( abi.encodePacked( bytes1(0xff), @@ -480,6 +485,14 @@ contract WitnetRequestFactory return IWitnetRequest(address(this)); } + function version() + virtual override(WitnetRequest, WitnetRequestTemplate, WitnetUpgradableBase) + public view + returns (string memory) + { + return WitnetUpgradableBase.version(); + } + /// =============================================================================================================== /// --- WitnetRequestTemplate implementation ---------------------------------------------------------------------- @@ -649,7 +662,17 @@ contract WitnetRequestFactory __data.resultDataMaxSize, _args ); - bytes32 _salt = keccak256(abi.encodePacked(_radHash, msg.sender)); + bytes32 _salt = keccak256( + // As to avoid request address collisions from: + abi.encodePacked( + // - different factory versions + _WITNET_UPGRADABLE_VERSION, + // - different curators + msg.sender, + // - different templates or args values + _radHash + ) + ); address _address = address(uint160(uint256(keccak256( abi.encodePacked( bytes1(0xff), diff --git a/contracts/impls/WitnetUpgradableBase.sol b/contracts/impls/WitnetUpgradableBase.sol index a7d4ce6ed..8e9d7515e 100644 --- a/contracts/impls/WitnetUpgradableBase.sol +++ b/contracts/impls/WitnetUpgradableBase.sol @@ -70,7 +70,7 @@ abstract contract WitnetUpgradableBase // --- Overrides 'Upgradeable' -------------------------------------------------------------------------------------- /// Retrieves human-readable version tag of current implementation. - function version() public view override returns (string memory) { + function version() public view virtual override returns (string memory) { return _toString(_WITNET_UPGRADABLE_VERSION); } diff --git a/contracts/requests/WitnetRequest.sol b/contracts/requests/WitnetRequest.sol index e5704fad7..6c9383a00 100644 --- a/contracts/requests/WitnetRequest.sol +++ b/contracts/requests/WitnetRequest.sol @@ -17,8 +17,9 @@ abstract contract WitnetRequest function curator() virtual external view returns (address); function getRadonSLA() virtual external view returns (WitnetV2.RadonSLA memory); function initialized() virtual external view returns (bool); + function modifySLA(WitnetV2.RadonSLA calldata sla) virtual external returns (IWitnetRequest); function radHash() virtual external view returns (bytes32); function slaHash() virtual external view returns (bytes32); function template() virtual external view returns (WitnetRequestTemplate); - function modifySLA(WitnetV2.RadonSLA calldata sla) virtual external returns (IWitnetRequest); + function version() virtual external view returns (string memory); } \ No newline at end of file diff --git a/contracts/requests/WitnetRequestTemplate.sol b/contracts/requests/WitnetRequestTemplate.sol index b17982ea5..1356a84f5 100644 --- a/contracts/requests/WitnetRequestTemplate.sol +++ b/contracts/requests/WitnetRequestTemplate.sol @@ -22,4 +22,5 @@ abstract contract WitnetRequestTemplate function lookupRadonTally() virtual external view returns (WitnetV2.RadonReducer memory); function parameterized() virtual external view returns (bool); function settleArgs(string[][] calldata args) virtual external returns (WitnetRequest); + function version() virtual external view returns (string memory); } \ No newline at end of file From 4304df1946f1e0c7280478f7aef40fc998f8fe40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Mon, 20 Feb 2023 11:34:21 +0100 Subject: [PATCH 102/119] fix(bytecodes): avoid publishing templated authorities as data providers --- contracts/impls/bytecodes/WitnetBytecodes.sol | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/contracts/impls/bytecodes/WitnetBytecodes.sol b/contracts/impls/bytecodes/WitnetBytecodes.sol index 2a8fda072..902aac6cb 100644 --- a/contracts/impls/bytecodes/WitnetBytecodes.sol +++ b/contracts/impls/bytecodes/WitnetBytecodes.sol @@ -603,12 +603,17 @@ contract WitnetBytecodes // ================================================================================================================ // --- Internal state-modifying methods --------------------------------------------------------------------------- - function __pushDataProviderSource(string memory _authority, bytes32 _sourceHash) - internal - virtual + function __pushDataProviderSource( + string memory _authority, + bytes32 _sourceHash + ) + internal virtual returns (bytes32 _hash) { - if (bytes(_authority).length > 0) { + if ( + bytes(_authority).length > 0 + && WitnetBuffer.argsCountOf(bytes(_authority)) == 0 + ) { _hash = keccak256(abi.encodePacked(_authority)); uint _index = __database().providersIndex[_hash]; if (_index == 0) { From f5e55711ac0bca2bc5b1c846e4b18f08e6bf8a63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Mon, 20 Feb 2023 12:28:05 +0100 Subject: [PATCH 103/119] fix(factory): supportsInterface on unproxified factories --- contracts/apps/WitnetRequestFactory.sol | 34 ++++++++++++------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/contracts/apps/WitnetRequestFactory.sol b/contracts/apps/WitnetRequestFactory.sol index b6ef08599..5d06f0e76 100644 --- a/contracts/apps/WitnetRequestFactory.sol +++ b/contracts/apps/WitnetRequestFactory.sol @@ -240,23 +240,24 @@ contract WitnetRequestFactory virtual override returns (bool) { - if (address(this) == _SELF) { + if (__witnetRequest().radHash != bytes32(0)) { + return ( + _interfaceId == type(IWitnetRequest).interfaceId + || _interfaceId == type(WitnetRequest).interfaceId + || _interfaceId == type(WitnetRequestTemplate).interfaceId + ); + } + else if (__witnetRequestTemplate().sources.length > 0) { + return (_interfaceId == type(WitnetRequestTemplate).interfaceId); + } + else if (address(this) == __proxy()) { + return ( + _interfaceId == type(IWitnetRequestFactory).interfaceId + || super.supportsInterface(_interfaceId) + ); + } + else { return (_interfaceId == type(Upgradeable).interfaceId); - } else { - if (address(this) == __proxy()) { - return ( - _interfaceId == type(IWitnetRequestFactory).interfaceId - || super.supportsInterface(_interfaceId) - ); - } else if (__witnetRequest().radHash != bytes32(0)) { - return ( - _interfaceId == type(WitnetRequestTemplate).interfaceId - || _interfaceId == type(WitnetRequest).interfaceId - || _interfaceId == type(IWitnetRequest).interfaceId - ); - } else { - return (_interfaceId == type(WitnetRequestTemplate).interfaceId); - } } } @@ -316,7 +317,6 @@ contract WitnetRequestFactory function initialize(bytes memory) virtual override public - initializer onlyDelegateCalls { // WitnetRequest or WitnetRequestTemplate instances would already be initialized, From 971b200e582e13d16d2ab79db5070321c14c4c9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Mon, 20 Feb 2023 12:48:25 +0100 Subject: [PATCH 104/119] chore: upgrade WB on polygon mumbai --- migrations/witnet.addresses.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/migrations/witnet.addresses.json b/migrations/witnet.addresses.json index ffdefd41f..5a32ced7e 100644 --- a/migrations/witnet.addresses.json +++ b/migrations/witnet.addresses.json @@ -284,8 +284,8 @@ "polygon": { "polygon.goerli": { "WitnetBytecodes": "0xB01A55f7D4324C92E16FC8AAf63d8060DE3CC21c", - "WitnetBytecodesImplementation": "0x32B57F0a4680384Ae9118A74bf0f7A657f8dF39a", - "WitnetEncodingLib": "0x0fde3791E85448cACc2Bf1B612C2f07525cE7663", + "WitnetBytecodesImplementation": "0x0A3a19bA45F54f7031A9aBB66C81e972cE618dD9", + "WitnetEncodingLib": "0x7DeDAafae9Af3A65F664b79e5F82156B5cdcD473", "WitnetLib": "0x70767887abBF23291B24620dE6bcEBe8d8F1e6F3", "WitnetPriceRouter": "0x6d5544ca5b35bf2e7a78ace4E7B8d191fe5C9FAb", "WitnetRandomness": "0x0178287DfECA2F13F342512282bc8B59e774aE43", From 6a3ec410541501795e7520133d6f885c5dab62a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Mon, 20 Feb 2023 12:48:41 +0100 Subject: [PATCH 105/119] chore: redeploy WRF on polygon mumbai --- migrations/witnet.addresses.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/migrations/witnet.addresses.json b/migrations/witnet.addresses.json index 5a32ced7e..977c78652 100644 --- a/migrations/witnet.addresses.json +++ b/migrations/witnet.addresses.json @@ -290,8 +290,8 @@ "WitnetPriceRouter": "0x6d5544ca5b35bf2e7a78ace4E7B8d191fe5C9FAb", "WitnetRandomness": "0x0178287DfECA2F13F342512282bc8B59e774aE43", "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431", - "WitnetRequestFactory": "0x3921cC3eBF8c8a9d0F788Fc6cD371287EEe636Ff", - "WitnetRequestFactoryImplementation": "" + "WitnetRequestFactory": "0x2A0C6942fb6fE55799C070CD88737106e6Cc0467", + "WitnetRequestFactoryImplementation": "0xaDfa4C235C951745A2f28b5ff54Ccc2Ff356384C" }, "polygon.mainnet": { "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", From d3677262bb0b3abc41923ee4ed7fb805aaa39b73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Mon, 20 Feb 2023 19:02:31 +0100 Subject: [PATCH 106/119] fix(factory): _salt on clone --- contracts/apps/WitnetRequestFactory.sol | 4 ++-- migrations/witnet.addresses.json | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/contracts/apps/WitnetRequestFactory.sol b/contracts/apps/WitnetRequestFactory.sol index 5d06f0e76..bfeaf99d6 100644 --- a/contracts/apps/WitnetRequestFactory.sol +++ b/contracts/apps/WitnetRequestFactory.sol @@ -684,10 +684,10 @@ contract WitnetRequestFactory if (_address.code.length > 0) { _request = WitnetRequest(_address); } else { - _request = WitnetRequestFactory(_cloneDeterministic(_radHash)) + _request = WitnetRequestFactory(_cloneDeterministic(_salt)) .initializeWitnetRequest( msg.sender, - _salt, + _radHash, _args ); emit WitnetRequestTemplateSettled(_request, _radHash, _args); diff --git a/migrations/witnet.addresses.json b/migrations/witnet.addresses.json index 977c78652..dde3e9b4f 100644 --- a/migrations/witnet.addresses.json +++ b/migrations/witnet.addresses.json @@ -43,10 +43,15 @@ }, "boba": { "boba.moonbeam.bobabase": { + "WitnetBytecodes": "", + "WitnetBytecodesImplementation": "", + "WitnetEncodingLib": "", "WitnetLib": "0x02Cd4089679EAA9431a88170fd784e7dE78A2425", "WitnetPriceRouter": "0xD9465D38f50f364b3263Cb219e58d4dB2D584530", "WitnetRandomness": "0xB4B2E2e00e9d6E5490d55623E4F403EC84c6D33f", - "WitnetRequestBoard": "0x0985FDe9f424fe4f5AC516F66bAf5591e18aCBEb" + "WitnetRequestBoard": "0x0985FDe9f424fe4f5AC516F66bAf5591e18aCBEb", + "WitnetRequestFactory": "", + "WitnetRequestFactoryImplementation": "" }, "boba.ethereum.goerli": { "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", @@ -291,7 +296,7 @@ "WitnetRandomness": "0x0178287DfECA2F13F342512282bc8B59e774aE43", "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431", "WitnetRequestFactory": "0x2A0C6942fb6fE55799C070CD88737106e6Cc0467", - "WitnetRequestFactoryImplementation": "0xaDfa4C235C951745A2f28b5ff54Ccc2Ff356384C" + "WitnetRequestFactoryImplementation": "0x285A3Fabb1E96aD80Bc3767AF08ACfcA71Bd14D2" }, "polygon.mainnet": { "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", From 2baaca868bbead17e0e261a6aaef5f0c3b31f9dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Mon, 20 Feb 2023 19:52:30 +0100 Subject: [PATCH 107/119] fix(factory): emit event on settleArgs even though no new request is created --- contracts/apps/WitnetRequestFactory.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/apps/WitnetRequestFactory.sol b/contracts/apps/WitnetRequestFactory.sol index bfeaf99d6..1c00d6491 100644 --- a/contracts/apps/WitnetRequestFactory.sol +++ b/contracts/apps/WitnetRequestFactory.sol @@ -690,7 +690,7 @@ contract WitnetRequestFactory _radHash, _args ); - emit WitnetRequestTemplateSettled(_request, _radHash, _args); } + emit WitnetRequestTemplateSettled(_request, _radHash, _args); } } \ No newline at end of file From ae1ec83e44c87a155dca8eef70281714a58dc636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Mon, 20 Feb 2023 20:06:09 +0100 Subject: [PATCH 108/119] fix(factory): avoid regenerating bytecode on modifySLA if there's no actual change --- contracts/apps/WitnetRequestFactory.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/apps/WitnetRequestFactory.sol b/contracts/apps/WitnetRequestFactory.sol index 1c00d6491..cc1243ac5 100644 --- a/contracts/apps/WitnetRequestFactory.sol +++ b/contracts/apps/WitnetRequestFactory.sol @@ -475,11 +475,11 @@ contract WitnetRequestFactory ); bytes32 _slaHash = registry.verifyRadonSLA(_sla); WitnetRequestSlot storage __data = __witnetRequest(); - bytes memory _bytecode = registry.bytecodeOf(__data.radHash, _slaHash); - { + if (_slaHash != __data.slaHash) { + bytes memory _bytecode = registry.bytecodeOf(__data.radHash, _slaHash); __data.bytecode = _bytecode; __data.hash = Witnet.hash(_bytecode); - __data.slaHash = _slaHash; + __data.slaHash = _slaHash; emit WitnetRequestSettled(_sla); } return IWitnetRequest(address(this)); From a0bbeb6f8d460d927ac543a2788621d5b7760156 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Mon, 20 Feb 2023 20:06:36 +0100 Subject: [PATCH 109/119] chore: bump package version to 0.6.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index eea63a920..5dc3ed6d1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "witnet-solidity-bridge", - "version": "0.6.4", + "version": "0.6.5", "description": "Witnet Solidity Bridge contracts for EVM-compatible chains", "main": "", "scripts": { From 9b67b4d001ee1e2e074983dfe6f2068fb34c1926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Mon, 20 Feb 2023 20:08:58 +0100 Subject: [PATCH 110/119] chore: upgrade WRF to v0.6.5 on polygon mumbai --- migrations/witnet.addresses.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations/witnet.addresses.json b/migrations/witnet.addresses.json index dde3e9b4f..c2fb349e2 100644 --- a/migrations/witnet.addresses.json +++ b/migrations/witnet.addresses.json @@ -296,7 +296,7 @@ "WitnetRandomness": "0x0178287DfECA2F13F342512282bc8B59e774aE43", "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431", "WitnetRequestFactory": "0x2A0C6942fb6fE55799C070CD88737106e6Cc0467", - "WitnetRequestFactoryImplementation": "0x285A3Fabb1E96aD80Bc3767AF08ACfcA71Bd14D2" + "WitnetRequestFactoryImplementation": "0x6B25A89dF5458aA8028358B8eA491EbD4EAe30b2" }, "polygon.mainnet": { "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", From 43c277425567da41e67c40d96b61345bf71cb103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Tue, 21 Feb 2023 13:52:03 +0100 Subject: [PATCH 111/119] fix(libs): buf in WitnetBuffer.replace when wildcard on first position of string --- contracts/libs/WitnetBuffer.sol | 4 +++- test/TestWitnetBuffer.sol | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/contracts/libs/WitnetBuffer.sol b/contracts/libs/WitnetBuffer.sol index b6984553c..b1631e124 100644 --- a/contracts/libs/WitnetBuffer.sol +++ b/contracts/libs/WitnetBuffer.sol @@ -463,7 +463,9 @@ library WitnetBuffer { ); inputPointer += inputLength + 3; outputPointer += inputLength; - } + } else { + inputPointer += 3; + } uint ax = uint(uint8(input[ix + 1]) - uint8(bytes1("0"))); if (ax >= args.length) { revert MissingArgs(ax + 1, args.length); diff --git a/test/TestWitnetBuffer.sol b/test/TestWitnetBuffer.sol index ac2240812..a216c96e9 100644 --- a/test/TestWitnetBuffer.sol +++ b/test/TestWitnetBuffer.sol @@ -87,6 +87,20 @@ contract TestWitnetBuffer { // TODO } + function testReplace() external { + string memory input = "\\0\\/image/\\1\\?digest=sha-256"; + string[] memory args = new string[](2); + args[0] = "https://api.whatever.com/"; + args[1] = "1"; + bytes memory phrase = WitnetBuffer.replace(bytes(input), args); + emit Log(string(phrase), phrase.length); + Assert.equal( + keccak256(phrase), + keccak256(bytes("https://api.whatever.com//image/1?digest=sha-256")), + "String replacement not good :/" + ); + } + function testReplace0Args() external { string memory input = "In a village of La Mancha, the name of which I have no desire to call to mind, there lived not long since one of those gentlemen that keep a lance in the lance-rack, an old buckler, a lean hack, and a greyhound for coursing"; string[] memory args = new string[](1); From b2a3b40329679fce42c9722651388f2b7ed49fe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Tue, 21 Feb 2023 13:55:48 +0100 Subject: [PATCH 112/119] chore: upgrade WB+lib on polygon mumbai --- migrations/witnet.addresses.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/migrations/witnet.addresses.json b/migrations/witnet.addresses.json index c2fb349e2..7d03d73d7 100644 --- a/migrations/witnet.addresses.json +++ b/migrations/witnet.addresses.json @@ -289,8 +289,8 @@ "polygon": { "polygon.goerli": { "WitnetBytecodes": "0xB01A55f7D4324C92E16FC8AAf63d8060DE3CC21c", - "WitnetBytecodesImplementation": "0x0A3a19bA45F54f7031A9aBB66C81e972cE618dD9", - "WitnetEncodingLib": "0x7DeDAafae9Af3A65F664b79e5F82156B5cdcD473", + "WitnetBytecodesImplementation": "0xA0f7B13F163e7790EaFFf4c3f490a97Ef8DD6d6e", + "WitnetEncodingLib": "0xf0e3C3B59a9f032CBD9ef461ffBF89A3Cf8445BD", "WitnetLib": "0x70767887abBF23291B24620dE6bcEBe8d8F1e6F3", "WitnetPriceRouter": "0x6d5544ca5b35bf2e7a78ace4E7B8d191fe5C9FAb", "WitnetRandomness": "0x0178287DfECA2F13F342512282bc8B59e774aE43", From 3abe7c8141cf16d6b221cecfb5e0fc97d6b0eaa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 1 Mar 2023 17:01:58 +0100 Subject: [PATCH 113/119] refactor(test): to latest changes --- .../requests/WitnetRequestMalleableBase.sol | 1 - scripts/utils.js | 2 +- test/using_witnet.test.js | 1 + test/witnet_bytecodes.test.js | 7 +- test/witnet_requests.test.js | 469 ------------------ test/wrb.test.js | 5 +- 6 files changed, 7 insertions(+), 478 deletions(-) delete mode 100644 test/witnet_requests.test.js diff --git a/contracts/requests/WitnetRequestMalleableBase.sol b/contracts/requests/WitnetRequestMalleableBase.sol index 23d2d8f74..5adf27547 100644 --- a/contracts/requests/WitnetRequestMalleableBase.sol +++ b/contracts/requests/WitnetRequestMalleableBase.sol @@ -265,7 +265,6 @@ abstract contract WitnetRequestMalleableBase /// @dev Initializes witnessing params and template bytecode. function _initialize(bytes memory _template) virtual internal - initializer { _transferOwnership(_msgSender()); diff --git a/scripts/utils.js b/scripts/utils.js index 4781c5ebc..0421b199c 100644 --- a/scripts/utils.js +++ b/scripts/utils.js @@ -101,4 +101,4 @@ function saveAddresses (addrs) { JSON.stringify(addrs, null, 4), { flag: "w+" } ) -} \ No newline at end of file +} diff --git a/test/using_witnet.test.js b/test/using_witnet.test.js index 5ad6106cd..24474478f 100644 --- a/test/using_witnet.test.js +++ b/test/using_witnet.test.js @@ -27,6 +27,7 @@ contract("UsingWitnet", accounts => { before(async () => { witnet = await WitnetLib.deployed() + await WRB.link(WitnetLib, witnet.address) if (!proxy) { // create one and only proxy contract: proxy = await WRBProxy.new({ from: ownerAccount }) diff --git a/test/witnet_bytecodes.test.js b/test/witnet_bytecodes.test.js index c118cbd40..00f0f37de 100644 --- a/test/witnet_bytecodes.test.js +++ b/test/witnet_bytecodes.test.js @@ -3,7 +3,6 @@ const { expectEvent, expectRevert } = require("@openzeppelin/test-helpers") const { assert } = require("chai") const { expectRevertCustomError } = require("custom-error-test-helper") -const WitnetBuffer = artifacts.require("WitnetBuffer") const WitnetBytecodes = artifacts.require("WitnetBytecodes") const WitnetV2 = artifacts.require("WitnetV2") @@ -99,7 +98,6 @@ contract("WitnetBytecodes", (accounts) => { let binanceTickerHash let uniswapToken1PriceHash let heavyRetrievalHash - let heavyRetrievalBytecode let rngHash @@ -405,7 +403,7 @@ contract("WitnetBytecodes", (accounts) => { stdev15ReducerHash, // aggregator stdev25ReducerHash, // tally 0, // resultMaxVariableSize - [ [], ], + [[]], ) ) }) @@ -533,7 +531,6 @@ contract("WitnetBytecodes", (accounts) => { "NewRadHash" ) heavyRetrievalHash = tx.logs[0].args.hash - heavyRetrievalBytecode = await bytecodes.bytecodeOf.call(heavyRetrievalHash) }) }) }) @@ -598,7 +595,7 @@ contract("WitnetBytecodes", (accounts) => { 51, 10 ** 9, 5 * 10 ** 9, - 10 ** 6 + 10 ** 6, ]), "RadonSlaNoWitnesses" ) diff --git a/test/witnet_requests.test.js b/test/witnet_requests.test.js deleted file mode 100644 index cf4cd7ef4..000000000 --- a/test/witnet_requests.test.js +++ /dev/null @@ -1,469 +0,0 @@ -const WitnetRequestRandomness = artifacts.require("WitnetRequestRandomness") -const truffleAssert = require("truffle-assertions") -const { assert } = require("chai") - -contract("WitnetRequest implementations", accounts => { - const master = accounts[0] - const deputy = accounts[1] - const stranger = accounts[2] - const cloner = accounts[3] - const cloneCloner = accounts[4] - describe("WitnetRequestRandomness", async () => { - let rng, clone, cloneClone - before(async () => { - rng = await WitnetRequestRandomness.new({ from: master }) - }) - describe("Proxiable", async () => { - it("proxiableUUID() concurs with expected value", async () => { - const uuid = await rng.proxiableUUID() - // console.log(uuid) - assert.equal( - uuid, - "0x851d0a92a3ad30295bef33afc69d6874779826b7789386b336e22621365ed2c2" - ) - }) - }) - describe("Ownable", async () => { - it("owner() concurs with expected value", async () => { - const owner = await rng.owner.call() - // console.log(owner) - assert.equal(owner, master) - }) - it("stranger cannot transfer ownership", async () => { - await truffleAssert.reverts( - rng.transferOwnership(stranger, { from: stranger }), - "not the owner" - ) - }) - it("owner can transfer ownership", async () => { - await rng.transferOwnership(deputy, { from: master }) - assert(await rng.owner.call(), deputy) - }) - it("ownership cannot be transferred to zero address", async () => { - await truffleAssert.reverts( - rng.transferOwnership("0x0000000000000000000000000000000000000000", { from: deputy }), - "zero address" - ) - }) - it("ownership can be renounced", async () => { - await rng.renounceOwnership({ from: deputy }) - assert( - await rng.owner.call(), - "0x0000000000000000000000000000000000000000" - ) - }) - it("ownership cannot be recovered", async () => { - await truffleAssert.reverts( - rng.transferOwnership(deputy, { from: deputy }), - "not the owner" - ) - await truffleAssert.reverts( - rng.transferOwnership(deputy, { from: master }), - "not the owner" - ) - }) - }) - describe("Clonable", async () => { - before(async () => { - const tx = await rng.clone({ from: cloner }) - const args = getEventArgs(tx.logs, "Cloned") - clone = await WitnetRequestRandomness.at(args.clone) - }) - it("master copy is no clone", async () => { - assert.equal(false, await rng.cloned.call()) - }) - it("first level clones correctly refer to master copy", async () => { - assert.equal(rng.address, await clone.self.call()) - }) - it("clones belong to the cloner, not the master's owner", async () => { - const owner = await clone.owner() - // console.log(owner) - assert.equal(owner, cloner) - }) - it("clones are fully initialized after cloning", async () => { - const bytecode = await clone.bytecode.call() - // console.log(bytecode) - assert(bytecode.length > 0) - }) - it("clones recognize themselves as such", async () => { - assert.equal(true, await clone.cloned.call()) - }) - it("clones can be cloned", async () => { - const tx = await clone.clone({ from: cloneCloner }) - const args = getEventArgs(tx.logs, "Cloned") - assert.notEqual(args.clone, clone.address) - cloneClone = await WitnetRequestRandomness.at(args.clone) - }) - it("clone of clone keeps referring to master copy", async () => { - assert.equal(rng.address, await cloneClone.self.call()) - }) - }) - describe("Initializable", async () => { - describe("master copy", async () => { - it("cannot be re-initialized", async () => { - await truffleAssert.reverts( - rng.initialize("0x80", { from: master }), - "already initialized" - ) - await truffleAssert.reverts( - rng.initialize("0x80", { from: deputy }), - "already initialized" - ) - await truffleAssert.reverts( - rng.initialize("0x80", { from: stranger }), - "already initialized" - ) - }) - }) - describe("clone copy", async () => { - it("cannot be re-initialized by owner", async () => { - await truffleAssert.reverts( - clone.initialize("0x80", { from: cloner }), - "already initialized" - ) - }) - it("cannot be re-initialized by anybody else", async () => { - await truffleAssert.reverts( - clone.initialize("0x80", { from: stranger }), - "already initialized" - ) - }) - }) - describe("clone of clone copy", async () => { - before(async () => { - cloneClone.transferOwnership(deputy, { from: cloneCloner }) - }) - it("cannot be re-initialized by owner", async () => { - await truffleAssert.reverts( - cloneClone.initialize("0x80", { from: deputy }), - "already initialized" - ) - }) - it("cannot re-initialized by anybody else", async () => { - await truffleAssert.reverts( - cloneClone.initialize("0x80", { from: cloneCloner }), - "already initialized" - ) - await truffleAssert.reverts( - cloneClone.initialize("0x80", { from: cloner }), - "already initialized" - ) - await truffleAssert.reverts( - clone.initialize("0x80", { from: stranger }), - "already initialized" - ) - }) - }) - }) - describe("IWitnetRequest", async () => { - before(async () => { - rng = await WitnetRequestRandomness.new({ from: master }) - }) - it("bytecode() concurs with expected default value", async () => { - const bytecode = await rng.bytecode.call() - // console.log(bytecode) - assert.equal( - "0x0a0f120508021a01801a0210022202100b10a0c21e18022090a10f2833308094ebdc03", - bytecode - ) - }) - it("hash() concurs with expected default value", async () => { - const hash = await rng.hash.call() - // console.log(hash) - assert.equal( - "0x0dd4be45fe46949658d276b2a9f8550f72c3352692cdcd718d16b87924fbc113", - hash - ) - }) - }) - describe("WitnetRequestMalleableBase", async () => { - before(async () => { - rng = await WitnetRequestRandomness.new({ from: master }) - }) - describe("master copy", async () => { - it("witnessingParams() concurs with expected default values", async () => { - const params = await rng.witnessingParams.call() - // console.log(params) - assert.equal(2, params.numWitnesses) - assert.equal(51, params.minWitnessingConsensus) - assert.equal(1000000000, params.witnessingCollateral) - assert.equal(500000, params.witnessingReward) - assert.equal(250000, params.witnessingUnitaryFee) - }) - describe("setWitnessingCollateral(.)", async () => { - it("stranger cannot change the witnessing collateral", async () => { - await truffleAssert.reverts( - rng.setWitnessingCollateral( - 10 ** 9, - { from: stranger } - ), - "not the owner" - ) - }) - it("owner can change witnessing collateral to an acceptable value", async () => { - await rng.setWitnessingCollateral( - "1000000000000000000", - { from: master } - ) - assert.equal( - (await rng.witnessingParams.call()).witnessingCollateral, - "1000000000000000000" - ) - }) - it("the witnessing collateral cannot be set to less than 1 WIT", async () => { - await truffleAssert.reverts( - rng.setWitnessingCollateral( - 10 ** 9 - 1, - { from: master } - ), - "collateral below" - ) - }) - it("bytecode() concurs with last set parameters", async () => { - const bytecode = await rng.bytecode.call() - // console.log(bytecode) - assert.equal( - "0x0a0f120508021a01801a0210022202100b10a0c21e18022090a10f283330808090bbbad6adf00d", - bytecode - ) - }) - }) - describe("setWitnessingFees(..)", async () => { - it("stranger cannot change the witnessing fees", async () => { - await truffleAssert.reverts( - rng.setWitnessingFees( - 5 * 10 ** 5, - 25 * 10 ** 4, - { from: stranger } - ), - "not the owner" - ) - }) - it("owner can change witnessing fees to acceptable values", async () => { - await rng.setWitnessingFees( - 10 ** 6, - 50 * 10 ** 4, - { from: master } - ) - const params = await rng.witnessingParams.call() - assert.equal(params.witnessingReward, "1000000") - assert.equal(params.witnessingUnitaryFee, "500000") - }) - it("the witnessing reward cannot be set to zero", async () => { - await truffleAssert.reverts( - rng.setWitnessingFees(0, 50 * 10 ** 4, { from: master }), - "no reward" - ) - }) - it("bytecode() concurs with last set parameters", async () => { - const bytecode = await rng.bytecode.call() - // console.log(bytecode) - assert.equal( - "0x0a0f120508021a01801a0210022202100b10c0843d180220a0c21e283330808090bbbad6adf00d", - bytecode - ) - }) - }) - describe("setWitnessingQuorum(..)", async () => { - it("stranger cannot change the witnessing quorum", async () => { - await truffleAssert.reverts( - rng.setWitnessingQuorum(2, 51, { from: stranger }), - "not the owner" - ) - }) - it("owner can change witnessing to quorum to acceptable values", async () => { - await rng.setWitnessingQuorum(7, 67, { from: master }) - const params = await rng.witnessingParams.call() - assert.equal(7, params.numWitnesses) - assert.equal(67, params.minWitnessingConsensus) - }) - it("number of witnesses cannot be set to zero", async () => { - await truffleAssert.reverts( - rng.setWitnessingQuorum(0, 67, { from: master }), - "witnesses out of range" - ) - }) - it("number of witnesses cannot be set to more than 127", async () => { - await truffleAssert.reverts( - rng.setWitnessingQuorum(128, 67, { from: master }), - "witnesses out of range" - ) - }) - it("witnessing quorum cannot be set to less than 51", async () => { - await truffleAssert.reverts( - rng.setWitnessingQuorum(7, 50, { from: master }), - "consensus out of range" - ) - }) - it("witnessing quorum cannot be set to more than 100", async () => { - await truffleAssert.reverts( - rng.setWitnessingQuorum(7, 100, { from: master }), - "consensus out of range" - ) - }) - it("bytecode() concurs with last set parameters", async () => { - const bytecode = await rng.bytecode.call() - // console.log(bytecode) - assert.equal( - "0x0a0f120508021a01801a0210022202100b10c0843d180720a0c21e284330808090bbbad6adf00d", - bytecode - ) - }) - }) - }) - describe("clone copy", async () => { - before(async () => { - const tx = await rng.clone({ from: cloner }) - const args = getEventArgs(tx.logs, "Cloned") - clone = await WitnetRequestRandomness.at(args.clone) - // console.log(await rng.witnessingParams.call()) - }) - it("witnessingParams() concurs with expected default values", async () => { - const params = await clone.witnessingParams.call() - // console.log(params) - assert.equal(2, params.numWitnesses) - assert.equal(51, params.minWitnessingConsensus) - assert.equal(1000000000, params.witnessingCollateral) - assert.equal(500000, params.witnessingReward) - assert.equal(250000, params.witnessingUnitaryFee) - }) - describe("setWitnessingCollateral(.)", async () => { - it("stranger cannot change the witnessing collateral", async () => { - await truffleAssert.reverts( - clone.setWitnessingCollateral( - 10 ** 9, - { from: stranger } - ), - "not the owner" - ) - }) - it("owner can change witnessing collateral to an acceptable value", async () => { - await clone.setWitnessingCollateral( - "1000000000000000000", - { from: cloner } - ) - assert.equal( - (await clone.witnessingParams.call()).witnessingCollateral, - "1000000000000000000" - ) - }) - it("the witnessing collateral cannot be set to less than 1 WIT", async () => { - await truffleAssert.reverts( - clone.setWitnessingCollateral( - 10 ** 9 - 1, - { from: cloner } - ), - "collateral below" - ) - }) - it("bytecode() concurs with last set parameters", async () => { - const bytecode = await clone.bytecode.call() - // console.log(bytecode) - assert.equal( - "0x0a0f120508021a01801a0210022202100b10a0c21e18022090a10f283330808090bbbad6adf00d", - bytecode - ) - }) - }) - describe("setWitnessingFees(..)", async () => { - it("stranger cannot change the witnessing fees", async () => { - await truffleAssert.reverts( - clone.setWitnessingFees( - 5 * 10 ** 5, - 25 * 10 ** 4, - { from: stranger } - ), - "not the owner" - ) - }) - it("owner can change witnessing fees to acceptable values", async () => { - await clone.setWitnessingFees( - 10 ** 9, - 50 * 10 ** 4, - { from: cloner } - ) - const params = await clone.witnessingParams.call() - assert.equal(params.witnessingReward, "1000000000") - assert.equal(params.witnessingUnitaryFee, "500000") - }) - it("the witnessing reward cannot be set to zero", async () => { - await truffleAssert.reverts( - clone.setWitnessingFees(0, 50 * 10 ** 4, { from: cloner }), - "no reward" - ) - }) - it("the witnessing reward cannot be set to zero", async () => { - await truffleAssert.reverts( - clone.setWitnessingFees(0, 50 * 10 ** 4, { from: cloner }), - "no reward" - ) - }) - it("bytecode() concurs with last set parameters", async () => { - const bytecode = await clone.bytecode.call() - // console.log(bytecode) - assert.equal( - "0x0a0f120508021a01801a0210022202100b108094ebdc03180220a0c21e283330808090bbbad6adf00d", - bytecode - ) - }) - }) - describe("setWitnessingQuorum(..)", async () => { - it("stranger cannot change the witnessing quorum", async () => { - await truffleAssert.reverts( - clone.setWitnessingQuorum(2, 51, { from: stranger }), - "not the owner" - ) - }) - it("owner can change witnessing to quorum to acceptable values", async () => { - await clone.setWitnessingQuorum(7, 67, { from: cloner }) - const params = await clone.witnessingParams.call() - assert.equal(7, params.numWitnesses) - assert.equal(67, params.minWitnessingConsensus) - }) - it("number of witnesses cannot be set to zero", async () => { - await truffleAssert.reverts( - clone.setWitnessingQuorum(0, 67, { from: cloner }), - "witnesses out of range" - ) - }) - it("number of witnesses cannot be set to more than 127", async () => { - await truffleAssert.reverts( - clone.setWitnessingQuorum(128, 67, { from: cloner }), - "witnesses out of range" - ) - }) - it("witnessing quorum cannot be set to less than 51", async () => { - await truffleAssert.reverts( - clone.setWitnessingQuorum(7, 50, { from: cloner }), - "consensus out of range" - ) - }) - it("witnessing quorum cannot be set to more than 100", async () => { - await truffleAssert.reverts( - clone.setWitnessingQuorum(7, 100, { from: cloner }), - "consensus out of range" - ) - }) - it("bytecode() concurs with last set parameters", async () => { - const bytecode = await clone.bytecode.call() - // console.log(bytecode) - assert.equal( - "0x0a0f120508021a01801a0210022202100b108094ebdc03180720a0c21e284330808090bbbad6adf00d", - bytecode - ) - }) - }) - }) - }) - }) -}) - -function getEventArgs (logs, event) { - if (logs && logs.length > 0) { - for (let j = 0; j < logs.length; j++) { - if (logs[j].event === event) { - return logs[j].args - } - } - } -} diff --git a/test/wrb.test.js b/test/wrb.test.js index da19aabf9..c993ae8a4 100644 --- a/test/wrb.test.js +++ b/test/wrb.test.js @@ -11,7 +11,7 @@ const { expect, assert } = require("chai") // Contracts const WRB = artifacts.require(settings.artifacts.default.WitnetRequestBoard) - +const WitnetLib = artifacts.require("WitnetLib") const WitnetRequest = artifacts.require("WitnetRequestTestHelper") const WitnetRequestTestHelper = artifacts.require("WitnetRequestTestHelper") @@ -33,6 +33,7 @@ contract("WitnetRequestBoard", ([ other, ]) => { beforeEach(async () => { + await WRB.link(WitnetLib, WitnetLib.address) this.WitnetRequestBoard = await WRB.new( ...settings.constructorParams.default.WitnetRequestBoard, { from: owner } @@ -728,7 +729,7 @@ contract("WitnetRequestBoard", ([ web3.eth.abi.encodeParameter("address[]", [other]), { from: owner } ), - "already initialized" + "already upgraded" ) }) }) From f129d1b21a8b81e06d512e18d767d51534a8ec43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 1 Mar 2023 17:03:28 +0100 Subject: [PATCH 114/119] chore: fmt! --- contracts/impls/WitnetUpgradableBase.sol | 1 + migrations/scripts/1_deploy_witnet.js | 2 -- migrations/scripts/4_deploy_bytecodes.js | 6 +++--- migrations/scripts/5_deploy_factory.js | 6 +++--- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/contracts/impls/WitnetUpgradableBase.sol b/contracts/impls/WitnetUpgradableBase.sol index 8e9d7515e..bd687d324 100644 --- a/contracts/impls/WitnetUpgradableBase.sol +++ b/contracts/impls/WitnetUpgradableBase.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MIT // solhint-disable var-name-mixedcase +// solhint-disable payable-fallback pragma solidity >=0.8.0 <0.9.0; diff --git a/migrations/scripts/1_deploy_witnet.js b/migrations/scripts/1_deploy_witnet.js index ea77c21f2..ea6594d5c 100644 --- a/migrations/scripts/1_deploy_witnet.js +++ b/migrations/scripts/1_deploy_witnet.js @@ -4,8 +4,6 @@ const utils = require("../../scripts/utils") module.exports = async function (deployer, network, accounts) { if (network === "test") { - const WitnetRequestBoardTrustlessReporting1 = artifacts.require("WitnetRequestBoardTrustlessReporting1") - await deployer.deploy(WitnetRequestBoardTrustlessReporting1, true, utils.fromAscii("testing")) const WitnetLib = artifacts.require("WitnetLib") await deployer.deploy(WitnetLib) const WitnetEncodingLib = artifacts.require("WitnetEncodingLib") diff --git a/migrations/scripts/4_deploy_bytecodes.js b/migrations/scripts/4_deploy_bytecodes.js index f9323c588..bfa2c02cb 100644 --- a/migrations/scripts/4_deploy_bytecodes.js +++ b/migrations/scripts/4_deploy_bytecodes.js @@ -74,8 +74,8 @@ module.exports = async function (deployer, network, accounts) { console.info(" > WitnetBytecodes implementation:", implementation) console.info(" > WitnetBytecodesImplementation:", bytecodes.address, `(v${await bytecodes.version()})`) if ( - isDryRun - || ["y", "yes"].includes((await utils.prompt(" > Upgrade the proxy ? [y/N] ")).toLowerCase().trim()) + isDryRun || + ["y", "yes"].includes((await utils.prompt(" > Upgrade the proxy ? [y/N] ")).toLowerCase().trim()) ) { await proxy.upgradeTo(bytecodes.address, "0x") console.info(" > Done.") @@ -84,4 +84,4 @@ module.exports = async function (deployer, network, accounts) { } } } -} \ No newline at end of file +} diff --git a/migrations/scripts/5_deploy_factory.js b/migrations/scripts/5_deploy_factory.js index 56504c758..653148ce9 100644 --- a/migrations/scripts/5_deploy_factory.js +++ b/migrations/scripts/5_deploy_factory.js @@ -55,8 +55,8 @@ module.exports = async function (deployer, network, accounts) { console.info(" > WitnetRequestFactory implementation:", implementation) console.info(" > WitnetRequestFactoryImplementation:", factory.address, `(v${await factory.version()})`) if ( - isDryRun - || ["y", "yes"].includes((await utils.prompt(" > Upgrade the proxy ? [y/N] ")).toLowerCase().trim()) + isDryRun || + ["y", "yes"].includes((await utils.prompt(" > Upgrade the proxy ? [y/N] ")).toLowerCase().trim()) ) { await proxy.upgradeTo(factory.address, "0x") console.info(" > Done.") @@ -65,4 +65,4 @@ module.exports = async function (deployer, network, accounts) { } } } -} \ No newline at end of file +} From 71e5c510012b372f35140b366d210ac481d4d571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Wed, 1 Mar 2023 17:28:40 +0100 Subject: [PATCH 115/119] chore: deprecate node 14.x in github actions --- .github/workflows/nodejs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index abb61cdeb..773b33401 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: - node-version: [14.x, 16.x] + node-version: [16.x] steps: - uses: actions/checkout@v1 From 2295a0b31d69e4274874262de0c79cb5f9ece244 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 3 Mar 2023 12:36:06 +0100 Subject: [PATCH 116/119] chore: refactor flattening scripts --- package.json | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 5dc3ed6d1..6aed77e36 100644 --- a/package.json +++ b/package.json @@ -12,14 +12,16 @@ "console": "truffle console", "coverage": "solidity-coverage", "flatten": "node ./scripts/flatten.js 2>&1", - "flatten:witnet": "npm run clean && npm run flatten:witnet:libs && npm run flatten:witnet:proxy && npm run flatten:witnet:boards && npm run flatten:witnet:rng && npm run flatten:witnet:registry && npm run flatten:witnet:bytecodes", - "flatten:witnet:rng": "npm run flatten contracts/apps/WitnetRandomness.sol && npm run flatten contracts/requests/WitnetRequestRandomness.sol", - "flatten:witnet:libs": "npm run flatten contracts/libs/WitnetLib.sol", - "flatten:witnet:proxy": "npm run flatten contracts/impls/WitnetProxy.sol", - "flatten:witnet:registry": "npm run flatten contracts/apps/WitnetPriceRouter.sol", + "flatten:witnet": "npm run clean && npm run flatten:witnet:libs && npm run flatten:witnet:proxy && npm run flatten:witnet:boards && npm run flatten:witnet:bytecodes && npm run flatten:witnet:apps", + "flatten:witnet:apps": "npm run flatten:witnet:apps:randomness && npm run flatten:witnet:apps:router && npm rum flatten:witnet:apps:factory", + "flatten:witnet:apps:factory": "npm run flatten contracts/apps/WitnetRequestFactory.sol", + "flatten:witnet:apps:randomness": "npm run flatten contracts/apps/WitnetRandomness.sol && npm run flatten contracts/requests/WitnetRequestRandomness.sol", + "flatten:witnet:apps:router": "npm run flatten contracts/apps/WitnetPriceRouter.sol", "flatten:witnet:boards": "npm run flatten:witnet:boards:trustable", "flatten:witnet:boards:trustable": "npm run flatten contracts/impls/boards/trustable/", - "flatten:witnet:bytecodes": "npm run flatten contracts/impls/bytecodes", + "flatten:witnet:bytecodes": "npm run flatten contracts/impls/bytecodes/", + "flatten:witnet:libs": "npm run flatten contracts/libs/WitnetLib.sol && npm run flatten contracts/libs/WitnetEncodingLib.sol", + "flatten:witnet:proxy": "npm run flatten contracts/impls/WitnetProxy.sol", "fmt:js": "eslint \"**/*.js\"", "fmt:sol": "solhint --max-warnings 0 \"contracts/**/*.sol\" && solhint \"test/**/*.sol\" && solhint \"flattened/**/*.sol\"", "fmt!:js": "eslint \"**/*.js\" --fix", From 5ec84beaec47967bdf815b666ddefd9919475501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 3 Mar 2023 12:36:48 +0100 Subject: [PATCH 117/119] chore: upgrade WSB to v0.6.5 on Moonbase --- migrations/witnet.addresses.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/migrations/witnet.addresses.json b/migrations/witnet.addresses.json index 7d03d73d7..44c0edf12 100644 --- a/migrations/witnet.addresses.json +++ b/migrations/witnet.addresses.json @@ -235,15 +235,15 @@ }, "moonbeam": { "moonbeam.moonbase": { - "WitnetBytecodes": "", - "WitnetBytecodesImplementation": "", - "WitnetEncodingLib": "", + "WitnetBytecodes": "0x4Daa9A7880b27305B881F84e9D4c5Dc808ac4F7f", + "WitnetBytecodesImplementation": "0xd38CeD27B2A27DD4e62765a6fFB5F7078E346b54", + "WitnetEncodingLib": "0x92aD6266E77DE33E4135B199D79DCf004521cad3", "WitnetLib": "0xeD074DA2A76FD2Ca90C1508930b4FB4420e413B0", "WitnetPriceRouter": "0x56834Ff8D4b27db647Da97CA3bd8540f7fA0e89D", "WitnetRandomness": "0x45111778a7db1356DaAB576cBe73681F0745182c", "WitnetRequestBoard": "0x02Cd4089679EAA9431a88170fd784e7dE78A2425", - "WitnetRequestFactory": "0x34F14Fa1b0a6B6FBd1D9d4A62c77C4D0F9d1E9a9", - "WitnetRequestFactoryImplementation": "" + "WitnetRequestFactory": "0xecad2534fecD949A2e8751aB73ccD7c7e193A19F", + "WitnetRequestFactoryImplementation": "0xcf7D253BC0367A7Bf60762A803644a91435574e8" }, "moonbeam.moonriver": { "WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079", From 5c13979bc4c10c7560961e6d687882b7eeecbc05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Fri, 3 Mar 2023 12:37:10 +0100 Subject: [PATCH 118/119] chore: upgrade WSB to v0.6.5 on Ethereum Mainnet --- migrations/witnet.addresses.json | 7 ++++++- migrations/witnet.settings.js | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/migrations/witnet.addresses.json b/migrations/witnet.addresses.json index 44c0edf12..466b2246d 100644 --- a/migrations/witnet.addresses.json +++ b/migrations/witnet.addresses.json @@ -13,10 +13,15 @@ "WitnetRequestBoard": "0x6cE42a35C61ccfb42907EEE57eDF14Bb69C7fEF4" }, "ethereum.mainnet": { + "WitnetBytecodes": "0xB8CdB2577dA632a77D6b90526aB53Eb5694Ed2D1", + "WitnetBytecodesImplementation": "0x93b3bab878CFc17f784fA4B52a2fdE630dc7E8C8", + "WitnetEncodingLib": "0x01673796C51EDfFba3EAb7bf308bEA352D73A017", "WitnetLib": "0xaD18Fd3CC724A11c2B0D8cc7f1B108d8A3388416", "WitnetPriceRouter": "0x83A757eAe821Ad7B520D9A74952337138A80b2AF", "WitnetRandomness": "0x894907c7Ab64C1092620B5c8Ba039BB6E611eba8", - "WitnetRequestBoard": "0x9E4fae1c7ac543a81E4E2a5486a0dDaad8194bdA" + "WitnetRequestBoard": "0x9E4fae1c7ac543a81E4E2a5486a0dDaad8194bdA", + "WitnetRequestFactory": "0xdABE9E1B328d5Dd6b96271e5562ee3f7D8D035c4", + "WitnetRequestFactoryImplementation": "0xBfe9473720CC8DCa9615b8B6CFE228C7e1089396" } }, "arbitrum": { diff --git a/migrations/witnet.settings.js b/migrations/witnet.settings.js index b8da0ddae..0c0294835 100644 --- a/migrations/witnet.settings.js +++ b/migrations/witnet.settings.js @@ -187,6 +187,7 @@ module.exports = { network_id: 1, host: "localhost", port: 9545, + skipDryRun: true, }, "ethereum.rinkeby": { network_id: 4, From b53ec98e1b247ef230771e091c4aed079604253d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Thu, 9 Mar 2023 12:55:10 +0100 Subject: [PATCH 119/119] feat(factory): fork request if no curator tries to modify SLA --- contracts/apps/WitnetRequestFactory.sol | 88 ++++++++++++++++++------- package.json | 6 +- 2 files changed, 66 insertions(+), 28 deletions(-) diff --git a/contracts/apps/WitnetRequestFactory.sol b/contracts/apps/WitnetRequestFactory.sol index cc1243ac5..ad66b66e8 100644 --- a/contracts/apps/WitnetRequestFactory.sol +++ b/contracts/apps/WitnetRequestFactory.sol @@ -214,7 +214,7 @@ contract WitnetRequestFactory } function initializeWitnetRequest( - address _from, + address _curator, bytes32 _radHash, string[][] memory _args ) @@ -224,12 +224,34 @@ contract WitnetRequestFactory { WitnetRequestSlot storage __data = __witnetRequest(); __data.args = _args; - __data.curator = _from; + __data.curator = _curator; __data.radHash = _radHash; __data.template = WitnetRequestTemplate(msg.sender); return WitnetRequest(address(this)); } + function forkWitnetRequest( + address _curator, + bytes32 _slaHash + ) + virtual public + initializer + returns (WitnetRequest) + { + WitnetRequest parent = WitnetRequest(msg.sender); + WitnetRequestSlot storage __data = __witnetRequest(); + bytes32 _radHash = parent.radHash(); + __data.args = parent.args(); + __data.curator = _curator; + __data.radHash = _radHash; + __data.slaHash = _slaHash; + __data.template = parent.template(); + bytes memory _bytecode = registry.bytecodeOf(_radHash, _slaHash); + __data.bytecode = _bytecode; + __data.hash = Witnet.hash(_bytecode); + return WitnetRequest(address(this)); + } + // ================================================================================================================ // ---Overrides 'IERC165' ----------------------------------------------------------------------------------------- @@ -469,18 +491,27 @@ contract WitnetRequestFactory address(_template) != address(0), "WitnetRequestFactory: not a request" ); - require( - msg.sender == __witnetRequest().curator, - "WitnetRequest: not the curator" - ); bytes32 _slaHash = registry.verifyRadonSLA(_sla); WitnetRequestSlot storage __data = __witnetRequest(); if (_slaHash != __data.slaHash) { - bytes memory _bytecode = registry.bytecodeOf(__data.radHash, _slaHash); - __data.bytecode = _bytecode; - __data.hash = Witnet.hash(_bytecode); - __data.slaHash = _slaHash; emit WitnetRequestSettled(_sla); + if (msg.sender == __witnetRequest().curator) { + bytes memory _bytecode = registry.bytecodeOf(__data.radHash, _slaHash); + __data.bytecode = _bytecode; + __data.hash = Witnet.hash(_bytecode); + __data.slaHash = _slaHash; + } else { + (address _addr, bytes32 _salt) = _calcRequestAddrSalt(msg.sender, __data.radHash); + if (_addr.code.length > 0) { + return IWitnetRequest(_addr); + } else { + return WitnetRequestFactory(_cloneDeterministic(_salt)) + .forkWitnetRequest( + msg.sender, + _slaHash + ); + } + } } return IWitnetRequest(address(this)); } @@ -650,7 +681,7 @@ contract WitnetRequestFactory function settleArgs(string[][] memory _args) virtual override - external + public onlyOnTemplates returns (WitnetRequest _request) { @@ -662,18 +693,36 @@ contract WitnetRequestFactory __data.resultDataMaxSize, _args ); - bytes32 _salt = keccak256( + (address _address, bytes32 _salt) = _calcRequestAddrSalt(msg.sender, _radHash); + if (_address.code.length > 0) { + _request = WitnetRequest(_address); + } else { + _request = WitnetRequestFactory(_cloneDeterministic(_salt)) + .initializeWitnetRequest( + msg.sender, + _radHash, + _args + ); + } + emit WitnetRequestTemplateSettled(_request, _radHash, _args); + } + + function _calcRequestAddrSalt(address _curator, bytes32 _radHash) + virtual internal view + returns (address _addr, bytes32 _salt) + { + _salt = keccak256( // As to avoid request address collisions from: abi.encodePacked( // - different factory versions _WITNET_UPGRADABLE_VERSION, // - different curators - msg.sender, + _curator, // - different templates or args values _radHash ) ); - address _address = address(uint160(uint256(keccak256( + _addr = address(uint160(uint256(keccak256( abi.encodePacked( bytes1(0xff), address(this), @@ -681,16 +730,5 @@ contract WitnetRequestFactory keccak256(_cloneBytecode()) ) )))); - if (_address.code.length > 0) { - _request = WitnetRequest(_address); - } else { - _request = WitnetRequestFactory(_cloneDeterministic(_salt)) - .initializeWitnetRequest( - msg.sender, - _radHash, - _args - ); - } - emit WitnetRequestTemplateSettled(_request, _radHash, _args); } } \ No newline at end of file diff --git a/package.json b/package.json index 6aed77e36..cad749278 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "witnet-solidity-bridge", - "version": "0.6.5", + "version": "0.6.6", "description": "Witnet Solidity Bridge contracts for EVM-compatible chains", "main": "", "scripts": { @@ -51,7 +51,6 @@ }, "devDependencies": { "@openzeppelin/test-helpers": "~0.5.16", - "@witnet/truffle-flattener-single-experimental": "^0.1.0", "chai": "4.3.6", "custom-error-test-helper": "^1.0.6", "dotenv": "8.2.0", @@ -66,6 +65,7 @@ "solidity-coverage": "0.7.16", "solidity-stringutils": "https://github.com/Arachnid/solidity-stringutils/", "truffle": "~5.7.2", - "truffle-assertions": "0.9.2" + "truffle-assertions": "0.9.2", + "truffle-flattener": "~1.6.0" } }