diff --git a/README.md b/README.md index 9f4f4c6ec..6baeb4572 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. @@ -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 · │ ···································································|·············|·············|·············|··············|·············· -| WitnetDecoderLib · - · - · 2036059 · 30.3 % · - │ -···································································|·············|·············|·············|··············|·············· -| WitnetParserLib · - · - · 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/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/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/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/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/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/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..f5559d3ac --- /dev/null +++ b/contracts/impls/WitnetUpgradableBase.sol @@ -0,0 +1,113 @@ +// 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; + + error AlreadyInitialized(address implementation); + error NotCompliant(bytes4 interfaceId); + error NotUpgradable(address self); + error OnlyOwner(address owner); + + constructor( + bool _upgradable, + bytes32 _versionTag, + string memory _proxiableUUID + ) + Upgradable(_upgradable) + { + _WITNET_UPGRADABLE_VERSION = _versionTag; + proxiableUUID = keccak256(bytes(_proxiableUUID)); + } + + receive() external payable virtual; + + /// @dev Reverts if proxy delegatecalls to unexistent method. + fallback() external payable { + 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 91% rename from contracts/impls/trustable/WitnetRequestBoardTrustableBase.sol rename to contracts/impls/boards/trustable/WitnetRequestBoardTrustableBase.sol index fb7dcbf68..531c95684 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' ------------------------------------------------------------------------------------- @@ -46,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: @@ -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,7 @@ abstract contract WitnetRequestBoardTrustableBase override returns (Witnet.Result memory) { - return WitnetParserLib.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) - external pure - override - returns (Witnet.Result memory) - { - return WitnetParserLib.resultFromCborValue(_cborValue); + return WitnetLib.resultFromCborBytes(_cborBytes); } /// Tell if a Witnet.Result is successful. @@ -586,7 +598,7 @@ abstract contract WitnetRequestBoardTrustableBase override returns (bool) { - return _result.isOk(); + return _result.succeeded(); } /// Tell if a Witnet.Result is errored. @@ -597,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. @@ -621,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. @@ -642,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`. @@ -700,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. @@ -744,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/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/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol b/contracts/impls/boards/trustless/WitnetRequestBoardTrustlessBase.sol new file mode 100644 index 000000000..85ffde368 --- /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.lookupRadonSLAReward(_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).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 _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 _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 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 diff --git a/contracts/interfaces/IWitnetRequestParser.sol b/contracts/interfaces/IWitnetRequestParser.sol index 0cef865c9..0aefa35f9 100644 --- a/contracts/interfaces/IWitnetRequestParser.sol +++ b/contracts/interfaces/IWitnetRequestParser.sol @@ -17,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(Witnet.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. @@ -32,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. @@ -47,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. @@ -97,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/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..9698d4e23 --- /dev/null +++ b/contracts/interfaces/V2/IWitnetDecoder.sol @@ -0,0 +1,103 @@ +// 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 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..38d16715d --- /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 _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 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 diff --git a/contracts/libs/Witnet.sol b/contracts/libs/Witnet.sol index f9e934877..b0f02a971 100644 --- a/contracts/libs/Witnet.sol +++ b/contracts/libs/Witnet.sol @@ -3,10 +3,14 @@ pragma solidity >=0.7.0 <0.9.0; pragma experimental ABIEncoderV2; +import "./WitnetCBOR.sol"; 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) { @@ -48,26 +52,12 @@ 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. + WitnetCBOR.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; - } + /// =============================================================================================================== + /// --- Witnet error codes table ---------------------------------------------------------------------------------- - /// Iterable bytes buffer. - struct Buffer { - bytes data; - uint32 cursor; - } - - /// Witnet error codes table. enum ErrorCodes { // 0x00: Unknown error. Something went really bad! Unknown, @@ -370,4 +360,5 @@ library Witnet { /// 0xFF: Some tally error is not intercepted but should UnhandledIntercept } + } diff --git a/contracts/libs/WitnetBuffer.sol b/contracts/libs/WitnetBuffer.sol index 78ee55849..f1db05d15 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,211 +10,341 @@ import "./Witnet.sol"; /// @author The Witnet Foundation. library WitnetBuffer { + error EmptyBuffer(); + error IndexOutOfBounds(uint index, uint range); + + /// Iterable bytes buffer. + struct Buffer { + bytes data; + 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 `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) { - // 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 `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 + 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. - /// @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, + 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. /// @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) { - 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 `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 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 `Witnet.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) { - bytes memory bytesValue = _buffer.data; - uint32 offset = _buffer.cursor; - uint8 value; + /// @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) + { + 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 `Witnet.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) { - bytes memory bytesValue = _buffer.data; - uint32 offset = _buffer.cursor; - uint16 value; + /// @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) + { + 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 `Witnet.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) { - bytes memory bytesValue = _buffer.data; - uint32 offset = _buffer.cursor; - uint32 value; + /// @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) + { + 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 `Witnet.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) { - bytes memory bytesValue = _buffer.data; - uint32 offset = _buffer.cursor; - uint64 value; + /// @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) + { + 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 `Witnet.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) { - bytes memory bytesValue = _buffer.data; - uint32 offset = _buffer.cursor; - uint128 value; + /// @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) + { + 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 `Witnet.Buffer`. - function readUint256(Witnet.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; + /// @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) + { + 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 `Witnet.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) { - 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. @@ -226,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 { @@ -239,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/WitnetCBOR.sol b/contracts/libs/WitnetCBOR.sol new file mode 100644 index 000000000..de4ab04f5 --- /dev/null +++ b/contracts/libs/WitnetCBOR.sol @@ -0,0 +1,544 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.0 <0.9.0; + +import "./WitnetBuffer.sol"; + +/// @title A minimalistic implementation of “RFC 7049 Concise Binary Object Representation” +/// @notice This library leverages a buffer-like structure for step-by-step decoding of bytes so as to minimize +/// the gas cost of decoding them into a useful native type. +/// @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 Map (majorType = 5) +/// TODO: add support for Float32 (majorType = 7, additionalInformation = 26) +/// TODO: add support for Float64 (majorType = 7, additionalInformation = 27) + +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; + 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 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 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; + } + } + if (_majorType > 7) { + revert UnsupportedMajorType(_majorType); + } + return CBOR( + _buffer, + _initialByte, + _majorType, + _additionalInformation, + 0, + _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); + } + } + + /// 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); + } else { + revert UnsupportedMajorType(_cbor.majorType); + } + } + + 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(); + } + } + + 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); + } + + 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 { + revert InvalidLengthEncoding(_cbor.additionalInformation); + } + } + _cbor.buffer.seek(_offset); + return _valueFromBuffer(_cbor.buffer); + } else { + revert UnexpectedMajorType(_cbor.majorType, 1); + } + } + + 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 _valueFromBuffer(_cbor.buffer); + } + + 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); + } + + 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]); + } + } + + 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; + } + + 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); + } + } + } + + /// @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); + } + } + + /// @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)); + } + } + + /// @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); + } + } + + /// @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); + } + } + + /// @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)); + } + else { + 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) + 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); + } + } + + /// @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 { + _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 ++; + } + } + } else { + revert InvalidLengthEncoding(_length); + } + } + + /// @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/WitnetDecoderLib.sol b/contracts/libs/WitnetDecoderLib.sol deleted file mode 100644 index ad89ef6a0..000000000 --- a/contracts/libs/WitnetDecoderLib.sol +++ /dev/null @@ -1,318 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.7.0 <0.9.0; -pragma experimental ABIEncoderV2; - -import "./WitnetBuffer.sol"; - -/// @title A minimalistic implementation of “RFC 7049 Concise Binary Object Representation” -/// @notice This library leverages a buffer-like structure for step-by-step decoding of bytes so as to minimize -/// the gas cost of decoding them into a useful native type. -/// @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) - -library WitnetDecoderLib { - - using WitnetBuffer for Witnet.Buffer; - - 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`. - /// @return The value represented by the input, as a `bool` value. - function decodeBool(Witnet.CBOR memory _cborValue) public 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"); - 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"); - } - } - - /// @notice Decode a `Witnet.CBOR` structure into a native `bytes` value. - /// @param _cborValue An instance of `Witnet.CBOR`. - /// @return The value represented by the input, as a `bytes` value. - function decodeBytes(Witnet.CBOR memory _cborValue) public 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)); - } - } - return bytesData; - } else { - return _cborValue.buffer.read(uint32(_cborValue.len)); - } - } - - /// @notice Decode a `Witnet.CBOR` structure into a native `bytes32` value. - /// @param _cborValue An instance of `Witnet.CBOR`. - /// @return _bytes32 The value represented by the input, as a `bytes32` value. - function decodeBytes32(Witnet.CBOR memory _cborValue) public 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); - } - } - - /// @notice Decode a `Witnet.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`. - /// @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"); - return _cborValue.buffer.readFloat16(); - } - - /// @notice Decode a `Witnet.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`. - /// @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"); - - uint64 length = readLength(_cborValue.buffer, _cborValue.additionalInformation); - require(length < _UINT64_MAX, "WitnetDecoderLib: 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); - array[i] = decodeFixed16(item); - } - - return array; - } - - /// @notice Decode a `Witnet.CBOR` structure into a native `int128` value. - /// @param _cborValue An instance of `Witnet.CBOR`. - /// @return The value represented by the input, as an `int128` value. - function decodeInt128(Witnet.CBOR memory _cborValue) public 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))); - } - revert("WitnetDecoderLib: Tried to read `int128` from a `Witnet.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`. - /// @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"); - - uint64 length = readLength(_cborValue.buffer, _cborValue.additionalInformation); - require(length < _UINT64_MAX, "WitnetDecoderLib: 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); - array[i] = decodeInt128(item); - } - - return array; - } - - /// @notice Decode a `Witnet.CBOR` structure into a native `string` value. - /// @param _cborValue An instance of `Witnet.CBOR`. - /// @return The value represented by the input, as a `string` value. - function decodeString(Witnet.CBOR memory _cborValue) public 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)); - } else { - done = true; - } - } - return string(textData); - } else { - return string(readText(_cborValue.buffer, _cborValue.len)); - } - } - - /// @notice Decode a `Witnet.CBOR` structure into a native `string[]` value. - /// @param _cborValue An instance of `Witnet.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"); - - uint64 length = readLength(_cborValue.buffer, _cborValue.additionalInformation); - require(length < _UINT64_MAX, "WitnetDecoderLib: 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); - array[i] = decodeString(item); - } - - return array; - } - - /// @notice Decode a `Witnet.CBOR` structure into a native `uint64` value. - /// @param _cborValue An instance of `Witnet.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"); - return readLength(_cborValue.buffer, _cborValue.additionalInformation); - } - - /// @notice Decode a `Witnet.CBOR` structure into a native `uint64[]` value. - /// @param _cborValue An instance of `Witnet.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"); - - uint64 length = readLength(_cborValue.buffer, _cborValue.additionalInformation); - require(length < _UINT64_MAX, "WitnetDecoderLib: 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); - 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. - /// @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 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. - /// @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"); - - 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; - } - } - - require(majorType <= 7, "WitnetDecoderLib: Invalid CBOR major type"); - - return Witnet.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(Witnet.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("WitnetDecoderLib: 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) { - 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"); - 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) { - 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; - } else { - value = (value & 0x0f) << 18 | - (_buffer.readUint8() & 0x3f) << 12 | - (_buffer.readUint8() & 0x3f) << 6 | - (_buffer.readUint8() & 0x3f); - _length -= 3; - } - } - result = abi.encodePacked(result, value); - } - return result; - } -} diff --git a/contracts/libs/WitnetLib.sol b/contracts/libs/WitnetLib.sol new file mode 100644 index 000000000..5e9b30558 --- /dev/null +++ b/contracts/libs/WitnetLib.sol @@ -0,0 +1,674 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.0 <0.9.0; + +import "./WitnetV2.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 WitnetLib { + + using WitnetCBOR for WitnetCBOR.CBOR; + using WitnetCBOR for WitnetCBOR.CBOR[]; + using WitnetLib for bytes; + + /// =============================================================================================================== + /// --- 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) + { + 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) + 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); + } + + /// @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) + { + if (_stageIndex == 0) { + return "retrieval"; + } else if (_stageIndex == 1) { + return "aggregation"; + } else if (_stageIndex == 2) { + return "tally"; + } else { + return "unknown"; + } + } + + + /// =============================================================================================================== + /// --- 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 The `bool` decoded from the Witnet.Result. + function asBool(Witnet.Result memory _result) + public pure + returns (bool) + { + 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) + public pure + returns(bytes memory) + { + 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) + public pure + returns (bytes32) + { + 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) + public pure + returns (Witnet.ErrorCodes) + { + uint[] memory _errors = _errorsFromResult(_result); + if (_errors.length == 0) { + return Witnet.ErrorCodes.Unknown; + } else { + 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) + public pure + returns ( + Witnet.ErrorCodes _errorCode, + string memory _errorString + ) + { + uint[] memory _errors = _errorsFromResult(_result); + if (_errors.length == 0) { + return ( + Witnet.ErrorCodes.Unknown, + "Unknown error: no error code." + ); + } + else { + _errorCode = Witnet.ErrorCodes(_errors[0]); + } + if ( + _errorCode == Witnet.ErrorCodes.SourceScriptNotCBOR + && _errors.length >= 2 + ) { + _errorString = string(abi.encodePacked( + "Source script #", + toString(uint8(_errors[1])), + " was not a valid CBOR value" + )); + } else if ( + _errorCode == Witnet.ErrorCodes.SourceScriptNotArray + && _errors.length >= 2 + ) { + _errorString = string(abi.encodePacked( + "The CBOR value in script #", + toString(uint8(_errors[1])), + " was not an Array of calls" + )); + } else if ( + _errorCode == Witnet.ErrorCodes.SourceScriptNotRADON + && _errors.length >= 2 + ) { + _errorString = string(abi.encodePacked( + "The CBOR value in script #", + toString(uint8(_errors[1])), + " was not a valid Data Request" + )); + } else if ( + _errorCode == Witnet.ErrorCodes.RequestTooManySources + && _errors.length >= 2 + ) { + _errorString = string(abi.encodePacked( + "The request contained too many sources (", + toString(uint8(_errors[1])), + ")" + )); + } else if ( + _errorCode == Witnet.ErrorCodes.ScriptTooManyCalls + && _errors.length >= 4 + ) { + _errorString = string(abi.encodePacked( + "Script #", + toString(uint8(_errors[2])), + " from the ", + _stageName(uint8(_errors[1])), + " stage contained too many calls (", + toString(uint8(_errors[3])), + ")" + )); + } else if ( + _errorCode == Witnet.ErrorCodes.UnsupportedOperator + && _errors.length >= 5 + ) { + _errorString = string(abi.encodePacked( + "Operator code 0x", + toHexString(uint8(_errors[4])), + " found at call #", + toString(uint8(_errors[3])), + " in script #", + toString(uint8(_errors[2])), + " from ", + _stageName(uint8(_errors[1])), + " stage is not supported" + )); + } else if ( + _errorCode == Witnet.ErrorCodes.HTTP + && _errors.length >= 3 + ) { + _errorString = string(abi.encodePacked( + "Source #", + 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)) + )); + } else if ( + _errorCode == Witnet.ErrorCodes.RetrievalTimeout + && _errors.length >= 2 + ) { + _errorString = string(abi.encodePacked( + "Source #", + toString(uint8(_errors[1])), + " could not be retrieved because of a timeout" + )); + } else if ( + _errorCode == Witnet.ErrorCodes.Underflow + && _errors.length >= 5 + ) { + _errorString = string(abi.encodePacked( + "Underflow at operator code 0x", + toHexString(uint8(_errors[4])), + " found at call #", + toString(uint8(_errors[3])), + " in script #", + toString(uint8(_errors[2])), + " from ", + _stageName(uint8(_errors[1])), + " stage" + )); + } else if ( + _errorCode == Witnet.ErrorCodes.Overflow + && _errors.length >= 5 + ) { + _errorString = string(abi.encodePacked( + "Overflow at operator code 0x", + toHexString(uint8(_errors[4])), + " found at call #", + toString(uint8(_errors[3])), + " in script #", + toString(uint8(_errors[2])), + " from ", + _stageName(uint8(_errors[1])), + " stage" + )); + } else if ( + _errorCode == Witnet.ErrorCodes.DivisionByZero + && _errors.length >= 5 + ) { + _errorString = string(abi.encodePacked( + "Division by zero at operator code 0x", + toHexString(uint8(_errors[4])), + " found at call #", + toString(uint8(_errors[3])), + " in script #", + toString(uint8(_errors[2])), + " from ", + _stageName(uint8(_errors[1])), + " stage" + )); + } 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 { + _errorString = string(abi.encodePacked( + "Unknown error (0x", + toHexString(uint8(_errors[0])), + ")" + )); + } + return ( + _errorCode, + _errorString + ); + } + + /// @notice 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 asFixed16(Witnet.Result memory _result) + public pure + returns (int32) + { + 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 `int32[]` array. + /// @param _result An instance of Witnet.Result. + /// @return The `int128[]` decoded from the Witnet.Result. + function asFixed16Array(Witnet.Result memory _result) + public pure + returns (int32[] memory) + { + 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 `int` decoded from the Witnet.Result. + function asInt(Witnet.Result memory _result) + public pure + returns (int) + { + 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 `int[]` array. + /// @param _result An instance of Witnet.Result. + /// @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 `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) + public pure + returns(string memory) + { + 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) + public pure + returns (string[] memory) + { + 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 `uint` value. + /// @param _result An instance of Witnet.Result. + /// @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 result" + ); + 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. + /// @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 `uint[]` value from errored result." + ); + return _result.value.readUintArray(); + } + + /// ----------------------------- 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 (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 replaceCborBytesWildcards( + bytes memory _cborBytes, + string[] memory _args + ) + public pure + returns (WitnetCBOR.CBOR memory _cbor) + { + _cbor = WitnetCBOR.valueFromBytes(_cborBytes); + _cbor.readArray().replaceWildcards(_args); + } + + /// @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 diff --git a/contracts/libs/WitnetParserLib.sol b/contracts/libs/WitnetParserLib.sol deleted file mode 100644 index a2dc6bfdf..000000000 --- a/contracts/libs/WitnetParserLib.sol +++ /dev/null @@ -1,409 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.7.0 <0.9.0; -pragma experimental ABIEncoderV2; - -import "./WitnetDecoderLib.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 { - - using WitnetDecoderLib for bytes; - using WitnetDecoderLib for Witnet.CBOR; - - /// @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) - { - Witnet.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) - public 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); - } - - /// @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) - { - return _result.success; - } - - /// @notice Tell if a Witnet.Result is errored. - /// @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 !_result.success; - } - - /// @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 - returns(bytes memory) - { - require(_result.success, "WitnetParserLib: Tried to read bytes value from errored Witnet.Result"); - return _result.value.decodeBytes(); - } - - /// @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) - { - require(_result.success, "WitnetParserLib: tried to read bytes32 value from errored Witnet.Result"); - return _result.value.decodeBytes32(); - } - - /// @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 - returns (Witnet.ErrorCodes) - { - uint64[] memory error = asRawError(_result); - if (error.length == 0) { - return Witnet.ErrorCodes.Unknown; - } - 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. - function asErrorMessage(Witnet.Result memory _result) - public pure - returns (Witnet.ErrorCodes, string memory) - { - uint64[] memory error = asRawError(_result); - if (error.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( - "Source script #", - _utoa(error[1]), - " was not a valid CBOR value" - ); - } else if (errorCode == Witnet.ErrorCodes.SourceScriptNotArray && error.length >= 2) { - errorMessage = abi.encodePacked( - "The CBOR value in script #", - _utoa(error[1]), - " was not an Array of calls" - ); - } else if (errorCode == Witnet.ErrorCodes.SourceScriptNotRADON && error.length >= 2) { - errorMessage = abi.encodePacked( - "The CBOR value in script #", - _utoa(error[1]), - " was not a valid Data Request" - ); - } else if (errorCode == Witnet.ErrorCodes.RequestTooManySources && error.length >= 2) { - errorMessage = abi.encodePacked( - "The request contained too many sources (", - _utoa(error[1]), - ")" - ); - } else if (errorCode == Witnet.ErrorCodes.ScriptTooManyCalls && error.length >= 4) { - errorMessage = abi.encodePacked( - "Script #", - _utoa(error[2]), - " from the ", - stageName(error[1]), - " stage contained too many calls (", - _utoa(error[3]), - ")" - ); - } else if (errorCode == Witnet.ErrorCodes.UnsupportedOperator && error.length >= 5) { - errorMessage = abi.encodePacked( - "Operator code 0x", - _utohex(error[4]), - " found at call #", - _utoa(error[3]), - " in script #", - _utoa(error[2]), - " from ", - stageName(error[1]), - " stage is not supported" - ); - } else if (errorCode == Witnet.ErrorCodes.HTTP && error.length >= 3) { - errorMessage = abi.encodePacked( - "Source #", - _utoa(error[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( - "Source #", - _utoa(error[1]), - " could not be retrieved because of a timeout" - ); - } else if (errorCode == Witnet.ErrorCodes.Underflow && error.length >= 5) { - errorMessage = abi.encodePacked( - "Underflow at operator code 0x", - _utohex(error[4]), - " found at call #", - _utoa(error[3]), - " in script #", - _utoa(error[2]), - " from ", - stageName(error[1]), - " stage" - ); - } else if (errorCode == Witnet.ErrorCodes.Overflow && error.length >= 5) { - errorMessage = abi.encodePacked( - "Overflow at operator code 0x", - _utohex(error[4]), - " found at call #", - _utoa(error[3]), - " in script #", - _utoa(error[2]), - " from ", - stageName(error[1]), - " stage" - ); - } else if (errorCode == Witnet.ErrorCodes.DivisionByZero && error.length >= 5) { - errorMessage = abi.encodePacked( - "Division by zero at operator code 0x", - _utohex(error[4]), - " found at call #", - _utoa(error[3]), - " in script #", - _utoa(error[2]), - " from ", - stageName(error[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 { - errorMessage = abi.encodePacked("Unknown error (0x", _utohex(error[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, - "WitnetParserLib: Tried to read error code from successful Witnet.Result" - ); - 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, "WitnetParserLib: 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. - /// @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 asFixed16(Witnet.Result memory _result) - external pure - returns (int32) - { - require(_result.success, "WitnetParserLib: Tried to read `fixed16` value from errored Witnet.Result"); - return _result.value.decodeFixed16(); - } - - /// @notice 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 asFixed16Array(Witnet.Result memory _result) - external pure - returns (int32[] memory) - { - require(_result.success, "WitnetParserLib: Tried to read `fixed16[]` value from errored Witnet.Result"); - return _result.value.decodeFixed16Array(); - } - - /// @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) - { - require(_result.success, "WitnetParserLib: Tried to read `int128` value from errored Witnet.Result"); - return _result.value.decodeInt128(); - } - - /// @notice 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) - { - require(_result.success, "WitnetParserLib: Tried to read `int128[]` value from errored Witnet.Result"); - return _result.value.decodeInt128Array(); - } - - /// @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 - returns(string memory) - { - require(_result.success, "WitnetParserLib: Tried to read `string` value from errored Witnet.Result"); - return _result.value.decodeString(); - } - - /// @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 - returns (string[] memory) - { - require(_result.success, "WitnetParserLib: Tried to read `string[]` value from errored Witnet.Result"); - return _result.value.decodeStringArray(); - } - - /// @notice 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 asUint64(Witnet.Result memory _result) - external pure - returns(uint64) - { - require(_result.success, "WitnetParserLib: Tried to read `uint64` value from errored Witnet.Result"); - return _result.value.decodeUint64(); - } - - /// @notice 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 asUint64Array(Witnet.Result memory _result) - external pure - returns (uint64[] memory) - { - require(_result.success, "WitnetParserLib: Tried to read `uint64[]` value from errored Witnet.Result"); - return _result.value.decodeUint64Array(); - } - - /// @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 pure - returns (string memory) - { - if (_stageIndex == 0) { - return "retrieval"; - } else if (_stageIndex == 1) { - return "aggregation"; - } else if (_stageIndex == 2) { - return "tally"; - } else { - return "unknown"; - } - } - - /// @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) - { - return Witnet.ErrorCodes(_discriminant); - } - - /// @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) - { - 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 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) - { - 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); - } -} diff --git a/contracts/libs/WitnetV2.sol b/contracts/libs/WitnetV2.sol new file mode 100644 index 000000000..2d1e9047f --- /dev/null +++ b/contracts/libs/WitnetV2.sol @@ -0,0 +1,175 @@ +// 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 DrPost { + uint256 block; + DrPostStatus status; + DrPostRequest request; + DrPostResponse response; + } + + /// 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 drTallyResultCborBytes; + } + + 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 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 218dd316f..622ae2d2a 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" @@ -21,13 +21,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" @@ -35,13 +35,13 @@ }, "boba": { "boba.rinkeby": { - "WitnetParserLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", + "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", "WitnetPriceRouter": "0x36928Aeedaaf7D85bcA39aDfB2A39ec529ce221a", "WitnetRandomness": "0xeAcAcC48eDD5221EC7182E1789d8bFa9dF801dFF", "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" }, "boba.mainnet": { - "WitnetParserLib": "0x6473063EBEabC0606A4159b7d9F79BB306ED0D2A", + "WitnetLib": "0x6473063EBEabC0606A4159b7d9F79BB306ED0D2A", "WitnetPriceRouter": "0x93f61D0D5F623144e7C390415B70102A9Cc90bA5", "WitnetRandomness": "0x3D7Ccf6518deBE3d2fd20c4b6AEc3FD904c0Ad29", "WitnetRequestBoard": "0xd3AD9a4b26527E3bA5Fc60B75Eb002D47D98e292" @@ -49,13 +49,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" @@ -63,25 +63,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" @@ -89,13 +89,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" @@ -103,13 +103,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" @@ -117,7 +117,7 @@ }, "dogechain": { "dogechain.testnet": { - "WitnetParserLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", + "WitnetLib": "0x7D8A488BACB56dA2De17628e26a21fFd97792b81", "WitnetPriceRouter": "0x9E943Ab1FD0D35B3BaDe31AA78D60C485EA1a604", "WitnetRandomness": "0xD9465D38f50f364b3263Cb219e58d4dB2D584530", "WitnetRequestBoard": "0x58D8ECe142c60f5707594a7C1D90e46eAE5AF431" @@ -125,14 +125,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" @@ -140,13 +140,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" @@ -154,13 +154,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" @@ -168,13 +168,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" @@ -182,13 +182,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" @@ -196,13 +196,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" @@ -210,19 +210,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" @@ -230,13 +230,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" @@ -244,13 +244,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" @@ -258,13 +258,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" @@ -272,7 +272,7 @@ }, "reef": { "reef.testnet": { - "WitnetParserLib": "0x5757040246996BFcDC890CD1CcdE6D414eAbFF74", + "WitnetLib": "0x5757040246996BFcDC890CD1CcdE6D414eAbFF74", "WitnetPriceRouter": "0xB600e92DbA7CA66895Aa353d9128514ba47e7896", "WitnetRandomness": "0x3f159F3bD5c27A936E0C897a4584Eb1647a62197", "WitnetRequestBoard": "0x77d64ec18b0a14fefe673e3aa194c816c2383232" @@ -280,13 +280,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" @@ -294,13 +294,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 9ee9d38a3..00177f543 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", @@ -311,6 +310,12 @@ module.exports = { network_id: 568, skipDryRun: true, }, + "dogechain.mainnet": { + host: "localhost", + port: 9519, + network_id: 2000, + skipDryRun: true, + }, }, harmony: { "harmony.testnet#0": { diff --git a/package.json b/package.json index b658390e1..555867dde 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "witnet-solidity-bridge", - "version": "0.6.0", + "version": "2.0.0", "description": "Witnet Solidity Bridge contracts for EVM-compatible chains", "main": "", "scripts": { @@ -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", @@ -43,7 +43,7 @@ "dependencies": { "ado-contracts": "1.0.0", "@eth-optimism/solc": "0.7.6-alpha.1", - "@openzeppelin/contracts": "^4.7.3", + "@openzeppelin/contracts": "~4.8.0-rc.2", "lodash": "^4.17.21" }, "devDependencies": { @@ -57,7 +57,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", 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/TestWitnetCBOR.sol b/test/TestWitnetCBOR.sol new file mode 100644 index 000000000..585cf408d --- /dev/null +++ b/test/TestWitnetCBOR.sol @@ -0,0 +1,431 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.7.0 <0.9.0; +pragma experimental ABIEncoderV2; + +import "truffle/Assert.sol"; +import "../contracts/libs/WitnetCBOR.sol"; +import "../contracts/libs/WitnetLib.sol"; + +contract TestWitnetCBOR { + + using WitnetCBOR for WitnetCBOR.CBOR; + + event Log(string _topic, uint256 _value); + event Log(string _topic, bytes _value); + + function testBoolDecode() external { + bool decodedFalse = WitnetCBOR.valueFromBytes(hex"f4").readBool(); + bool decodedTrue = WitnetCBOR.valueFromBytes(hex"f5").readBool(); + Assert.equal( + decodedFalse, + false, + "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 WitnetCBOR.CBOR containing the correct bool true value" + ); + } + + function helperDecodeBoolRevert() public pure { + 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 readBool function"); + } + + function testUint64DecodeDiscriminant() external { + 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 = uint64(WitnetCBOR.valueFromBytes(hex"1b0020000000000000").readUint()); + Assert.equal( + uint(decoded), + 9007199254740992, + "CBOR-encoded Uint64 value should be decoded into a WitnetCBOR.CBOR containing the correct Uint64 value" + ); + } + + function testInt128DecodeDiscriminant() external { + 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 = int128(WitnetCBOR.valueFromBytes(hex"3bfffffffffffffffe").readInt()); + Assert.equal( + int(decoded), + -18446744073709551615, + "CBOR-encoded Int128 value should be decoded into a WitnetCBOR.CBOR containing the correct Uint64 value" + ); + } + + function testInt128DecodeZeroValue() external { + 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"); + } + + function testBytes0DecodeDiscriminant() external { + 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 = 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"); + } + + function testBytes4BDecodeDiscriminant() external { + 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 = WitnetCBOR.valueFromBytes(encoded).readBytes(); + bytes memory expected = abi.encodePacked( + uint8(1), + uint8(2), + uint8(3), + uint8(4) + ); + + Assert.equal( + decoded[0], + expected[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 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 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 WitnetCBOR.CBOR containing the correct Bytes value (error at item 3)" + ); + } + + function testBytes32DecodeValueFrom31bytes() external { + bytes memory encoded = hex"581f01020304050607080910111213141516171819202122232425262728293031"; + 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" + ); + } + + function testBytes32DecodeValueFrom32bytes() external { + bytes memory encoded = hex"58200102030405060708091011121314151617181920212223242526272829303132"; + 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" + ); + } + + function testBytes32DecodeValueFrom33bytes() external { + bytes memory encoded = hex"5821010203040506070809101112131415161718192021222324252627282930313233"; + 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" + ); + } + + 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" + ); + } + + function testStringDecodeValue() external { + bytes memory encoded = hex"6449455446"; + 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" + ); + } + + 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" + ); + } + + function testFloatDecodeSmallestSubnormal() external { + bytes memory encoded = hex"f90001"; + 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" + ); + } + + function testFloatDecodeLargestSubnormal() external { + bytes memory encoded = hex"f903ff"; + 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" + ); + } + + function testFloatDecodeSmallestPositiveNormal() external { + bytes memory encoded = hex"f90400"; + 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" + ); + } + + function testFloatDecodeLargestNormal() external { + bytes memory encoded = hex"f97bff"; + 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" + ); + } + + function testFloatDecodeLargestLessThanOne() external { + bytes memory encoded = hex"f93bff"; + 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" + ); + } + + function testFloatDecodeOne() external { + bytes memory encoded = hex"f93c00"; + 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" + ); + } + + function testFloatDecodeSmallestGreaterThanOne() external { + bytes memory encoded = hex"f93c01"; + 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" + ); + } + + function testFloatDecodeOneThird() external { + bytes memory encoded = hex"f93555"; + 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" + ); + } + + function testFloatDecodeMinusTwo() external { + bytes memory encoded = hex"f9c000"; + 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" + ); + } + + function testFloatDecodeZero() external { + bytes memory encoded = hex"f90000"; + 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" + ); + } + + function testFloatDecodeMinusZero() external { + bytes memory encoded = hex"f98000"; + 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" + ); + } + + function testUintArrayDecode() external { + bytes memory encoded = hex"840102031a002fefd8"; + 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 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 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 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 uint values should be decoded into a WitnetCBOR.CBOR containing the correct uint values (error at item 3)" + ); + } + + function testIntArrayDecode() external { + bytes memory encoded = hex"840121033a002fefd7"; + 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 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 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 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 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).readFloat16Array(); + int32[4] memory expected = [ + int32(11250), + int32(-22500), + int32(32812), + int32(-31425) + ]; + Assert.equal( + decoded[0], + expected[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 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 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 WitnetCBOR.CBOR containing the correct Int128 values (error at item 3)" + ); + } + + function testStringArrayDecode() external { + bytes memory encoded = hex"846548656c6c6f6d646563656e7472616c697a656465776f726c646121"; + string[] memory decoded = WitnetCBOR.valueFromBytes(encoded).readStringArray(); + string[4] memory expected = [ + "Hello", + "decentralized", + "world", + "!" + ]; + Assert.equal( + decoded[0], + expected[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 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 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 WitnetCBOR.CBOR containing the correct String values (error at item 3)" + ); + } + +} \ No newline at end of file diff --git a/test/TestWitnetDecoderLib.sol b/test/TestWitnetDecoderLib.sol deleted file mode 100644 index 2c8b5530e..000000000 --- a/test/TestWitnetDecoderLib.sol +++ /dev/null @@ -1,378 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.7.0 <0.9.0; -pragma experimental ABIEncoderV2; - -import "truffle/Assert.sol"; -import "../contracts/libs/WitnetDecoderLib.sol"; - - -contract TestWitnetDecoderLib { - - using WitnetDecoderLib for Witnet.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(); - Assert.equal( - decodedFalse, - false, - "CBOR-encoded false value should be decoded into a Witnet.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" - ); - } - - function helperDecodeBoolRevert() public pure { - WitnetDecoderLib.valueFromBytes(hex"f6").decodeBool(); - } - - 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"); - } - - 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"); - } - - function testUint64DecodeValue() external { - uint64 decoded = WitnetDecoderLib.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" - ); - } - - 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"); - } - - function testInt128DecodeValue() external { - int128 decoded = WitnetDecoderLib.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" - ); - } - - 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"); - } - - 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"); - } - - 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"); - } - - 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"); - } - - function testBytes4DecodeValue() external { - bytes memory encoded = hex"4401020304"; - bytes memory decoded = WitnetDecoderLib.valueFromBytes(encoded).decodeBytes(); - bytes memory expected = abi.encodePacked( - uint8(1), - uint8(2), - uint8(3), - uint8(4) - ); - - 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)" - ); - 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)" - ); - 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)" - ); - 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)" - ); - } - - function testBytes32DecodeValueFrom31bytes() external { - bytes memory encoded = hex"581f01020304050607080910111213141516171819202122232425262728293031"; - bytes32 decoded = WitnetDecoderLib.decodeBytes32(WitnetDecoderLib.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 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 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"); - } - - function testStringDecodeValue() external { - bytes memory encoded = hex"6449455446"; - string memory decoded = WitnetDecoderLib.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"); - } - - function testFloatDecodeDiscriminant() external { - Witnet.CBOR memory decoded = WitnetDecoderLib.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 expected = 0; - - Assert.equal(decoded, expected, "CBOR-encoded Float value should be decoded into a Witnet.CBOR containing the correct Float value"); - } - - function testFloatDecodeLargestSubnormal() external { - bytes memory encoded = hex"f903ff"; - int32 decoded = WitnetDecoderLib.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"); - } - - function testFloatDecodeSmallestPositiveNormal() external { - bytes memory encoded = hex"f90400"; - int32 decoded = WitnetDecoderLib.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"); - } - - function testFloatDecodeLargestNormal() external { - bytes memory encoded = hex"f97bff"; - int32 decoded = WitnetDecoderLib.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"); - } - - function testFloatDecodeLargestLessThanOne() external { - bytes memory encoded = hex"f93bff"; - int32 decoded = WitnetDecoderLib.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"); - } - - function testFloatDecodeOne() external { - bytes memory encoded = hex"f93c00"; - int32 decoded = WitnetDecoderLib.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"); - } - - function testFloatDecodeSmallestGreaterThanOne() external { - bytes memory encoded = hex"f93c01"; - int32 decoded = WitnetDecoderLib.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"); - } - - function testFloatDecodeOneThird() external { - bytes memory encoded = hex"f93555"; - int32 decoded = WitnetDecoderLib.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"); - } - - function testFloatDecodeMinusTwo() external { - bytes memory encoded = hex"f9c000"; - int32 decoded = WitnetDecoderLib.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"); - } - - function testFloatDecodeZero() external { - bytes memory encoded = hex"f90000"; - int32 decoded = WitnetDecoderLib.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"); - } - - function testFloatDecodeMinusZero() external { - bytes memory encoded = hex"f98000"; - int32 decoded = WitnetDecoderLib.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"); - } - - function testUint64ArrayDecode() external { - bytes memory encoded = hex"840102031a002fefd8"; - uint64[] memory decoded = WitnetDecoderLib.valueFromBytes(encoded).decodeUint64Array(); - uint64[4] memory expected = [ - uint64(1), - uint64(2), - uint64(3), - uint64(3141592) - ]; - - 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)" - ); - 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)" - ); - 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)" - ); - 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)" - ); - } - - function testInt128ArrayDecode() external { - bytes memory encoded = hex"840121033a002fefd7"; - int128[] memory decoded = WitnetDecoderLib.valueFromBytes(encoded).decodeInt128Array(); - int128[4] memory expected = [ - int128(1), - int128(-2), - int128(3), - int128(-3141592) - ]; - - 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)" - ); - 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)" - ); - 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)" - ); - 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)" - ); - } - - function testFixed16ArrayDecode() external { - bytes memory encoded = hex"84f93c80f9c080f94290f9C249"; - int32[] memory decoded = WitnetDecoderLib.valueFromBytes(encoded).decodeFixed16Array(); - int32[4] memory expected = [ - int32(11250), - int32(-22500), - int32(32812), - int32(-31425) - ]; - - 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)" - ); - 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)" - ); - 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)" - ); - 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)" - ); - } - - function testStringArrayDecode() external { - bytes memory encoded = hex"846548656c6c6f6d646563656e7472616c697a656465776f726c646121"; - string[] memory decoded = WitnetDecoderLib.valueFromBytes(encoded).decodeStringArray(); - string[4] memory expected = [ - "Hello", - "decentralized", - "world", - "!" - ]; - - 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)" - ); - 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)" - ); - 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)" - ); - 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)" - ); - } -} diff --git a/test/TestWitnetParserLib.sol b/test/TestWitnetLib.sol similarity index 60% rename from test/TestWitnetParserLib.sol rename to test/TestWitnetLib.sol index 027081f9c..a3b609de8 100644 --- a/test/TestWitnetParserLib.sol +++ b/test/TestWitnetLib.sol @@ -4,36 +4,27 @@ 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; - - // Test the `WitnetParserLib.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\""); - - } + using WitnetLib for Witnet.Result; // 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 +93,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,23 +163,23 @@ 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)", + "Unknown error: no error code.", "Empty error message `[]` should be properly formatted" ); Assert.equal( @@ -259,9 +250,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.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/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) 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, }, }