Description:
Proxy contract enabling upgradeable smart contract patterns. Delegates calls to an implementation contract.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"/contracts/libs/WitOracleResultStatusLib.sol": {
"content": "// SPDX-License-Identifier: MIT\r
\r
pragma solidity >=0.8.0 <0.9.0;\r
\r
import "./Witnet.sol";\r
\r
/// @title A library for interpreting Witnet resolution errors\r
/// @author The Witnet Foundation.\r
library WitOracleResultStatusLib {\r
\r
using Witnet for bytes;\r
using Witnet for uint8;\r
using Witnet for uint64;\r
using Witnet for Witnet.DataResult;\r
using Witnet for Witnet.ResultStatus;\r
using WitnetCBOR for WitnetCBOR.CBOR;\r
\r
// ================================================================================================================\r
// --- Library public methods -------------------------------------------------------------------------------------\r
\r
function toString(bytes calldata _result) public pure returns (string memory) {\r
Witnet.DataResult memory result = abi.decode(_result, (Witnet.DataResult));\r
if (result.status == Witnet.ResultStatus.NoErrors) {\r
return "No errors.";\r
\r
} else if (result.status == Witnet.ResultStatus.BoardAwaitingResult) {\r
return "Awaiting result.";\r
\r
} else if (result.status == Witnet.ResultStatus.BoardFinalizingResult) {\r
return "Finalizing result.";\r
\r
} else if (result.status == Witnet.ResultStatus.BoardBeingDisputed) {\r
return "Being disputed.";\r
\r
} else if (result.status == Witnet.ResultStatus.BoardAlreadyDelivered) {\r
return "Already delivered.";\r
\r
} else if (result.status == Witnet.ResultStatus.BoardResolutionTimeout) {\r
return "Error: resolution timeout.";\r
\r
} else if (result.status == Witnet.ResultStatus.BridgeMalformedDataRequest) {\r
return "Bridge: malformed data request.";\r
\r
} else if (result.status == Witnet.ResultStatus.BridgePoorIncentives) {\r
return "Bridge: poor incentives.";\r
\r
} else if (result.status == Witnet.ResultStatus.BridgeOversizedTallyResult) {\r
return "Bridge: oversized tally result.";\r
\r
} else {\r
return _parseError(result);\r
}\r
}\r
\r
function _parseError(Witnet.DataResult memory result) private pure returns (string memory) {\r
string memory _prefix;\r
if (result.status.isCircumstantial()) {\r
_prefix = "Circumstantial: ";\r
\r
} else if (result.status.poorIncentives()) {\r
_prefix = "Poor incentives: ";\r
\r
} else if (result.status.lackOfConsensus()) {\r
_prefix = "Consensus: ";\r
\r
} else {\r
_prefix = "Critical: ";\r
} \r
return string(abi.encodePacked(\r
_prefix, \r
_parseErrorCode(result)\r
));\r
}\r
\r
function _parseErrorCode(Witnet.DataResult memory result)\r
private pure\r
returns (string memory)\r
{\r
if (result.status == Witnet.ResultStatus.InsufficientCommits) {\r
return "insufficient commits.";\r
\r
} else if (result.status == Witnet.ResultStatus.CircumstantialFailure) {\r
return _parseErrorDetails(result);\r
\r
} else if (result.status == Witnet.ResultStatus.InsufficientMajority) {\r
return "insufficient majority.";\r
\r
} else if (result.status == Witnet.ResultStatus.InsufficientReveals) {\r
return "insufficient reveals.";\r
\r
} else if (\r
result.status == Witnet.ResultStatus.OversizedTallyResult\r
|| result.status == Witnet.ResultStatus.BridgeOversizedTallyResult\r
) {\r
return "oversized result.";\r
\r
} else if (result.status == Witnet.ResultStatus.InconsistentSources) {\r
return "inconsistent data sources.";\r
\r
} else if (result.status == Witnet.ResultStatus.MalformedQueryResponses) {\r
return string(abi.encodePacked(\r
"malformed response: ",\r
_parseErrorDetails(result)\r
));\r
\r
} else if (\r
result.status == Witnet.ResultStatus.MalformedDataRequest \r
|| result.status == Witnet.ResultStatus.BridgeMalformedDataRequest\r
\r
) {\r
return string(abi.encodePacked(\r
"malformed request: ",\r
_parseErrorDetails(result)\r
));\r
\r
} else if (result.status == Witnet.ResultStatus.UnhandledIntercept) {\r
if (result.dataType != Witnet.RadonDataTypes.Any) {\r
return string(abi.encodePacked(\r
"unhanled intercept: ",\r
_parseErrorDetails(result)\r
));\r
} else {\r
return "unhandled intercept.";\r
}\r
\r
} else {\r
return string(abi.encodePacked(\r
"0x",\r
uint8(result.status).toHexString()\r
));\r
}\r
}\r
\r
function _parseErrorDetails(Witnet.DataResult memory result) private pure returns (string memory) {\r
if (result.dataType == Witnet.RadonDataTypes.Integer) {\r
result.status = Witnet.ResultStatus(uint8(result.fetchUint()));\r
} else {\r
return "(unparsable error details)";\r
}\r
if (result.status == Witnet.ResultStatus.HttpErrors) {\r
if (result.dataType == Witnet.RadonDataTypes.Integer) {\r
return string(abi.encodePacked(\r
"http/",\r
result.fetchUint().toString()\r
));\r
} else {\r
return "unspecific http status code.";\r
}\r
\r
} else if (result.status == Witnet.ResultStatus.RetrievalsTimeout) {\r
return "response timeout.";\r
\r
} else if (result.status == Witnet.ResultStatus.ArrayIndexOutOfBounds) {\r
if (result.dataType == Witnet.RadonDataTypes.Integer) {\r
return string(abi.encodePacked(\r
"array index out of bounds: ",\r
result.fetchUint().toString()\r
));\r
} else {\r
return "array index out of bounds.";\r
}\r
\r
} else if (result.status == Witnet.ResultStatus.MapKeyNotFound) {\r
if (result.dataType == Witnet.RadonDataTypes.String) {\r
return string(abi.encodePacked(\r
"map key not found: ",\r
result.fetchString()\r
));\r
} else {\r
return "map key not found.";\r
}\r
\r
} else if (result.status == Witnet.ResultStatus.JsonPathNotFound) {\r
if (result.dataType == Witnet.RadonDataTypes.String) {\r
return string(abi.encodePacked(\r
"json path returned no values: ",\r
result.fetchString()\r
));\r
} else {\r
return "json path returned no values.";\r
}\r
\r
} else {\r
return string(abi.encodePacked(\r
"0x",\r
Witnet.toHexString(uint8(result.status)),\r
result.dataType != Witnet.RadonDataTypes.Any \r
? string(abi.encodePacked(" (", _parseErrorArgs(result), ")"))\r
: ""\r
));\r
}\r
}\r
\r
function _parseErrorArgs(Witnet.DataResult memory result) private pure returns (string memory _str) {\r
if (result.dataType == Witnet.RadonDataTypes.Any) {\r
return "";\r
\r
} else if (result.dataType == Witnet.RadonDataTypes.String) {\r
_str = string(abi.encodePacked("'", result.fetchString(), "', "));\r
\r
} else if (result.dataType == Witnet.RadonDataTypes.Integer) {\r
_str = string(abi.encodePacked(result.fetchUint().toString(), ", "));\r
\r
} else {\r
_str = "?, ";\r
}\r
return string(abi.encodePacked(_str, _parseErrorArgs(result)));\r
}\r
}\r
"
},
"/contracts/libs/WitnetCBOR.sol": {
"content": "// SPDX-License-Identifier: MIT\r
\r
pragma solidity >=0.8.0 <0.9.0;\r
\r
import "./WitnetBuffer.sol";\r
\r
/// @title A minimalistic implementation of “RFC 7049 Concise Binary Object Representation”\r
/// @notice This library leverages a buffer-like structure for step-by-step decoding of bytes so as to minimize\r
/// the gas cost of decoding them into a useful native type.\r
/// @dev Most of the logic has been borrowed from Patrick Gansterer’s cbor.js library: https://github.com/paroga/cbor-js\r
/// @author The Witnet Foundation.\r
\r
library WitnetCBOR {\r
\r
using WitnetBuffer for WitnetBuffer.Buffer;\r
using WitnetCBOR for WitnetCBOR.CBOR;\r
\r
/// Data struct following the RFC-7049 standard: Concise Binary Object Representation.\r
struct CBOR {\r
WitnetBuffer.Buffer buffer;\r
uint8 initialByte;\r
uint8 majorType;\r
uint8 additionalInformation;\r
uint64 len;\r
uint64 tag;\r
}\r
\r
uint8 internal constant MAJOR_TYPE_INT = 0;\r
uint8 internal constant MAJOR_TYPE_NEGATIVE_INT = 1;\r
uint8 internal constant MAJOR_TYPE_BYTES = 2;\r
uint8 internal constant MAJOR_TYPE_STRING = 3;\r
uint8 internal constant MAJOR_TYPE_ARRAY = 4;\r
uint8 internal constant MAJOR_TYPE_MAP = 5;\r
uint8 internal constant MAJOR_TYPE_TAG = 6;\r
uint8 internal constant MAJOR_TYPE_CONTENT_FREE = 7;\r
\r
uint32 internal constant UINT32_MAX = type(uint32).max;\r
uint64 internal constant UINT64_MAX = type(uint64).max;\r
\r
error EmptyArray();\r
error InvalidLengthEncoding(uint length);\r
error UnexpectedMajorType(uint read, uint expected);\r
error UnsupportedPrimitive(uint primitive);\r
error UnsupportedMajorType(uint unexpected); \r
\r
modifier isMajorType(\r
WitnetCBOR.CBOR memory cbor,\r
uint8 expected\r
) {\r
if (cbor.majorType != expected) {\r
revert UnexpectedMajorType(cbor.majorType, expected);\r
}\r
_;\r
}\r
\r
modifier notEmpty(WitnetBuffer.Buffer memory buffer) {\r
if (buffer.data.length == 0) {\r
revert WitnetBuffer.EmptyBuffer();\r
}\r
_;\r
}\r
\r
function eof(CBOR memory cbor)\r
internal pure\r
returns (bool)\r
{\r
return cbor.buffer.cursor >= cbor.buffer.data.length;\r
}\r
\r
/// @notice Decode a CBOR structure from raw bytes.\r
/// @dev This is the main factory for CBOR instances, which can be later decoded into native EVM types.\r
/// @param bytecode Raw bytes representing a CBOR-encoded value.\r
/// @return A `CBOR` instance containing a partially decoded value.\r
function fromBytes(bytes memory bytecode)\r
internal pure\r
returns (CBOR memory)\r
{\r
WitnetBuffer.Buffer memory buffer = WitnetBuffer.Buffer(bytecode, 0);\r
return fromBuffer(buffer);\r
}\r
\r
/// @notice Decode a CBOR structure from raw bytes.\r
/// @dev This is an alternate factory for CBOR instances, which can be later decoded into native EVM types.\r
/// @param buffer A Buffer structure representing a CBOR-encoded value.\r
/// @return A `CBOR` instance containing a partially decoded value.\r
function fromBuffer(WitnetBuffer.Buffer memory buffer)\r
internal pure\r
notEmpty(buffer)\r
returns (CBOR memory)\r
{\r
uint8 initialByte;\r
uint8 majorType = 255;\r
uint8 additionalInformation;\r
uint64 tag = UINT64_MAX;\r
uint256 len;\r
bool isTagged = true;\r
while (isTagged) {\r
// Extract basic CBOR properties from input bytes\r
initialByte = buffer.readUint8();\r
len ++;\r
majorType = initialByte >> 5;\r
additionalInformation = initialByte & 0x1f;\r
// Early CBOR tag parsing.\r
if (majorType == MAJOR_TYPE_TAG) {\r
uint _cursor = buffer.cursor;\r
tag = readLength(buffer, additionalInformation);\r
len += buffer.cursor - _cursor;\r
} else {\r
isTagged = false;\r
}\r
}\r
if (majorType > MAJOR_TYPE_CONTENT_FREE) {\r
revert UnsupportedMajorType(majorType);\r
}\r
return CBOR(\r
buffer,\r
initialByte,\r
majorType,\r
additionalInformation,\r
uint64(len),\r
tag\r
);\r
}\r
\r
function fork(WitnetCBOR.CBOR memory self)\r
internal pure\r
returns (WitnetCBOR.CBOR memory)\r
{\r
return CBOR({\r
buffer: self.buffer.fork(),\r
initialByte: self.initialByte,\r
majorType: self.majorType,\r
additionalInformation: self.additionalInformation,\r
len: self.len,\r
tag: self.tag\r
});\r
}\r
\r
function settle(CBOR memory self)\r
internal pure\r
returns (WitnetCBOR.CBOR memory)\r
{\r
if (!self.eof()) {\r
return fromBuffer(self.buffer);\r
} else {\r
return self;\r
}\r
}\r
\r
function skip(CBOR memory self)\r
internal pure\r
returns (WitnetCBOR.CBOR memory)\r
{\r
if (\r
self.majorType == MAJOR_TYPE_INT\r
|| self.majorType == MAJOR_TYPE_NEGATIVE_INT\r
|| (\r
self.majorType == MAJOR_TYPE_CONTENT_FREE \r
&& self.additionalInformation >= 25\r
&& self.additionalInformation <= 27\r
)\r
) {\r
self.buffer.cursor += self.peekLength();\r
} else if (\r
self.majorType == MAJOR_TYPE_STRING\r
|| self.majorType == MAJOR_TYPE_BYTES\r
) {\r
uint64 len = readLength(self.buffer, self.additionalInformation);\r
self.buffer.cursor += len;\r
} else if (\r
self.majorType == MAJOR_TYPE_ARRAY\r
|| self.majorType == MAJOR_TYPE_MAP\r
) { \r
self.len = readLength(self.buffer, self.additionalInformation); \r
} else if (\r
self.majorType != MAJOR_TYPE_CONTENT_FREE\r
|| (\r
self.additionalInformation != 20\r
&& self.additionalInformation != 21\r
)\r
) {\r
revert("WitnetCBOR.skip: unsupported major type");\r
}\r
return self;\r
}\r
\r
function peekLength(CBOR memory self)\r
internal pure\r
returns (uint64)\r
{\r
if (self.additionalInformation < 24) {\r
return 0;\r
} else if (self.additionalInformation < 28) {\r
return uint64(1 << (self.additionalInformation - 24));\r
} else {\r
revert InvalidLengthEncoding(self.additionalInformation);\r
}\r
}\r
\r
function readArray(CBOR memory self)\r
internal pure\r
isMajorType(self, MAJOR_TYPE_ARRAY)\r
returns (CBOR[] memory items)\r
{\r
// read array's length and move self cursor forward to the first array element:\r
uint64 len = readLength(self.buffer, self.additionalInformation);\r
items = new CBOR[](len + 1);\r
for (uint ix = 0; ix < len; ix ++) {\r
// settle next element in the array:\r
self = self.settle();\r
// fork it and added to the list of items to be returned:\r
items[ix] = self.fork();\r
if (self.majorType == MAJOR_TYPE_ARRAY) {\r
CBOR[] memory _subitems = self.readArray();\r
// move forward to the first element after inner array:\r
self = _subitems[_subitems.length - 1];\r
} else if (self.majorType == MAJOR_TYPE_MAP) {\r
CBOR[] memory _subitems = self.readMap();\r
// move forward to the first element after inner map:\r
self = _subitems[_subitems.length - 1];\r
} else {\r
// move forward to the next element:\r
self.skip();\r
}\r
}\r
// return self cursor as extra item at the end of the list,\r
// as to optimize recursion when jumping over nested arrays:\r
items[len] = self;\r
}\r
\r
function readMap(CBOR memory self)\r
internal pure\r
isMajorType(self, MAJOR_TYPE_MAP)\r
returns (CBOR[] memory items)\r
{\r
// read number of items within the map and move self cursor forward to the first inner element:\r
uint64 len = readLength(self.buffer, self.additionalInformation) * 2;\r
items = new CBOR[](len + 1);\r
for (uint ix = 0; ix < len; ix ++) {\r
// settle next element in the array:\r
self = self.settle();\r
// fork it and added to the list of items to be returned:\r
items[ix] = self.fork();\r
if (ix % 2 == 0 && self.majorType != MAJOR_TYPE_STRING) {\r
revert UnexpectedMajorType(self.majorType, MAJOR_TYPE_STRING);\r
} else if (self.majorType == MAJOR_TYPE_ARRAY || self.majorType == MAJOR_TYPE_MAP) {\r
CBOR[] memory _subitems = (self.majorType == MAJOR_TYPE_ARRAY\r
? self.readArray()\r
: self.readMap()\r
);\r
// move forward to the first element after inner array or map:\r
self = _subitems[_subitems.length - 1];\r
} else {\r
// move forward to the next element:\r
self.skip();\r
}\r
}\r
// return self cursor as extra item at the end of the list,\r
// as to optimize recursion when jumping over nested arrays:\r
items[len] = self;\r
}\r
\r
/// Reads the length of the settle CBOR item from a buffer, consuming a different number of bytes depending on the\r
/// value of the `additionalInformation` argument.\r
function readLength(\r
WitnetBuffer.Buffer memory buffer,\r
uint8 additionalInformation\r
) \r
internal pure\r
returns (uint64)\r
{\r
if (additionalInformation < 24) {\r
return additionalInformation;\r
}\r
if (additionalInformation == 24) {\r
return buffer.readUint8();\r
}\r
if (additionalInformation == 25) {\r
return buffer.readUint16();\r
}\r
if (additionalInformation == 26) {\r
return buffer.readUint32();\r
}\r
if (additionalInformation == 27) {\r
return buffer.readUint64();\r
}\r
if (additionalInformation == 31) {\r
return UINT64_MAX;\r
}\r
revert InvalidLengthEncoding(additionalInformation);\r
}\r
\r
/// @notice Read a `CBOR` structure into a native `bool` value.\r
/// @param cbor An instance of `CBOR`.\r
/// @return The value represented by the input, as a `bool` value.\r
function readBool(CBOR memory cbor)\r
internal pure\r
isMajorType(cbor, MAJOR_TYPE_CONTENT_FREE)\r
returns (bool)\r
{\r
if (cbor.additionalInformation == 20) {\r
return false;\r
} else if (cbor.additionalInformation == 21) {\r
return true;\r
} else {\r
revert UnsupportedPrimitive(cbor.additionalInformation);\r
}\r
}\r
\r
/// @notice Decode a `CBOR` structure into a native `bytes` value.\r
/// @param cbor An instance of `CBOR`.\r
/// @return output The value represented by the input, as a `bytes` value. \r
function readBytes(CBOR memory cbor)\r
internal pure\r
isMajorType(cbor, MAJOR_TYPE_BYTES)\r
returns (bytes memory output)\r
{\r
cbor.len = readLength(\r
cbor.buffer,\r
cbor.additionalInformation\r
);\r
if (cbor.len == UINT32_MAX) {\r
// These checks look repetitive but the equivalent loop would be more expensive.\r
uint32 length = uint32(_readIndefiniteStringLength(\r
cbor.buffer,\r
cbor.majorType\r
));\r
if (length < UINT32_MAX) {\r
output = abi.encodePacked(cbor.buffer.read(length));\r
length = uint32(_readIndefiniteStringLength(\r
cbor.buffer,\r
cbor.majorType\r
));\r
if (length < UINT32_MAX) {\r
output = abi.encodePacked(\r
output,\r
cbor.buffer.read(length)\r
);\r
}\r
}\r
} else {\r
return cbor.buffer.read(uint32(cbor.len));\r
}\r
}\r
\r
/// @notice Decode a `CBOR` structure into a `fixed16` value.\r
/// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values\r
/// by 5 decimal orders so as to get a fixed precision of 5 decimal positions, which should be OK for most `fixed16`\r
/// use cases. In other words, the output of this method is 10,000 times the actual value, encoded into an `int32`.\r
/// @param cbor An instance of `CBOR`.\r
/// @return The value represented by the input, as an `int128` value.\r
function readFloat16(CBOR memory cbor)\r
internal pure\r
isMajorType(cbor, MAJOR_TYPE_CONTENT_FREE)\r
returns (int32)\r
{\r
if (cbor.additionalInformation == 25) {\r
return cbor.buffer.readFloat16();\r
} else {\r
revert UnsupportedPrimitive(cbor.additionalInformation);\r
}\r
}\r
\r
/// @notice Decode a `CBOR` structure into a `fixed32` value.\r
/// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values\r
/// by 9 decimal orders so as to get a fixed precision of 9 decimal positions, which should be OK for most `fixed64`\r
/// use cases. In other words, the output of this method is 10^9 times the actual value, encoded into an `int`.\r
/// @param cbor An instance of `CBOR`.\r
/// @return The value represented by the input, as an `int` value.\r
function readFloat32(CBOR memory cbor)\r
internal pure\r
isMajorType(cbor, MAJOR_TYPE_CONTENT_FREE)\r
returns (int)\r
{\r
if (cbor.additionalInformation == 26) {\r
return cbor.buffer.readFloat32();\r
} else {\r
revert UnsupportedPrimitive(cbor.additionalInformation);\r
}\r
}\r
\r
/// @notice Decode a `CBOR` structure into a `fixed64` value.\r
/// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values\r
/// by 15 decimal orders so as to get a fixed precision of 15 decimal positions, which should be OK for most `fixed64`\r
/// use cases. In other words, the output of this method is 10^15 times the actual value, encoded into an `int`.\r
/// @param cbor An instance of `CBOR`.\r
/// @return The value represented by the input, as an `int` value.\r
function readFloat64(CBOR memory cbor)\r
internal pure\r
isMajorType(cbor, MAJOR_TYPE_CONTENT_FREE)\r
returns (int)\r
{\r
if (cbor.additionalInformation == 27) {\r
return cbor.buffer.readFloat64();\r
} else {\r
revert UnsupportedPrimitive(cbor.additionalInformation);\r
}\r
}\r
\r
/// @notice Decode a `CBOR` structure into a native `int128[]` value whose inner values follow the same convention \r
/// @notice as explained in `decodeFixed16`.\r
/// @param cbor An instance of `CBOR`.\r
function readFloat16Array(CBOR memory cbor)\r
internal pure\r
isMajorType(cbor, MAJOR_TYPE_ARRAY)\r
returns (int32[] memory values)\r
{\r
uint64 length = readLength(cbor.buffer, cbor.additionalInformation);\r
if (length < UINT64_MAX) {\r
values = new int32[](length);\r
for (uint64 i = 0; i < length; ) {\r
CBOR memory item = fromBuffer(cbor.buffer);\r
values[i] = readFloat16(item);\r
unchecked {\r
i ++;\r
}\r
}\r
} else {\r
revert InvalidLengthEncoding(length);\r
}\r
}\r
\r
/// @notice Decode a `CBOR` structure into a native `int128` value.\r
/// @param cbor An instance of `CBOR`.\r
/// @return The value represented by the input, as an `int128` value.\r
function readInt(CBOR memory cbor)\r
internal pure\r
returns (int64)\r
{\r
if (cbor.majorType == 1) {\r
uint64 _value = readLength(\r
cbor.buffer,\r
cbor.additionalInformation\r
);\r
return int64(-1) - int64(uint64(_value));\r
} else if (cbor.majorType == 0) {\r
// Any `uint64` can be safely casted to `int128`, so this method supports majorType 1 as well so as to have offer\r
// a uniform API for positive and negative numbers\r
return int64(readUint(cbor));\r
}\r
else {\r
revert UnexpectedMajorType(cbor.majorType, 1);\r
}\r
}\r
\r
/// @notice Decode a `CBOR` structure into a native `int[]` value.\r
/// @param cbor instance of `CBOR`.\r
/// @return array The value represented by the input, as an `int[]` value.\r
function readIntArray(CBOR memory cbor)\r
internal pure\r
isMajorType(cbor, MAJOR_TYPE_ARRAY)\r
returns (int64[] memory array)\r
{\r
uint64 length = readLength(cbor.buffer, cbor.additionalInformation);\r
if (length < UINT64_MAX) {\r
array = new int64[](length);\r
for (uint i = 0; i < length; ) {\r
CBOR memory item = fromBuffer(cbor.buffer);\r
array[i] = readInt(item);\r
unchecked {\r
i ++;\r
}\r
}\r
} else {\r
revert InvalidLengthEncoding(length);\r
}\r
}\r
\r
/// @notice Decode a `CBOR` structure into a native `string` value.\r
/// @param cbor An instance of `CBOR`.\r
/// @return text The value represented by the input, as a `string` value.\r
function readString(CBOR memory cbor)\r
internal pure\r
isMajorType(cbor, MAJOR_TYPE_STRING)\r
returns (string memory text)\r
{\r
cbor.len = readLength(cbor.buffer, cbor.additionalInformation);\r
if (cbor.len == UINT64_MAX) {\r
bool _done;\r
while (!_done) {\r
uint64 length = _readIndefiniteStringLength(\r
cbor.buffer,\r
cbor.majorType\r
);\r
if (length < UINT64_MAX) {\r
text = string(abi.encodePacked(\r
text,\r
cbor.buffer.readText(length / 4)\r
));\r
} else {\r
_done = true;\r
}\r
}\r
} else {\r
return string(cbor.buffer.readText(cbor.len));\r
}\r
}\r
\r
/// @notice Decode a `CBOR` structure into a native `string[]` value.\r
/// @param cbor An instance of `CBOR`.\r
/// @return strings The value represented by the input, as an `string[]` value.\r
function readStringArray(CBOR memory cbor)\r
internal pure\r
isMajorType(cbor, MAJOR_TYPE_ARRAY)\r
returns (string[] memory strings)\r
{\r
uint length = readLength(cbor.buffer, cbor.additionalInformation);\r
if (length < UINT64_MAX) {\r
strings = new string[](length);\r
for (uint i = 0; i < length; ) {\r
CBOR memory item = fromBuffer(cbor.buffer);\r
strings[i] = readString(item);\r
unchecked {\r
i ++;\r
}\r
}\r
} else {\r
revert InvalidLengthEncoding(length);\r
}\r
}\r
\r
/// @notice Decode a `CBOR` structure into a native `uint64` value.\r
/// @param cbor An instance of `CBOR`.\r
/// @return The value represented by the input, as an `uint64` value.\r
function readUint(CBOR memory cbor)\r
internal pure\r
isMajorType(cbor, MAJOR_TYPE_INT)\r
returns (uint64)\r
{\r
return readLength(\r
cbor.buffer,\r
cbor.additionalInformation\r
);\r
}\r
\r
/// @notice Decode a `CBOR` structure into a native `uint64[]` value.\r
/// @param cbor An instance of `CBOR`.\r
/// @return values The value represented by the input, as an `uint64[]` value.\r
function readUintArray(CBOR memory cbor)\r
internal pure\r
isMajorType(cbor, MAJOR_TYPE_ARRAY)\r
returns (uint64[] memory values)\r
{\r
uint64 length = readLength(cbor.buffer, cbor.additionalInformation);\r
if (length < UINT64_MAX) {\r
values = new uint64[](length);\r
for (uint ix = 0; ix < length; ) {\r
CBOR memory item = fromBuffer(cbor.buffer);\r
values[ix] = readUint(item);\r
unchecked {\r
ix ++;\r
}\r
}\r
} else {\r
revert InvalidLengthEncoding(length);\r
}\r
} \r
\r
/// Read the length of a CBOR indifinite-length item (arrays, maps, byte strings and text) from a buffer, consuming\r
/// as many bytes as specified by the first byte.\r
function _readIndefiniteStringLength(\r
WitnetBuffer.Buffer memory buffer,\r
uint8 majorType\r
)\r
private pure\r
returns (uint64 len)\r
{\r
uint8 initialByte = buffer.readUint8();\r
if (initialByte == 0xff) {\r
return UINT64_MAX;\r
}\r
len = readLength(\r
buffer,\r
initialByte & 0x1f\r
);\r
if (len >= UINT64_MAX) {\r
revert InvalidLengthEncoding(len);\r
} else if (majorType != (initialByte >> 5)) {\r
revert UnexpectedMajorType((initialByte >> 5), majorType);\r
}\r
}\r
\r
}"
},
"/contracts/libs/WitnetBuffer.sol": {
"content": "// SPDX-License-Identifier: MIT\r
\r
pragma solidity >=0.8.0 <0.9.0;\r
\r
/// @title A convenient wrapper around the `bytes memory` type that exposes a buffer-like interface\r
/// @notice The buffer has an inner cursor that tracks the final offset of every read, i.e. any subsequent read will\r
/// start with the byte that goes right after the last one in the previous read.\r
/// @dev `uint32` is used here for `cursor` because `uint16` would only enable seeking up to 8KB, which could in some\r
/// theoretical use cases be exceeded. Conversely, `uint32` supports up to 512MB, which cannot credibly be exceeded.\r
/// @author The Witnet Foundation.\r
library WitnetBuffer {\r
\r
error EmptyBuffer();\r
error IndexOutOfBounds(uint index, uint range);\r
error MissingArgs(uint expected, uint given);\r
\r
/// Iterable bytes buffer.\r
struct Buffer {\r
bytes data;\r
uint cursor;\r
}\r
\r
// Ensures we access an existing index in an array\r
modifier withinRange(uint index, uint _range) {\r
if (index > _range) {\r
revert IndexOutOfBounds(index, _range);\r
}\r
_;\r
}\r
\r
/// @notice Concatenate undefinite number of bytes chunks.\r
/// @dev Faster than looping on `abi.encodePacked(output, _buffs[ix])`.\r
function concat(bytes[] memory _buffs)\r
internal pure\r
returns (bytes memory output)\r
{\r
unchecked {\r
uint destinationPointer;\r
uint destinationLength;\r
assembly {\r
// get safe scratch location\r
output := mload(0x40)\r
// set starting destination pointer\r
destinationPointer := add(output, 32)\r
} \r
for (uint ix = 1; ix <= _buffs.length; ix ++) { \r
uint source;\r
uint sourceLength;\r
uint sourcePointer; \r
assembly {\r
// load source length pointer\r
source := mload(add(_buffs, mul(ix, 32)))\r
// load source length\r
sourceLength := mload(source)\r
// sets source memory pointer\r
sourcePointer := add(source, 32)\r
}\r
memcpy(\r
destinationPointer,\r
sourcePointer,\r
sourceLength\r
);\r
assembly { \r
// increase total destination length\r
destinationLength := add(destinationLength, sourceLength)\r
// sets destination memory pointer\r
destinationPointer := add(destinationPointer, sourceLength)\r
}\r
}\r
assembly {\r
// protect output bytes\r
mstore(output, destinationLength)\r
// set final output length\r
mstore(0x40, add(mload(0x40), add(destinationLength, 32)))\r
}\r
}\r
}\r
\r
function fork(WitnetBuffer.Buffer memory buffer)\r
internal pure\r
returns (WitnetBuffer.Buffer memory)\r
{\r
return Buffer(\r
buffer.data,\r
buffer.cursor\r
);\r
}\r
\r
function mutate(\r
WitnetBuffer.Buffer memory buffer,\r
uint length,\r
bytes memory pokes\r
)\r
internal pure\r
withinRange(length, buffer.data.length - buffer.cursor + 1)\r
{\r
bytes[] memory parts = new bytes[](3);\r
parts[0] = peek(\r
buffer,\r
0,\r
buffer.cursor\r
);\r
parts[1] = pokes;\r
parts[2] = peek(\r
buffer,\r
buffer.cursor + length,\r
buffer.data.length - buffer.cursor - length\r
);\r
buffer.data = concat(parts);\r
}\r
\r
/// @notice Read and consume the next byte from the buffer.\r
/// @param buffer An instance of `Buffer`.\r
/// @return The next byte in the buffer counting from the cursor position.\r
function next(Buffer memory buffer)\r
internal pure\r
withinRange(buffer.cursor, buffer.data.length)\r
returns (bytes1)\r
{\r
// Return the byte at the position marked by the cursor and advance the cursor all at once\r
return buffer.data[buffer.cursor ++];\r
}\r
\r
function peek(\r
WitnetBuffer.Buffer memory buffer,\r
uint offset,\r
uint length\r
)\r
internal pure\r
withinRange(offset + length, buffer.data.length)\r
returns (bytes memory)\r
{\r
bytes memory data = buffer.data;\r
bytes memory peeks = new bytes(length);\r
uint destinationPointer;\r
uint sourcePointer;\r
assembly {\r
destinationPointer := add(peeks, 32)\r
sourcePointer := add(add(data, 32), offset)\r
}\r
memcpy(\r
destinationPointer,\r
sourcePointer,\r
length\r
);\r
return peeks;\r
}\r
\r
// @notice Extract bytes array from buffer starting from current cursor.\r
/// @param buffer An instance of `Buffer`.\r
/// @param length How many bytes to peek from the Buffer.\r
// solium-disable-next-line security/no-assign-params\r
function peek(\r
WitnetBuffer.Buffer memory buffer,\r
uint length\r
)\r
internal pure\r
withinRange(length, buffer.data.length - buffer.cursor)\r
returns (bytes memory)\r
{\r
return peek(\r
buffer,\r
buffer.cursor,\r
length\r
);\r
}\r
\r
/// @notice Read and consume a certain amount of bytes from the buffer.\r
/// @param buffer An instance of `Buffer`.\r
/// @param length How many bytes to read and consume from the buffer.\r
/// @return output A `bytes memory` containing the first `length` bytes from the buffer, counting from the cursor position.\r
function read(Buffer memory buffer, uint length)\r
internal pure\r
withinRange(buffer.cursor + length, buffer.data.length)\r
returns (bytes memory output)\r
{\r
// Create a new `bytes memory destination` value\r
output = new bytes(length);\r
// Early return in case that bytes length is 0\r
if (length > 0) {\r
bytes memory input = buffer.data;\r
uint offset = buffer.cursor;\r
// Get raw pointers for source and destination\r
uint sourcePointer;\r
uint destinationPointer;\r
assembly {\r
sourcePointer := add(add(input, 32), offset)\r
destinationPointer := add(output, 32)\r
}\r
// Copy `length` bytes from source to destination\r
memcpy(\r
destinationPointer,\r
sourcePointer,\r
length\r
);\r
// Move the cursor forward by `length` bytes\r
seek(\r
buffer,\r
length,\r
true\r
);\r
}\r
}\r
\r
/// @notice Read and consume the next 2 bytes from the buffer as an IEEE 754-2008 floating point number enclosed in an\r
/// `int32`.\r
/// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values\r
/// by 5 decimal orders so as to get a fixed precision of 5 decimal positions, which should be OK for most `float16`\r
/// use cases. In other words, the integer output of this method is 10,000 times the actual value. The input bytes are\r
/// expected to follow the 16-bit base-2 format (a.k.a. `binary16`) in the IEEE 754-2008 standard.\r
/// @param buffer An instance of `Buffer`.\r
/// @return result The `int32` value of the next 4 bytes in the buffer counting from the cursor position.\r
function readFloat16(Buffer memory buffer)\r
internal pure\r
returns (int32 result)\r
{\r
uint32 value = readUint16(buffer);\r
// Get bit at position 0\r
uint32 sign = value & 0x8000;\r
// Get bits 1 to 5, then normalize to the [-15, 16] range so as to counterweight the IEEE 754 exponent bias\r
int32 exponent = (int32(value & 0x7c00) >> 10) - 15;\r
// Get bits 6 to 15\r
int32 fraction = int32(value & 0x03ff);\r
// Add 2^10 to the fraction if exponent is not -15\r
if (exponent != -15) {\r
fraction |= 0x400;\r
} else if (exponent == 16) {\r
revert(\r
string(abi.encodePacked(\r
"WitnetBuffer.readFloat16: ",\r
sign != 0 ? "negative" : hex"",\r
" infinity"\r
))\r
);\r
}\r
// Compute `2 ^ exponent · (1 + fraction / 1024)`\r
if (exponent >= 0) {\r
result = int32(int(\r
int(1 << uint256(int256(exponent)))\r
* 10000\r
* fraction\r
) >> 10);\r
} else {\r
result = int32(int(\r
int(fraction)\r
* 10000\r
/ int(1 << uint(int(- exponent)))\r
) >> 10);\r
}\r
// Make the result negative if the sign bit is not 0\r
if (sign != 0) {\r
result *= -1;\r
}\r
}\r
\r
/// @notice Consume the next 4 bytes from the buffer as an IEEE 754-2008 floating point number enclosed into an `int`.\r
/// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values\r
/// by 9 decimal orders so as to get a fixed precision of 9 decimal positions, which should be OK for most `float32`\r
/// use cases. In other words, the integer output of this method is 10^9 times the actual value. The input bytes are\r
/// expected to follow the 64-bit base-2 format (a.k.a. `binary32`) in the IEEE 754-2008 standard.\r
/// @param buffer An instance of `Buffer`.\r
/// @return result The `int` value of the next 8 bytes in the buffer counting from the cursor position.\r
function readFloat32(Buffer memory buffer)\r
internal pure\r
returns (int result)\r
{\r
uint value = readUint32(buffer);\r
// Get bit at position 0\r
uint sign = value & 0x80000000;\r
// Get bits 1 to 8, then normalize to the [-127, 128] range so as to counterweight the IEEE 754 exponent bias\r
int exponent = (int(value & 0x7f800000) >> 23) - 127;\r
// Get bits 9 to 31\r
int fraction = int(value & 0x007fffff);\r
// Add 2^23 to the fraction if exponent is not -127\r
if (exponent != -127) {\r
fraction |= 0x800000;\r
} else if (exponent == 128) {\r
revert(\r
string(abi.encodePacked(\r
"WitnetBuffer.readFloat32: ",\r
sign != 0 ? "negative" : hex"",\r
" infinity"\r
))\r
);\r
}\r
// Compute `2 ^ exponent · (1 + fraction / 2^23)`\r
if (exponent >= 0) {\r
result = (\r
int(1 << uint(exponent))\r
* (10 ** 9)\r
* fraction\r
) >> 23;\r
} else {\r
result = (\r
fraction \r
* (10 ** 9)\r
/ int(1 << uint(-exponent)) \r
) >> 23;\r
}\r
// Make the result negative if the sign bit is not 0\r
if (sign != 0) {\r
result *= -1;\r
}\r
}\r
\r
/// @notice Consume the next 8 bytes from the buffer as an IEEE 754-2008 floating point number enclosed into an `int`.\r
/// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values\r
/// by 15 decimal orders so as to get a fixed precision of 15 decimal positions, which should be OK for most `float64`\r
/// use cases. In other words, the integer output of this method is 10^15 times the actual value. The input bytes are\r
/// expected to follow the 64-bit base-2 format (a.k.a. `binary64`) in the IEEE 754-2008 standard.\r
/// @param buffer An instance of `Buffer`.\r
/// @return result The `int` value of the next 8 bytes in the buffer counting from the cursor position.\r
function readFloat64(Buffer memory buffer)\r
internal pure\r
returns (int result)\r
{\r
uint value = readUint64(buffer);\r
// Get bit at position 0\r
uint sign = value & 0x8000000000000000;\r
// Get bits 1 to 12, then normalize to the [-1023, 1024] range so as to counterweight the IEEE 754 exponent bias\r
int exponent = (int(value & 0x7ff0000000000000) >> 52) - 1023;\r
// Get bits 6 to 15\r
int fraction = int(value & 0x000fffffffffffff);\r
// Add 2^52 to the fraction if exponent is not -1023\r
if (exponent != -1023) {\r
fraction |= 0x10000000000000;\r
} else if (exponent == 1024) {\r
revert(\r
string(abi.encodePacked(\r
"WitnetBuffer.readFloat64: ",\r
sign != 0 ? "negative" : hex"",\r
" infinity"\r
))\r
);\r
}\r
// Compute `2 ^ exponent · (1 + fraction / 1024)`\r
if (exponent >= 0) {\r
result = (\r
int(1 << uint(exponent))\r
* (10 ** 15)\r
* fraction\r
) >> 52;\r
} else {\r
result = (\r
fraction \r
* (10 ** 15)\r
/ int(1 << uint(-exponent)) \r
) >> 52;\r
}\r
// Make the result negative if the sign bit is not 0\r
if (sign != 0) {\r
result *= -1;\r
}\r
}\r
\r
// Read a text string of a given length from a buffer. Returns a `bytes memory` value for the sake of genericness,\r
/// but it can be easily casted into a string with `string(result)`.\r
// solium-disable-next-line security/no-assign-params\r
function readText(\r
WitnetBuffer.Buffer memory buffer,\r
uint64 length\r
)\r
internal pure\r
returns (bytes memory text)\r
{\r
text = new bytes(length);\r
unchecked {\r
for (uint64 index = 0; index < length; index ++) {\r
uint8 char = readUint8(buffer);\r
if (char & 0x80 != 0) {\r
if (char < 0xe0) {\r
char = (char & 0x1f) << 6\r
| (readUint8(buffer) & 0x3f);\r
length -= 1;\r
} else if (char < 0xf0) {\r
char = (char & 0x0f) << 12\r
| (readUint8(buffer) & 0x3f) << 6\r
| (readUint8(buffer) & 0x3f);\r
length -= 2;\r
} else {\r
char = (char & 0x0f) << 18\r
| (readUint8(buffer) & 0x3f) << 12\r
| (readUint8(buffer) & 0x3f) << 6 \r
| (readUint8(buffer) & 0x3f);\r
length -= 3;\r
}\r
}\r
text[index] = bytes1(char);\r
}\r
// Adjust text to actual length:\r
assembly {\r
mstore(text, length)\r
}\r
}\r
}\r
\r
/// @notice Read and consume the next byte from the buffer as an `uint8`.\r
/// @param buffer An instance of `Buffer`.\r
/// @return value The `uint8` value of the next byte in the buffer counting from the cursor position.\r
function readUint8(Buffer memory buffer)\r
internal pure\r
withinRange(buffer.cursor, buffer.data.length)\r
returns (uint8 value)\r
{\r
bytes memory data = buffer.data;\r
uint offset = buffer.cursor;\r
assembly {\r
value := mload(add(add(data, 1), offset))\r
}\r
buffer.cursor ++;\r
}\r
\r
/// @notice Read and consume the next 2 bytes from the buffer as an `uint16`.\r
/// @param buffer An instance of `Buffer`.\r
/// @return value The `uint16` value of the next 2 bytes in the buffer counting from the cursor position.\r
function readUint16(Buffer memory buffer)\r
internal pure\r
withinRange(buffer.cursor + 2, buffer.data.length)\r
returns (uint16 value)\r
{\r
bytes memory data = buffer.data;\r
uint offset = buffer.cursor;\r
assembly {\r
value := mload(add(add(data, 2), offset))\r
}\r
buffer.cursor += 2;\r
}\r
\r
/// @notice Read and consume the next 4 bytes from the buffer as an `uint32`.\r
/// @param buffer An instance of `Buffer`.\r
/// @return value The `uint32` value of the next 4 bytes in the buffer counting from the cursor position.\r
function readUint32(Buffer memory buffer)\r
internal pure\r
withinRange(buffer.cursor + 4, buffer.data.length)\r
returns (uint32 value)\r
{\r
bytes memory data = buffer.data;\r
uint offset = buffer.cursor;\r
assembly {\r
value := mload(add(add(data, 4), offset))\r
}\r
buffer.cursor += 4;\r
}\r
\r
/// @notice Read and consume the next 8 bytes from the buffer as an `uint64`.\r
/// @param buffer An instance of `Buffer`.\r
/// @return value The `uint64` value of the next 8 bytes in the buffer counting from the cursor position.\r
function readUint64(Buffer memory buffer)\r
internal pure\r
withinRange(buffer.cursor + 8, buffer.data.length)\r
returns (uint64 value)\r
{\r
bytes memory data = buffer.data;\r
uint offset = buffer.cursor;\r
assembly {\r
value := mload(add(add(data, 8), offset))\r
}\r
buffer.cursor += 8;\r
}\r
\r
/// @notice Read and consume the next 16 bytes from the buffer as an `uint128`.\r
/// @param buffer An instance of `Buffer`.\r
/// @return value The `uint128` value of the next 16 bytes in the buffer counting from the cursor position.\r
function readUint128(Buffer memory buffer)\r
internal pure\r
withinRange(buffer.cursor + 16, buffer.data.length)\r
returns (uint128 value)\r
{\r
bytes memory data = buffer.data;\r
uint offset = buffer.cursor;\r
assembly {\r
value := mload(add(add(data, 16), offset))\r
}\r
buffer.cursor += 16;\r
}\r
\r
/// @notice Read and consume the next 32 bytes from the buffer as an `uint256`.\r
/// @param buffer An instance of `Buffer`.\r
/// @return value The `uint256` value of the next 32 bytes in the buffer counting from the cursor position.\r
function readUint256(Buffer memory buffer)\r
internal pure\r
withinRange(buffer.cursor + 32, buffer.data.length)\r
returns (uint256 value)\r
{\r
bytes memory data = buffer.data;\r
uint offset = buffer.cursor;\r
assembly {\r
value := mload(add(add(data, 32), offset))\r
}\r
buffer.cursor += 32;\r
}\r
\r
/// @notice Count number of required parameters for given bytes arrays\r
/// @dev Wildcard format: "\#\", with # in ["0".."9"].\r
/// @param input Bytes array containing strings.\r
/// @param count Highest wildcard index found, plus 1.\r
function argsCountOf(bytes memory input)\r
internal pure\r
returns (uint8 count)\r
{\r
if (input.length < 3) {\r
return 0;\r
}\r
unchecked {\r
uint ix = 0; \r
uint length = input.length - 2;\r
for (; ix < length; ) {\r
if (\r
input[ix] == bytes1("\\")\r
&& input[ix + 2] == bytes1("\\")\r
&& input[ix + 1] >= bytes1("0")\r
&& input[ix + 1] <= bytes1("9")\r
) {\r
uint8 ax = uint8(uint8(input[ix + 1]) - uint8(bytes1("0")) + 1);\r
if (ax > count) {\r
count = ax;\r
}\r
ix += 3;\r
} else {\r
ix ++;\r
}\r
}\r
}\r
}\r
\r
/// @notice Replace indexed bytes-wildcards by correspondent substrings.\r
/// @dev Wildcard format: "\#\", with # in ["0".."9"].\r
/// @param input Bytes array containing strings.\r
/// @param args Array of substring values for replacing indexed wildcards.\r
/// @return output Resulting bytes array after replacing all wildcards.\r
/// @return hits Total number of replaced wildcards.\r
function replace(bytes memory input, string[] memory args)\r
internal pure\r
returns (bytes memory output, uint hits)\r
{\r
uint ix = 0; uint lix = 0;\r
uint inputLength;\r
uint inputPointer;\r
uint outputLength;\r
uint outputPointer; \r
uint source;\r
uint sourceLength;\r
uint sourcePointer;\r
\r
if (input.length < 3) {\r
return (input, 0);\r
}\r
\r
assembly {\r
// set starting input pointer\r
inputPointer := add(input, 32)\r
// get safe output location\r
output := mload(0x40)\r
// set starting output pointer\r
outputPointer := add(output, 32)\r
} \r
\r
unchecked {\r
uint length = input.length - 2;\r
for (; ix < length; ) {\r
if (\r
input[ix] == bytes1("\\")\r
&& input[ix + 2] == bytes1("\\")\r
&& input[ix + 1] >= bytes1("0")\r
&& input[ix + 1] <= bytes1("9")\r
) {\r
inputLength = (ix - lix);\r
if (ix > lix) {\r
memcpy(\r
outputPointer,\r
inputPointer,\r
inputLength\r
);\r
inputPointer += inputLength + 3;\r
outputPointer += inputLength;\r
} else {\r
inputPointer += 3;\r
}\r
uint ax = uint(uint8(input[ix + 1]) - uint8(bytes1("0")));\r
if (ax >= args.length) {\r
revert MissingArgs(ax + 1, args.length);\r
}\r
assembly {\r
source := mload(add(args, mul(32, add(ax, 1))))\r
sourceLength := mload(source)\r
sourcePointer := add(source, 32) \r
} \r
memcpy(\r
outputPointer,\r
sourcePointer,\r
sourceLength\r
);\r
outputLength += inputLength + sourceLength;\r
outputPointer += sourceLength;\r
ix += 3;\r
lix = ix;\r
hits ++;\r
} else {\r
ix ++;\r
}\r
}\r
ix = input.length; \r
}\r
if (outputLength > 0) {\r
if (ix > lix ) {\r
memcpy(\r
outputPointer,\r
inputPointer,\r
ix - lix\r
);\r
outputLength += (ix - lix);\r
}\r
assembly {\r
// set final output length\r
mstore(output, outputLength)\r
// protect output bytes\r
mstore(0x40, add(mload(0x40), add(outputLength, 32)))\r
}\r
}\r
else {\r
return (input, 0);\r
}\r
}\r
\r
/// @notice Replace indexed bytes-wildcard by given substring.\r
/// @dev Wildcard format: "\#\", with # in ["0".."9"].\r
/// @param input Bytes array containing strings.\r
/// @param argIndex Index of the wildcard to be replaced.\r
/// @param argValue Replacing substring to be used.\r
/// @return output Resulting bytes array after replacing all wildcards.\r
/// @return hits Total number of replaced wildcards.\r
function replace(bytes memory input, uint8 argIndex, string memory argValue)\r
internal pure\r
returns (bytes memory output, uint hits)\r
{\r
uint ix = 0; uint lix = 0;\r
uint inputLength;\r
uint inputPointer;\r
uint outputLength;\r
uint outputPointer; \r
uint argValueLength;\r
uint argValuePointer;\r
\r
if (input.length < 3) {\r
return (input, 0);\r
}\r
\r
assembly {\r
// set starting input pointer\r
inputPointer := add(input, 32)\r
// get safe output location\r
output := mload(0x40)\r
// set starting output pointer\r
outputPointer := add(output, 32)\r
// set pointer to arg value substring\r
argValuePointer := add(argValue, 32)\r
// set arg value substring length\r
argValueLength := mload(argValue)\r
} \r
\r
unchecked {\r
uint length = input.length - 2;\r
for (; ix < length; ) {\r
if (\r
input[ix] == bytes1("\\")\r
&& input[ix + 2] == bytes1("\\")\r
&& input[ix + 1] >= bytes1("0")\r
&& input[ix + 1] <= bytes1("9")\r
&& uint8(input[ix + 1]) - uint8(bytes1("0")) == argIndex\r
) {\r
inputLength = (ix - lix);\r
if (ix > lix) {\r
memcpy(\r
outputPointer,\r
inputPointer,\r
inputLength\r
);\r
inputPointer += inputLength + 3;\r
outputPointer += inputLength;\r
} else {\r
inputPointer += 3;\r
}\r
memcpy(\r
outputPointer,\r
argValuePointer,\r
argValueLength\r
);\r
outputLength += inputLength + argValueLength;\r
outputPointer += argValueLength;\r
ix += 3;\r
lix = ix;\r
hits ++;\r
} else {\r
ix ++;\r
}\r
}\r
ix = input.length; \r
}\r
if (outputLength > 0) {\r
if (ix > lix ) {\r
memcpy(\r
outputPointer,\r
inputPointer,\r
ix - lix\r
);\r
outputLength += (ix - lix);\r
}\r
assembly {\r
// set final output length\r
mstore(output, outputLength)\r
// protect output bytes\r
mstore(0x40, add(mload(0x40), add(outputLength, 32)))\r
}\r
}\r
else {\r
return (input, 0);\r
}\r
}\r
\r
/// @notice Replace indexed string wildcards by correspondent substrings.\r
/// @dev Wildcard format: "\#\", with # in ["0".."9"].\r
/// @param input String potentially containing wildcards.\r
/// @param args Array of substring values for replacing indexed wildcards.\r
/// @return output Resulting string after replacing all wildcards.\r
function replace(string memory input, string[] memory args)\r
internal pure\r
returns (string memory)\r
{\r
(bytes memory _outputBytes, ) = replace(bytes(input), args);\r
return string(_outputBytes);\r
}\r
\r
/// @notice Replace last indexed wildcard by given substring.\r
/// @dev Wildcard format: "\#\", with # in ["0".."9"].\r
/// @param input String potentially containing wildcards.\r
/// @param argIndex Index of the wildcard to be replaced.\r
/// @param argValue Replacing string to be used.\r
/// @return output Resulting string after replacing all wildcards.\r
function replace(string memory input, uint8 argIndex, string memory argValue)\r
internal pure\r
returns (string memory)\r
{\r
(bytes memory _outputBytes, ) = replace(bytes(input), argIndex, argValue);\r
return string(_outputBytes);\r
}\r
\r
/// @notice Move the inner cursor of the buffer to a relative or absolute position.\r
/// @param buffer An instance of `Buffer`.\r
/// @param offset How many bytes to move the cursor forward.\r
/// @param relative Whether to count `offset` from the last position of the cursor (`true`) or the beginning of the\r
/// buffer (`true`).\r
/// @return The final position of the cursor (will equal `offset` if `relative` is `false`).\r
// solium-disable-next-line security/no-assign-params\r
function seek(\r
Buffer memory buffer,\r
uint offset,\r
bool relative\r
)\r
internal pure\r
withinRange(offset, buffer.data.length)\r
returns (uint)\r
{\r
// Deal with relative offsets\r
if (relative) {\r
offset += buffer.cursor;\r
}\r
buffer.cursor = offset;\r
return offset;\r
}\r
\r
/// @notice Move the inner cursor a number of bytes forward.\r
/// @dev This is a simple wrapper around the relative offset case of `seek()`.\r
/// @param buffer An instance of `Buffer`.\r
/// @param relativeOffset How many bytes to move the cursor forward.\r
/// @return The final position of the cursor.\r
function seek(\r
Buffer memory buffer,\r
uint relativeOffset\r
)\r
internal pure\r
returns (uint)\r
{\r
return seek(\r
buffer,\r
relativeOffset,\r
true\r
);\r
}\r
\r
/// @notice Copy bytes from one memory address into another.\r
/// @dev This function was borrowed from Nick Johnson's `solidity-stringutils` lib, and reproduced here under the terms\r
/// of [Apache License 2.0](https://github.com/Arachnid/solidity-stringutils/blob/master/LICENSE).\r
/// @param dest Address of the destination memory.\r
/// @param src Address to the source memory.\r
/// @param len How many bytes to copy.\r
// solium-disable-next-line security/no-assign-params\r
function memcpy(\r
uint dest,\r
uint src,\r
uint len\r
)\r
private pure\r
{\r
unchecked {\r
// Copy word-length chunks while possible\r
for (; len >= 32; len -= 32) {\r
assembly {\r
mstore(dest, mload(src))\r
}\r
dest += 32;\r
src += 32;\r
}\r
if (len > 0) {\r
// Copy remaining bytes\r
uint _mask = 256 ** (32 - len) - 1;\r
assembly {\r
let srcpart := and(mload(src), not(_mask))\r
let destpart := and(mload(dest), _mask)\r
mstore(dest, or(destpart, srcpart))\r
}\r
}\r
}\r
}\r
\r
}"
},
"/contracts/libs/Witnet.sol": {
"content": "// SPDX-License-Identifier: MIT\r
\r
pragma solidity >=0.8.0 <0.9.0;\r
\r
import "./Bech32.sol";\r
import "./Secp256k1.sol";\r
import "./WitnetCBOR.sol";\r
\r
library Witnet {\r
\r
using Bech32 for Witnet.Address;\r
using WitnetBuffer for WitnetBuffer.Buffer;\r
using WitnetCBOR for WitnetCBOR.CBOR;\r
using WitnetCBOR for WitnetCBOR.CBOR[];\r
\r
type Address is bytes20;\r
\r
type BlockNumber is uint64;\r
\r
type QueryEvmReward is uint72;\r
type QueryUUID is bytes15;\r
type QueryId is uint64;\r
\r
type RadonHash is bytes32;\r
type ServiceProvider is bytes20;\r
\r
type Timestamp is uint64;\r
type TransactionHash is bytes32;\r
\r
uint32 constant internal WIT_1_GENESIS_TIMESTAMP = 0; // TBD \r
uint32 constant internal WIT_1_SECS_PER_EPOCH = 45;\r
\r
uint32 constant internal WIT_2_GENESIS_BEACON_INDEX = 0; // TBD\r
uint32 constant internal WIT_2_GENESIS_BEACON_PREV_INDEX = 0; // TBD\r
bytes24 constant internal WIT_2_GENESIS_BEACON_PREV_ROOT = 0; // TBD\r
bytes16 constant internal WIT_2_GENESIS_BEACON_DDR_TALLIES_MERKLE_ROOT = 0; // TBD\r
bytes16 constant internal WIT_2_GENESIS_BEACON_DRO_TALLIES_MERKLE_ROOT = 0; // TBD\r
uint256 constant internal WIT_2_GENESIS_BEACON_NEXT_COMMITTEE_AGG_PUBKEY_0 = 0; // TBD\r
uint256 constant internal WIT_2_GENESIS_BEACON_NEXT_COMMITTEE_AGG_PUBKEY_1 = 0; // TBD\r
uint256 constant internal WIT_2_GENESIS_BEACON_NEXT_COMMITTEE_AGG_PUBKEY_2 = 0; // TBD\r
uint256 constant internal WIT_2_GENESIS_BEACON_NEXT_COMMITTEE_AGG_PUBKEY_3 = 0; // TBD\r
uint32 constant internal WIT_2_GENESIS_EPOCH = 0; // TBD\r
uint32 constant internal WIT_2_GENESIS_TIMESTAMP = 0; // TBD\r
uint32 constant internal WIT_2_SECS_PER_EPOCH = 20; // TBD\r
uint32 constant internal WIT_2_FAST_FORWARD_COMMITTEE_SIZE = 64; // TBD\r
\r
\r
function channel(address wrb) internal view returns (bytes4) {\r
return bytes4(keccak256(abi.encode(address(wrb), block.chainid)));\r
}\r
\r
struct Beacon {\r
uint32 index;\r
uint32 prevIndex;\r
bytes24 prevRoot;\r
bytes16 ddrTalliesMerkleRoot;\r
bytes16 droTalliesMerkleRoot;\r
uint256[4] nextCommitteeAggPubkey;\r
}\r
\r
struct DataPullReport {\r
QueryId queryId;\r
QueryUUID queryHash; // KECCAK256(channel | blockhash(block.number - 1) | ...)\r
bytes witDrRelayerSignature; // ECDSA.signature(queryHash)\r
BlockNumber witDrResultEpoch;\r
bytes witDrResultCborBytes;\r
TransactionHash witDrTxHash;\r
}\r
\r
struct DataPushReport {\r
TransactionHash witDrTxHash;\r
RadonHash queryRadHash;\r
QuerySLA queryParams;\r
Timestamp resultTimestamp;\r
bytes resultCborBytes;\r
}\r
\r
/// Data struct containing the Witnet-provided result to a Data Request.\r
struct DataResult {\r
ResultStatus status;\r
RadonDataTypes dataType;\r
TransactionHash drTxHash;\r
uint256 finality;\r
Timestamp timestamp;\r
WitnetCBOR.CBOR value;\r
}\r
\r
struct FastForward {\r
Beacon beacon;\r
uint256[2] committeeAggSignature;\r
uint256[4][] committeeMissingPubkeys;\r
}\r
\r
/// Struct containing both request and response data related to every query posted to the Witnet Request Board\r
struct Query {\r
QueryRequest request;\r
QueryResponse response;\r
QuerySLA slaParams; // Minimum Service-Level parameters to be committed by the Witnet blockchain.\r
QueryUUID uuid; // Universal unique identifier determined by the payload, WRB instance, chain id and EVM's previous block hash.\r
QueryEvmReward reward; // EVM amount in wei eventually to be paid to the legit reporter.\r
BlockNumber checkpoint;\r
}\r
\r
/// Possible status of a Witnet query.\r
enum QueryStatus {\r
Unknown,\r
Posted,\r
Reported,\r
Finalized,\r
Delayed,\r
Expired,\r
Disputed\r
}\r
\r
struct QueryCallback {\r
address consumer; // consumer contract address to which the query result will be reported\r
uint24 gasLimit; // expected max amount of gas required by the callback method in the consumer contract\r
}\r
\r
/// Data kept in EVM-storage for every Request posted to the Witnet Request Board.\r
struct QueryRequest {\r
address requester; // EVM address from which the request was posted.\r
uint24 callbackGas; // Max callback gas limit upon response, if a callback is required.\r
bytes radonBytecode; // Optional: Witnet Data Request bytecode to be solved by the Witnet blockchain.\r
RadonHash radonHash; // Optional: Previously verified hash of the Witnet Data Request to be solved.\r
}\r
\r
/// QueryResponse metadata and result as resolved by the Witnet blockchain.\r
struct QueryResponse {\r
address reporter; // EVM address from which the Data Request result was reported.\r
Timestamp resultTimestamp; // Unix timestamp (seconds) at which the data request was resolved in the Witnet blockchain.\r
TransactionHash resultDrTxHash; // Unique hash of the commit/reveal act in the Witnet blockchain that resolved the data request.\r
bytes resultCborBytes; // CBOR-encode result to the request, as resolved in the Witnet blockchain.\r
address disputer;\r
}\r
\r
/// Structure containing all possible SLA security parameters for Wit/2.1 Data Requests\r
struct QuerySLA {\r
uint16 witResultMaxSize; // max size permitted to whatever query result may come from the Wit/Oracle blockchain.\r
uint16 witCommitteeSize; // max number of eligibile witnesses in the Wit/Oracle blockchain for solving some query.\r
uint64 witUnitaryReward; // min fees in nanowits to be paid for getting the query solved and reported from the Wit/Oracle.\r
}\r
\r
enum ResultStatus {\r
/// 0x00: No errors.\r
NoErrors,\r
\r
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\r
/// Source-specific format error sub-codes ============================================================================\r
\r
/// 0x01: At least one of the source scripts is not a valid CBOR-encoded value.\r
SourceScriptNotCBOR, \r
\r
/// 0x02: The CBOR value decoded from a source script is not an Array.\r
SourceScriptNotArray,\r
\r
/// 0x03: The Array value decoded form a source script is not a valid Data Request.\r
SourceScriptNotRADON,\r
\r
/// 0x04: The request body of at least one data
Submitted on: 2025-10-14 13:44:01
Comments
Log in to comment.
No comments yet.