From 0c1eda1b86ab9ae12159804b9df81bdc50b371ea Mon Sep 17 00:00:00 2001 From: nonergodic Date: Mon, 2 Sep 2024 13:25:19 -0700 Subject: [PATCH 1/6] cleanup, fix, refactor --- src/QueryResponse.sol | 1074 +++++++++---------- src/testing/helpers/QueryTest.sol | 581 +++++------ test/QueryResponse.t.sol | 1604 +++++++++++++++-------------- test/QueryTest.t.sol | 548 +++++----- 4 files changed, 1828 insertions(+), 1979 deletions(-) diff --git a/src/QueryResponse.sol b/src/QueryResponse.sol index b52bb9a..6ce8e17 100644 --- a/src/QueryResponse.sol +++ b/src/QueryResponse.sol @@ -3,122 +3,140 @@ pragma solidity ^0.8.4; import {BytesParsing} from "./libraries/BytesParsing.sol"; -import "./interfaces/IWormhole.sol"; - -// @dev ParsedQueryResponse is returned by QueryResponse.parseAndVerifyQueryResponse(). -struct ParsedQueryResponse { - uint8 version; - uint16 senderChainId; - uint32 nonce; - bytes requestId; // 65 byte sig for off-chain, 32 byte vaaHash for on-chain - ParsedPerChainQueryResponse [] responses; +import {IWormhole} from "./interfaces/IWormhole.sol"; + +error UnsupportedQueryType(uint8 received); + +library QueryType { + //Solidity enums don't permit custom values (i.e. can't start from 1) + //Also invalid enum conversions result in panics and manual range checking requires assembly + // to avoid superfluous double checking. + //So we're sticking with uint8 constants instead. + uint8 internal constant ETH_CALL = 1; + uint8 internal constant ETH_CALL_BY_TIMESTAMP = 2; + uint8 internal constant ETH_CALL_WITH_FINALITY = 3; + uint8 internal constant SOLANA_ACCOUNT = 4; + uint8 internal constant SOLANA_PDA = 5; + + //emulate type(enum).min/max for external consumers (mainly tests) + function min() internal pure returns (uint8) { return ETH_CALL; } + function max() internal pure returns (uint8) { return SOLANA_PDA; } + + function checkValid(uint8 queryType) internal pure { + //slightly more gas efficient than calling `isValid` + if (queryType == 0 || queryType > SOLANA_PDA) + revert UnsupportedQueryType(queryType); + } + + function isValid(uint8 queryType) internal pure returns (bool) { + //see docs/optimizations.md why `< CONST + 1` rather than `<= CONST` + return (queryType > 0 && queryType < SOLANA_PDA + 1); + } +} + +struct QueryResponse { + uint8 version; + uint16 senderChainId; + uint32 nonce; + bytes requestId; // 65 byte sig for off-chain, 32 byte vaaHash for on-chain + PerChainQueryResponse[] responses; } -// @dev ParsedPerChainQueryResponse describes a single per-chain response. -struct ParsedPerChainQueryResponse { - uint16 chainId; - uint8 queryType; - bytes request; - bytes response; +struct PerChainQueryResponse { + uint16 chainId; + uint8 queryType; + bytes request; + bytes response; } -// @dev EthCallQueryResponse describes the response to an ETH call per-chain query. struct EthCallQueryResponse { - bytes requestBlockId; - uint64 blockNum; - uint64 blockTime; - bytes32 blockHash; - EthCallData [] result; + bytes requestBlockId; + uint64 blockNum; + uint64 blockTime; + bytes32 blockHash; + EthCallRecord[] results; } -// @dev EthCallByTimestampQueryResponse describes the response to an ETH call by timestamp per-chain query. struct EthCallByTimestampQueryResponse { - bytes requestTargetBlockIdHint; - bytes requestFollowingBlockIdHint; - uint64 requestTargetTimestamp; - uint64 targetBlockNum; - uint64 targetBlockTime; - uint64 followingBlockNum; - bytes32 targetBlockHash; - bytes32 followingBlockHash; - uint64 followingBlockTime; - EthCallData [] result; + bytes requestTargetBlockIdHint; + bytes requestFollowingBlockIdHint; + uint64 requestTargetTimestamp; + uint64 targetBlockNum; + uint64 targetBlockTime; + uint64 followingBlockNum; + bytes32 targetBlockHash; + bytes32 followingBlockHash; + uint64 followingBlockTime; + EthCallRecord[] results; } -// @dev EthCallWithFinalityQueryResponse describes the response to an ETH call with finality per-chain query. struct EthCallWithFinalityQueryResponse { - bytes requestBlockId; - bytes requestFinality; - uint64 blockNum; - uint64 blockTime; - bytes32 blockHash; - EthCallData [] result; + bytes requestBlockId; + bytes requestFinality; + uint64 blockNum; + uint64 blockTime; + bytes32 blockHash; + EthCallRecord[] results; } -// @dev EthCallData describes a single ETH call query / response pair. -struct EthCallData { - address contractAddress; - bytes callData; - bytes result; +struct EthCallRecord { + address contractAddress; + bytes callData; + bytes result; } -// @dev SolanaAccountQueryResponse describes the response to a Solana Account query per-chain query. struct SolanaAccountQueryResponse { - bytes requestCommitment; - uint64 requestMinContextSlot; - uint64 requestDataSliceOffset; - uint64 requestDataSliceLength; - uint64 slotNumber; - uint64 blockTime; - bytes32 blockHash; - SolanaAccountResult [] results; + bytes requestCommitment; + uint64 requestMinContextSlot; + uint64 requestDataSliceOffset; + uint64 requestDataSliceLength; + uint64 slotNumber; + uint64 blockTime; + bytes32 blockHash; + SolanaAccountResult[] results; } -// @dev SolanaAccountResult describes a single Solana Account query result. struct SolanaAccountResult { - bytes32 account; - uint64 lamports; - uint64 rentEpoch; - bool executable; - bytes32 owner; - bytes data; + bytes32 account; + uint64 lamports; + uint64 rentEpoch; + bool executable; + bytes32 owner; + bytes data; } -// @dev SolanaPdaQueryResponse describes the response to a Solana PDA (Program Derived Address) query per-chain query. struct SolanaPdaQueryResponse { - bytes requestCommitment; - uint64 requestMinContextSlot; - uint64 requestDataSliceOffset; - uint64 requestDataSliceLength; - uint64 slotNumber; - uint64 blockTime; - bytes32 blockHash; - SolanaPdaResult [] results; + bytes requestCommitment; + uint64 requestMinContextSlot; + uint64 requestDataSliceOffset; + uint64 requestDataSliceLength; + uint64 slotNumber; + uint64 blockTime; + bytes32 blockHash; + SolanaPdaResult[] results; } -// @dev SolanaPdaResult describes a single Solana PDA (Program Derived Address) query result. struct SolanaPdaResult { - bytes32 programId; - bytes[] seeds; - bytes32 account; - uint64 lamports; - uint64 rentEpoch; - bool executable; - bytes32 owner; - bytes data; - uint8 bump; + bytes32 programId; + bytes[] seeds; + bytes32 account; + uint64 lamports; + uint64 rentEpoch; + bool executable; + bytes32 owner; + bytes data; + uint8 bump; } // Custom errors -error EmptyWormholeAddress(); + +error WrongQueryType(uint8 received, uint8 expected); error InvalidResponseVersion(); error VersionMismatch(); error ZeroQueries(); error NumberOfResponsesMismatch(); error ChainIdMismatch(); error RequestTypeMismatch(); -error UnsupportedQueryType(uint8 received); -error WrongQueryType(uint8 received, uint8 expected); error UnexpectedNumberOfResults(); error InvalidPayloadLength(uint256 received, uint256 expected); error InvalidContractAddress(); @@ -126,550 +144,434 @@ error InvalidFunctionSignature(); error InvalidChainId(); error StaleBlockNum(); error StaleBlockTime(); - -// @dev QueryResponse is a library that implements the parsing and verification of Cross Chain Query (CCQ) responses. -// For a detailed discussion of these query responses, please see the white paper: -// https://github.com/wormhole-foundation/wormhole/blob/main/whitepapers/0013_ccq.md -abstract contract QueryResponse { - using BytesParsing for bytes; - - IWormhole public immutable wormhole; - - bytes public constant responsePrefix = bytes("query_response_0000000000000000000|"); - uint8 public constant VERSION = 1; - - // TODO: Consider changing these to an enum. - uint8 public constant QT_ETH_CALL = 1; - uint8 public constant QT_ETH_CALL_BY_TIMESTAMP = 2; - uint8 public constant QT_ETH_CALL_WITH_FINALITY = 3; - uint8 public constant QT_SOL_ACCOUNT = 4; - uint8 public constant QT_SOL_PDA = 5; - uint8 public constant QT_MAX = 6; // Keep this last - - constructor(address _wormhole) { - if (_wormhole == address(0)) { - revert EmptyWormholeAddress(); - } - - wormhole = IWormhole(_wormhole); - } - - /// @dev getResponseHash computes the hash of the specified query response. - function getResponseHash(bytes memory response) public pure returns (bytes32) { - return keccak256(response); - } - - /// @dev getResponseDigest computes the digest of the specified query response. - function getResponseDigest(bytes memory response) public pure returns (bytes32) { - return keccak256(abi.encodePacked(responsePrefix,getResponseHash(response))); - } - - /// @dev parseAndVerifyQueryResponse verifies the query response and returns the parsed response. - function parseAndVerifyQueryResponse(bytes memory response, IWormhole.Signature[] memory signatures) public view returns (ParsedQueryResponse memory r) { - verifyQueryResponseSignatures(response, signatures); - - uint index; - - (r.version, index) = response.asUint8Unchecked(index); - if (r.version != VERSION) { - revert InvalidResponseVersion(); - } - - (r.senderChainId, index) = response.asUint16Unchecked(index); - - // For off chain requests (chainID zero), the requestId is the 65 byte signature. For on chain requests, it is the 32 byte VAA hash. - if (r.senderChainId == 0) { - (r.requestId, index) = response.sliceUnchecked(index, 65); - } else { - (r.requestId, index) = response.sliceUnchecked(index, 32); - } - - uint32 len; - (len, index) = response.asUint32Unchecked(index); // query_request_len - uint reqIdx = index; - - // Scope to avoid stack-too-deep error - { - uint8 version; - (version, reqIdx) = response.asUint8Unchecked(reqIdx); - if (version != r.version) { - revert VersionMismatch(); - } - } - - (r.nonce, reqIdx) = response.asUint32Unchecked(reqIdx); - - uint8 numPerChainQueries; - (numPerChainQueries, reqIdx) = response.asUint8Unchecked(reqIdx); - - // A valid query request has at least one per chain query - if (numPerChainQueries == 0) { - revert ZeroQueries(); - } - - // The response starts after the request. - uint respIdx = index + len; - uint startOfResponse = respIdx; - - uint8 respNumPerChainQueries; - (respNumPerChainQueries, respIdx) = response.asUint8Unchecked(respIdx); - if (respNumPerChainQueries != numPerChainQueries) { - revert NumberOfResponsesMismatch(); - } - - r.responses = new ParsedPerChainQueryResponse[](numPerChainQueries); - - // Walk through the requests and responses in lock step. - for (uint idx; idx < numPerChainQueries;) { - (r.responses[idx].chainId, reqIdx) = response.asUint16Unchecked(reqIdx); - uint16 respChainId; - (respChainId, respIdx) = response.asUint16Unchecked(respIdx); - if (respChainId != r.responses[idx].chainId) { - revert ChainIdMismatch(); - } - - (r.responses[idx].queryType, reqIdx) = response.asUint8Unchecked(reqIdx); - uint8 respQueryType; - (respQueryType, respIdx) = response.asUint8Unchecked(respIdx); - if (respQueryType != r.responses[idx].queryType) { - revert RequestTypeMismatch(); - } - - if (r.responses[idx].queryType < QT_ETH_CALL || r.responses[idx].queryType >= QT_MAX) { - revert UnsupportedQueryType(r.responses[idx].queryType); - } - - (len, reqIdx) = response.asUint32Unchecked(reqIdx); - (r.responses[idx].request, reqIdx) = response.sliceUnchecked(reqIdx, len); - - (len, respIdx) = response.asUint32Unchecked(respIdx); - (r.responses[idx].response, respIdx) = response.sliceUnchecked(respIdx, len); - - unchecked { ++idx; } - } - - // End of request body should align with start of response body - if (startOfResponse != reqIdx) { - revert InvalidPayloadLength(startOfResponse, reqIdx); - } - - checkLength(response, respIdx); - return r; - } - - /// @dev parseEthCallQueryResponse parses a ParsedPerChainQueryResponse for an ETH call per-chain query. - function parseEthCallQueryResponse(ParsedPerChainQueryResponse memory pcr) public pure returns (EthCallQueryResponse memory r) { - if (pcr.queryType != QT_ETH_CALL) { - revert WrongQueryType(pcr.queryType, QT_ETH_CALL); - } - - uint reqIdx; - uint respIdx; - - uint32 len; - (len, reqIdx) = pcr.request.asUint32Unchecked(reqIdx); // block_id_len - - (r.requestBlockId, reqIdx) = pcr.request.sliceUnchecked(reqIdx, len); - - uint8 numBatchCallData; - (numBatchCallData, reqIdx) = pcr.request.asUint8Unchecked(reqIdx); - - (r.blockNum, respIdx) = pcr.response.asUint64Unchecked(respIdx); - - (r.blockHash, respIdx) = pcr.response.asBytes32Unchecked(respIdx); - - (r.blockTime, respIdx) = pcr.response.asUint64Unchecked(respIdx); - - uint8 respNumResults; - (respNumResults, respIdx) = pcr.response.asUint8Unchecked(respIdx); - if (respNumResults != numBatchCallData) { - revert UnexpectedNumberOfResults(); - } - - r.result = new EthCallData[](numBatchCallData); - - // Walk through the call data and results in lock step. - for (uint idx; idx < numBatchCallData;) { - (r.result[idx].contractAddress, reqIdx) = pcr.request.asAddressUnchecked(reqIdx); - - (len, reqIdx) = pcr.request.asUint32Unchecked(reqIdx); // call_data_len - (r.result[idx].callData, reqIdx) = pcr.request.sliceUnchecked(reqIdx, len); - - (len, respIdx) = pcr.response.asUint32Unchecked(respIdx); // result_len - (r.result[idx].result, respIdx) = pcr.response.sliceUnchecked(respIdx, len); - - unchecked { ++idx; } - } - - checkLength(pcr.request, reqIdx); - checkLength(pcr.response, respIdx); - return r; +error NoQuorum(); +error VerificationFailed(); + +//QueryResponse is a library that implements the parsing and verification of +// Cross Chain Query (CCQ) responses. +// +//For a detailed discussion of these query responses, please see the white paper: +// https://github.com/wormhole-foundation/wormhole/blob/main/whitepapers/0013_ccq.md +library QueryResponseLib { + using BytesParsing for bytes; + + bytes internal constant RESPONSE_PREFIX = bytes("query_response_0000000000000000000|"); + uint8 internal constant VERSION = 1; + uint64 internal constant MICROSECONDS_PER_SECOND = 1_000_000; + + function calcPrefixedResponseHash(bytes memory response) internal pure returns (bytes32) { + return keccak256(abi.encodePacked(RESPONSE_PREFIX, keccak256(response))); + } + + //WARNING: see verifyQueryResponse WARNING + function parseAndVerifyQueryResponse( + address wormhole, + bytes memory response, + IWormhole.Signature[] memory signatures + ) internal view returns (QueryResponse memory ret) { + verifyQueryResponse(wormhole, response, signatures); + return parseQueryResponse(response); + } + + //WARNING: This call can fail during times of guardian set rotation. + // Unlikely, but possible: + // Since only the current guardian set is considered when verifying signatures here, a response + // will be rejected with a failed verification, even if the signing guardian is still within + // what would otherwise be the 24 hour transition window. + function verifyQueryResponse( + address wormhole, + bytes memory response, + IWormhole.Signature[] memory signatures + ) internal view { unchecked { + IWormhole wormhole_ = IWormhole(wormhole); + IWormhole.GuardianSet memory guardianSet = + wormhole_.getGuardianSet(wormhole_.getCurrentGuardianSetIndex()); + uint quorum = guardianSet.keys.length * 2 / 3 + 1; + if (signatures.length < quorum) + revert NoQuorum(); + + (bool signaturesValid, ) = + wormhole_.verifySignatures(calcPrefixedResponseHash(response), signatures, guardianSet); + if(!signaturesValid) + revert VerificationFailed(); + }} + + function parseQueryResponse( + bytes memory response + ) internal pure returns (QueryResponse memory ret) { unchecked { + uint offset; + + (ret.version, offset) = response.asUint8Unchecked(offset); + if (ret.version != VERSION) + revert InvalidResponseVersion(); + + (ret.senderChainId, offset) = response.asUint16Unchecked(offset); + + //For off chain requests (chainID zero), the requestId is the 65 byte signature. + //For on chain requests, it is the 32 byte VAA hash. + (ret.requestId, offset) = response.sliceUnchecked(offset, ret.senderChainId == 0 ? 65 : 32); + + uint32 queryReqLen; + (queryReqLen, offset) = response.asUint32Unchecked(offset); + uint reqOff = offset; + + { + uint8 version; + (version, reqOff) = response.asUint8Unchecked(reqOff); + if (version != ret.version) + revert VersionMismatch(); } - /// @dev parseEthCallByTimestampQueryResponse parses a ParsedPerChainQueryResponse for an ETH call per-chain query. - function parseEthCallByTimestampQueryResponse(ParsedPerChainQueryResponse memory pcr) public pure returns (EthCallByTimestampQueryResponse memory r) { - if (pcr.queryType != QT_ETH_CALL_BY_TIMESTAMP) { - revert WrongQueryType(pcr.queryType, QT_ETH_CALL_BY_TIMESTAMP); - } - - uint reqIdx; - uint respIdx; - uint32 len; - - (r.requestTargetTimestamp, reqIdx) = pcr.request.asUint64Unchecked(reqIdx); // Request target_time_us - - (len, reqIdx) = pcr.request.asUint32Unchecked(reqIdx); // Request target_block_id_hint_len - (r.requestTargetBlockIdHint, reqIdx) = pcr.request.sliceUnchecked(reqIdx, len); // Request target_block_id_hint - - (len, reqIdx) = pcr.request.asUint32Unchecked(reqIdx); // following_block_id_hint_len - (r.requestFollowingBlockIdHint, reqIdx) = pcr.request.sliceUnchecked(reqIdx, len); // Request following_block_id_hint - - uint8 numBatchCallData; - (numBatchCallData, reqIdx) = pcr.request.asUint8Unchecked(reqIdx); // Request num_batch_call_data + (ret.nonce, reqOff) = response.asUint32Unchecked(reqOff); - (r.targetBlockNum, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response target_block_number - (r.targetBlockHash, respIdx) = pcr.response.asBytes32Unchecked(respIdx); // Response target_block_hash - (r.targetBlockTime, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response target_block_time_us + uint8 numPerChainQueries; + (numPerChainQueries, reqOff) = response.asUint8Unchecked(reqOff); - (r.followingBlockNum, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response following_block_number - (r.followingBlockHash, respIdx) = pcr.response.asBytes32Unchecked(respIdx); // Response following_block_hash - (r.followingBlockTime, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response following_block_time_us + //A valid query request has at least one per chain query + if (numPerChainQueries == 0) + revert ZeroQueries(); - uint8 respNumResults; - (respNumResults, respIdx) = pcr.response.asUint8Unchecked(respIdx); // Response num_results - if (respNumResults != numBatchCallData) { - revert UnexpectedNumberOfResults(); - } + //The response starts after the request. + uint respOff = offset + queryReqLen; + uint startOfResponse = respOff; - r.result = new EthCallData[](numBatchCallData); + uint8 respNumPerChainQueries; + (respNumPerChainQueries, respOff) = response.asUint8Unchecked(respOff); + if (respNumPerChainQueries != numPerChainQueries) + revert NumberOfResponsesMismatch(); - // Walk through the call data and results in lock step. - for (uint idx; idx < numBatchCallData;) { - (r.result[idx].contractAddress, reqIdx) = pcr.request.asAddressUnchecked(reqIdx); + ret.responses = new PerChainQueryResponse[](numPerChainQueries); - (len, reqIdx) = pcr.request.asUint32Unchecked(reqIdx); // call_data_len - (r.result[idx].callData, reqIdx) = pcr.request.sliceUnchecked(reqIdx, len); + //Walk through the requests and responses in lock step. + for (uint i; i < numPerChainQueries; ++i) { + (ret.responses[i].chainId, reqOff) = response.asUint16Unchecked(reqOff); + uint16 respChainId; + (respChainId, respOff) = response.asUint16Unchecked(respOff); + if (respChainId != ret.responses[i].chainId) + revert ChainIdMismatch(); - (len, respIdx) = pcr.response.asUint32Unchecked(respIdx); // result_len - (r.result[idx].result, respIdx) = pcr.response.sliceUnchecked(respIdx, len); + (ret.responses[i].queryType, reqOff) = response.asUint8Unchecked(reqOff); + QueryType.checkValid(ret.responses[i].queryType); + uint8 respQueryType; + (respQueryType, respOff) = response.asUint8Unchecked(respOff); + if (respQueryType != ret.responses[i].queryType) + revert RequestTypeMismatch(); - unchecked { ++idx; } - } + (ret.responses[i].request, reqOff) = response.sliceUint32PrefixedUnchecked(reqOff); - checkLength(pcr.request, reqIdx); - checkLength(pcr.response, respIdx); + (ret.responses[i].response, respOff) = response.sliceUint32PrefixedUnchecked(respOff); } - /// @dev parseEthCallWithFinalityQueryResponse parses a ParsedPerChainQueryResponse for an ETH call per-chain query. - function parseEthCallWithFinalityQueryResponse(ParsedPerChainQueryResponse memory pcr) public pure returns (EthCallWithFinalityQueryResponse memory r) { - if (pcr.queryType != QT_ETH_CALL_WITH_FINALITY) { - revert WrongQueryType(pcr.queryType, QT_ETH_CALL_WITH_FINALITY); - } - - uint reqIdx; - uint respIdx; - uint32 len; - - (len, reqIdx) = pcr.request.asUint32Unchecked(reqIdx); // Request block_id_len - (r.requestBlockId, reqIdx) = pcr.request.sliceUnchecked(reqIdx, len); // Request block_id - - (len, reqIdx) = pcr.request.asUint32Unchecked(reqIdx); // Request finality_len - (r.requestFinality, reqIdx) = pcr.request.sliceUnchecked(reqIdx, len); // Request finality - - uint8 numBatchCallData; - (numBatchCallData, reqIdx) = pcr.request.asUint8Unchecked(reqIdx); // Request num_batch_call_data + //End of request body should align with start of response body + if (startOfResponse != reqOff) + revert InvalidPayloadLength(startOfResponse, reqOff); - (r.blockNum, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response block_number + checkLength(response, respOff); + return ret; + }} - (r.blockHash, respIdx) = pcr.response.asBytes32Unchecked(respIdx); // Response block_hash + function parseEthCallQueryResponse( + PerChainQueryResponse memory pcr + ) internal pure returns (EthCallQueryResponse memory ret) { unchecked { + if (pcr.queryType != QueryType.ETH_CALL) + revert WrongQueryType(pcr.queryType, QueryType.ETH_CALL); - (r.blockTime, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response block_time_us + uint reqOff; + uint respOff; - uint8 respNumResults; - (respNumResults, respIdx) = pcr.response.asUint8Unchecked(respIdx); // Response num_results - if (respNumResults != numBatchCallData) { - revert UnexpectedNumberOfResults(); - } + uint8 numBatchCallData; + (ret.requestBlockId, reqOff) = pcr.request.sliceUint32PrefixedUnchecked(reqOff); + (numBatchCallData, reqOff) = pcr.request.asUint8Unchecked(reqOff); - r.result = new EthCallData[](numBatchCallData); + uint8 respNumResults; + (ret.blockNum, respOff) = pcr.response.asUint64Unchecked(respOff); + (ret.blockHash, respOff) = pcr.response.asBytes32Unchecked(respOff); + (ret.blockTime, respOff) = pcr.response.asUint64Unchecked(respOff); + (respNumResults, respOff) = pcr.response.asUint8Unchecked(respOff); - // Walk through the call data and results in lock step. - for (uint idx; idx < numBatchCallData;) { - (r.result[idx].contractAddress, reqIdx) = pcr.request.asAddressUnchecked(reqIdx); + if (respNumResults != numBatchCallData) + revert UnexpectedNumberOfResults(); - (len, reqIdx) = pcr.request.asUint32Unchecked(reqIdx); // call_data_len - (r.result[idx].callData, reqIdx) = pcr.request.sliceUnchecked(reqIdx, len); + ret.results = new EthCallRecord[](numBatchCallData); - (len, respIdx) = pcr.response.asUint32Unchecked(respIdx); // result_len - (r.result[idx].result, respIdx) = pcr.response.sliceUnchecked(respIdx, len); + //Walk through the call inputs and outputs in lock step. + for (uint i; i < numBatchCallData; ++i) { + (ret.results[i].contractAddress, reqOff) = pcr.request.asAddressUnchecked(reqOff); + (ret.results[i].callData, reqOff) = pcr.request.sliceUint32PrefixedUnchecked(reqOff); - unchecked { ++idx; } - } - - checkLength(pcr.request, reqIdx); - checkLength(pcr.response, respIdx); + (ret.results[i].result, respOff) = pcr.response.sliceUint32PrefixedUnchecked(respOff); } - /// @dev parseSolanaAccountQueryResponse parses a ParsedPerChainQueryResponse for a Solana Account per-chain query. - function parseSolanaAccountQueryResponse(ParsedPerChainQueryResponse memory pcr) public pure returns (SolanaAccountQueryResponse memory r) { - if (pcr.queryType != QT_SOL_ACCOUNT) { - revert WrongQueryType(pcr.queryType, QT_SOL_ACCOUNT); - } - - uint reqIdx; - uint respIdx; - uint32 len; - - (len, reqIdx) = pcr.request.asUint32Unchecked(reqIdx); // Request commitment_len - (r.requestCommitment, reqIdx) = pcr.request.sliceUnchecked(reqIdx, len); // Request commitment - (r.requestMinContextSlot, reqIdx) = pcr.request.asUint64Unchecked(reqIdx); // Request min_context_slot - (r.requestDataSliceOffset, reqIdx) = pcr.request.asUint64Unchecked(reqIdx); // Request data_slice_offset - (r.requestDataSliceLength, reqIdx) = pcr.request.asUint64Unchecked(reqIdx); // Request data_slice_length - - uint8 numAccounts; - (numAccounts, reqIdx) = pcr.request.asUint8Unchecked(reqIdx); // Request num_accounts - - (r.slotNumber, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response slot_number - (r.blockTime, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response block_time_us - (r.blockHash, respIdx) = pcr.response.asBytes32Unchecked(respIdx); // Response block_hash - - uint8 respNumResults; - (respNumResults, respIdx) = pcr.response.asUint8Unchecked(respIdx); // Response num_results - if (respNumResults != numAccounts) { - revert UnexpectedNumberOfResults(); - } - - r.results = new SolanaAccountResult[](numAccounts); - - // Walk through the call data and results in lock step. - for (uint idx; idx < numAccounts;) { - (r.results[idx].account, reqIdx) = pcr.request.asBytes32Unchecked(reqIdx); // Request account - - (r.results[idx].lamports, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response lamports - (r.results[idx].rentEpoch, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response rent_epoch - - (r.results[idx].executable, respIdx) = pcr.response.asBoolUnchecked(respIdx); // Response executable - - (r.results[idx].owner, respIdx) = pcr.response.asBytes32Unchecked(respIdx); // Response owner - - - (len, respIdx) = pcr.response.asUint32Unchecked(respIdx); // result_len - (r.results[idx].data, respIdx) = pcr.response.sliceUnchecked(respIdx, len); - - unchecked { ++idx; } - } - - checkLength(pcr.request, reqIdx); - checkLength(pcr.response, respIdx); + checkLength(pcr.request, reqOff); + checkLength(pcr.response, respOff); + return ret; + }} + + function parseEthCallByTimestampQueryResponse( + PerChainQueryResponse memory pcr + ) internal pure returns (EthCallByTimestampQueryResponse memory ret) { unchecked { + if (pcr.queryType != QueryType.ETH_CALL_BY_TIMESTAMP) + revert WrongQueryType(pcr.queryType, QueryType.ETH_CALL_BY_TIMESTAMP); + + uint reqOff; + uint respOff; + + uint8 numBatchCallData; + (ret.requestTargetTimestamp, reqOff) = pcr.request.asUint64Unchecked(reqOff); + (ret.requestTargetBlockIdHint, reqOff) = pcr.request.sliceUint32PrefixedUnchecked(reqOff); + (ret.requestFollowingBlockIdHint, reqOff) = pcr.request.sliceUint32PrefixedUnchecked(reqOff); + (numBatchCallData, reqOff) = pcr.request.asUint8Unchecked(reqOff); + + uint8 respNumResults; + (ret.targetBlockNum, respOff) = pcr.response.asUint64Unchecked(respOff); + (ret.targetBlockHash, respOff) = pcr.response.asBytes32Unchecked(respOff); + (ret.targetBlockTime, respOff) = pcr.response.asUint64Unchecked(respOff); + (ret.followingBlockNum, respOff) = pcr.response.asUint64Unchecked(respOff); + (ret.followingBlockHash, respOff) = pcr.response.asBytes32Unchecked(respOff); + (ret.followingBlockTime, respOff) = pcr.response.asUint64Unchecked(respOff); + (respNumResults, respOff) = pcr.response.asUint8Unchecked(respOff); + + if (respNumResults != numBatchCallData) + revert UnexpectedNumberOfResults(); + + ret.results = new EthCallRecord[](numBatchCallData); + + // Walk through the call inputs and outputs in lock step. + for (uint i; i < numBatchCallData; ++i) { + (ret.results[i].contractAddress, reqOff) = pcr.request.asAddressUnchecked(reqOff); + (ret.results[i].callData, reqOff) = pcr.request.sliceUint32PrefixedUnchecked(reqOff); + + (ret.results[i].result, respOff) = pcr.response.sliceUint32PrefixedUnchecked(respOff); } - /// @dev parseSolanaPdaQueryResponse parses a ParsedPerChainQueryResponse for a Solana Pda per-chain query. - function parseSolanaPdaQueryResponse(ParsedPerChainQueryResponse memory pcr) public pure returns (SolanaPdaQueryResponse memory r) { - if (pcr.queryType != QT_SOL_PDA) { - revert WrongQueryType(pcr.queryType, QT_SOL_PDA); - } - - uint reqIdx; - uint respIdx; - uint32 len; - - (len, reqIdx) = pcr.request.asUint32Unchecked(reqIdx); // Request commitment_len - (r.requestCommitment, reqIdx) = pcr.request.sliceUnchecked(reqIdx, len); // Request commitment - (r.requestMinContextSlot, reqIdx) = pcr.request.asUint64Unchecked(reqIdx); // Request min_context_slot - (r.requestDataSliceOffset, reqIdx) = pcr.request.asUint64Unchecked(reqIdx); // Request data_slice_offset - (r.requestDataSliceLength, reqIdx) = pcr.request.asUint64Unchecked(reqIdx); // Request data_slice_length - - uint8 numPdas; - (numPdas, reqIdx) = pcr.request.asUint8Unchecked(reqIdx); // Request num_Pdas + checkLength(pcr.request, reqOff); + checkLength(pcr.response, respOff); + }} - (r.slotNumber, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response slot_number - (r.blockTime, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response block_time_us - (r.blockHash, respIdx) = pcr.response.asBytes32Unchecked(respIdx); // Response block_hash + function parseEthCallWithFinalityQueryResponse( + PerChainQueryResponse memory pcr + ) internal pure returns (EthCallWithFinalityQueryResponse memory ret) { unchecked { + if (pcr.queryType != QueryType.ETH_CALL_WITH_FINALITY) + revert WrongQueryType(pcr.queryType, QueryType.ETH_CALL_WITH_FINALITY); - uint8 respNumResults; - (respNumResults, respIdx) = pcr.response.asUint8Unchecked(respIdx); // Response num_results - if (respNumResults != numPdas) { - revert UnexpectedNumberOfResults(); - } + uint reqOff; + uint respOff; - r.results = new SolanaPdaResult[](numPdas); + uint8 numBatchCallData; + (ret.requestBlockId, reqOff) = pcr.request.sliceUint32PrefixedUnchecked(reqOff); + (ret.requestFinality, reqOff) = pcr.request.sliceUint32PrefixedUnchecked(reqOff); + (numBatchCallData, reqOff) = pcr.request.asUint8Unchecked(reqOff); - // Walk through the call data and results in lock step. - for (uint idx; idx < numPdas;) { - (r.results[idx].programId, reqIdx) = pcr.request.asBytes32Unchecked(reqIdx); // Request programId + uint8 respNumResults; + (ret.blockNum, respOff) = pcr.response.asUint64Unchecked(respOff); + (ret.blockHash, respOff) = pcr.response.asBytes32Unchecked(respOff); + (ret.blockTime, respOff) = pcr.response.asUint64Unchecked(respOff); + (respNumResults, respOff) = pcr.response.asUint8Unchecked(respOff); - uint8 numSeeds; // Request number of seeds - (numSeeds, reqIdx) = pcr.request.asUint8Unchecked(reqIdx); - r.results[idx].seeds = new bytes[](numSeeds); - for (uint idx2; idx2 < numSeeds;) { - uint32 seedLen; - (seedLen, reqIdx) = pcr.request.asUint32Unchecked(reqIdx); - (r.results[idx].seeds[idx2], reqIdx) = pcr.request.sliceUnchecked(reqIdx, seedLen); - unchecked { ++idx2; } - } + if (respNumResults != numBatchCallData) + revert UnexpectedNumberOfResults(); - (r.results[idx].account, respIdx) = pcr.response.asBytes32Unchecked(respIdx); // Response account - (r.results[idx].bump, respIdx) = pcr.response.asUint8Unchecked(respIdx); // Response bump + ret.results = new EthCallRecord[](numBatchCallData); - (r.results[idx].lamports, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response lamports - (r.results[idx].rentEpoch, respIdx) = pcr.response.asUint64Unchecked(respIdx); // Response rent_epoch + //Walk through the call inputs and outputs in lock step. + for (uint i; i < numBatchCallData; ++i) { + (ret.results[i].contractAddress, reqOff) = pcr.request.asAddressUnchecked(reqOff); + (ret.results[i].callData, reqOff) = pcr.request.sliceUint32PrefixedUnchecked(reqOff); - (r.results[idx].executable, respIdx) = pcr.response.asBoolUnchecked(respIdx); // Response executable - - (r.results[idx].owner, respIdx) = pcr.response.asBytes32Unchecked(respIdx); // Response owner - - (len, respIdx) = pcr.response.asUint32Unchecked(respIdx); // result_len - (r.results[idx].data, respIdx) = pcr.response.sliceUnchecked(respIdx, len); - - unchecked { ++idx; } - } - - checkLength(pcr.request, reqIdx); - checkLength(pcr.response, respIdx); + (ret.results[i].result, respOff) = pcr.response.sliceUint32PrefixedUnchecked(respOff); } - /// @dev validateBlockTime validates that the parsed block time isn't stale - /// @param _blockTime Wormhole block time in MICROseconds - /// @param _minBlockTime Minium block time in seconds - function validateBlockTime(uint64 _blockTime, uint256 _minBlockTime) public pure { - uint256 blockTimeInSeconds = _blockTime / 1_000_000; // Rounds down - - if (blockTimeInSeconds < _minBlockTime) { - revert StaleBlockTime(); - } + checkLength(pcr.request, reqOff); + checkLength(pcr.response, respOff); + }} + + function parseSolanaAccountQueryResponse( + PerChainQueryResponse memory pcr + ) internal pure returns (SolanaAccountQueryResponse memory ret) { unchecked { + if (pcr.queryType != QueryType.SOLANA_ACCOUNT) + revert WrongQueryType(pcr.queryType, QueryType.SOLANA_ACCOUNT); + + uint reqOff; + uint respOff; + + uint8 numAccounts; + (ret.requestCommitment, reqOff) = pcr.request.sliceUint32PrefixedUnchecked(reqOff); + (ret.requestMinContextSlot, reqOff) = pcr.request.asUint64Unchecked(reqOff); + (ret.requestDataSliceOffset, reqOff) = pcr.request.asUint64Unchecked(reqOff); + (ret.requestDataSliceLength, reqOff) = pcr.request.asUint64Unchecked(reqOff); + (numAccounts, reqOff) = pcr.request.asUint8Unchecked(reqOff); + + uint8 respNumResults; + (ret.slotNumber, respOff) = pcr.response.asUint64Unchecked(respOff); + (ret.blockTime, respOff) = pcr.response.asUint64Unchecked(respOff); + (ret.blockHash, respOff) = pcr.response.asBytes32Unchecked(respOff); + (respNumResults, respOff) = pcr.response.asUint8Unchecked(respOff); + + if (respNumResults != numAccounts) + revert UnexpectedNumberOfResults(); + + ret.results = new SolanaAccountResult[](numAccounts); + + //Walk through the call inputs and outputs in lock step. + for (uint i; i < numAccounts; ++i) { + (ret.results[i].account, reqOff) = pcr.request.asBytes32Unchecked(reqOff); + + (ret.results[i].lamports, respOff) = pcr.response.asUint64Unchecked(respOff); + (ret.results[i].rentEpoch, respOff) = pcr.response.asUint64Unchecked(respOff); + (ret.results[i].executable, respOff) = pcr.response.asBoolUnchecked(respOff); + (ret.results[i].owner, respOff) = pcr.response.asBytes32Unchecked(respOff); + (ret.results[i].data, respOff) = pcr.response.sliceUint32PrefixedUnchecked(respOff); } - /// @dev validateBlockNum validates that the parsed blockNum isn't stale - function validateBlockNum(uint64 _blockNum, uint256 _minBlockNum) public pure { - if (_blockNum < _minBlockNum) { - revert StaleBlockNum(); - } - } - - /// @dev validateChainId validates that the parsed chainId is one of an array of chainIds we expect - function validateChainId(uint16 chainId, uint16[] memory _validChainIds) public pure { - bool validChainId = false; - - uint256 numChainIds = _validChainIds.length; - - for (uint256 idx; idx < numChainIds;) { - if (chainId == _validChainIds[idx]) { - validChainId = true; - break; - } - - unchecked { ++idx; } - } - - if (!validChainId) revert InvalidChainId(); - } - - /// @dev validateMutlipleEthCallData validates that each EthCallData in an array comes from a function signature and contract address we expect - function validateMultipleEthCallData(EthCallData[] memory r, address[] memory _expectedContractAddresses, bytes4[] memory _expectedFunctionSignatures) public pure { - uint256 callDatasLength = r.length; - - for (uint256 idx; idx < callDatasLength;) { - validateEthCallData(r[idx], _expectedContractAddresses, _expectedFunctionSignatures); - - unchecked { ++idx; } - } + checkLength(pcr.request, reqOff); + checkLength(pcr.response, respOff); + }} + + function parseSolanaPdaQueryResponse( + PerChainQueryResponse memory pcr + ) internal pure returns (SolanaPdaQueryResponse memory ret) { unchecked { + if (pcr.queryType != QueryType.SOLANA_PDA) + revert WrongQueryType(pcr.queryType, QueryType.SOLANA_PDA); + + uint reqOff; + uint respOff; + + (ret.requestCommitment, reqOff) = pcr.request.sliceUint32PrefixedUnchecked(reqOff); + (ret.requestMinContextSlot, reqOff) = pcr.request.asUint64Unchecked(reqOff); + (ret.requestDataSliceOffset, reqOff) = pcr.request.asUint64Unchecked(reqOff); + (ret.requestDataSliceLength, reqOff) = pcr.request.asUint64Unchecked(reqOff); + + uint8 numPdas; + (numPdas, reqOff) = pcr.request.asUint8Unchecked(reqOff); + + (ret.slotNumber, respOff) = pcr.response.asUint64Unchecked(respOff); + (ret.blockTime, respOff) = pcr.response.asUint64Unchecked(respOff); + (ret.blockHash, respOff) = pcr.response.asBytes32Unchecked(respOff); + + uint8 respNumResults; + (respNumResults, respOff) = pcr.response.asUint8Unchecked(respOff); + if (respNumResults != numPdas) + revert UnexpectedNumberOfResults(); + + ret.results = new SolanaPdaResult[](numPdas); + + //Walk through the call inputs and outputs in lock step. + for (uint i; i < numPdas; ++i) { + (ret.results[i].programId, reqOff) = pcr.request.asBytes32Unchecked(reqOff); + + uint8 reqNumSeeds; + (reqNumSeeds, reqOff) = pcr.request.asUint8Unchecked(reqOff); + ret.results[i].seeds = new bytes[](reqNumSeeds); + for (uint s; s < reqNumSeeds; ++s) + (ret.results[i].seeds[s], reqOff) = pcr.request.sliceUint32PrefixedUnchecked(reqOff); + + (ret.results[i].account, respOff) = pcr.response.asBytes32Unchecked(respOff); + (ret.results[i].bump, respOff) = pcr.response.asUint8Unchecked(respOff); + (ret.results[i].lamports, respOff) = pcr.response.asUint64Unchecked(respOff); + (ret.results[i].rentEpoch, respOff) = pcr.response.asUint64Unchecked(respOff); + (ret.results[i].executable, respOff) = pcr.response.asBoolUnchecked(respOff); + (ret.results[i].owner, respOff) = pcr.response.asBytes32Unchecked(respOff); + (ret.results[i].data, respOff) = pcr.response.sliceUint32PrefixedUnchecked(respOff); } - /// @dev validateEthCallData validates that EthCallData comes from a function signature and contract address we expect - /// @dev An empty array means we accept all addresses/function signatures - /// @dev Example 1: To accept signatures 0xaaaaaaaa and 0xbbbbbbbb from `address(abcd)` you'd pass in [0xaaaaaaaa, 0xbbbbbbbb], [address(abcd)] - /// @dev Example 2: To accept any function signatures from `address(abcd)` or `address(efab)` you'd pass in [], [address(abcd), address(efab)] - /// @dev Example 3: To accept function signature 0xaaaaaaaa from any address you'd pass in [0xaaaaaaaa], [] - /// @dev WARNING Example 4: If you want to accept signature 0xaaaaaaaa from `address(abcd)` and signature 0xbbbbbbbb from `address(efab)` the following input would be incorrect: - /// @dev [0xaaaaaaaa, 0xbbbbbbbb], [address(abcd), address(efab)] - /// @dev This would accept both 0xaaaaaaaa and 0xbbbbbbbb from `address(abcd)` AND `address(efab)`. Instead you should make 2 calls to this method - /// @dev using the pattern in Example 1. [0xaaaaaaaa], [address(abcd)] OR [0xbbbbbbbb], [address(efab)] - function validateEthCallData(EthCallData memory r, address[] memory _expectedContractAddresses, bytes4[] memory _expectedFunctionSignatures) public pure { - bool validContractAddress = _expectedContractAddresses.length == 0 ? true : false; - bool validFunctionSignature = _expectedFunctionSignatures.length == 0 ? true : false; - - uint256 contractAddressesLength = _expectedContractAddresses.length; - - // Check that the contract address called in the request is expected - for (uint256 idx; idx < contractAddressesLength;) { - if (r.contractAddress == _expectedContractAddresses[idx]) { - validContractAddress = true; - break; - } - - unchecked { ++idx; } - } - - // Early exit to save gas - if (!validContractAddress) { - revert InvalidContractAddress(); - } - - uint256 functionSignaturesLength = _expectedFunctionSignatures.length; - - // Check that the function signature called is expected - for (uint256 idx; idx < functionSignaturesLength;) { - (bytes4 funcSig,) = r.callData.asBytes4Unchecked(0); - if (funcSig == _expectedFunctionSignatures[idx]) { - validFunctionSignature = true; - break; - } - - unchecked { ++idx; } - } - - if (!validFunctionSignature) { - revert InvalidFunctionSignature(); - } - } - - /** - * @dev verifyQueryResponseSignatures verifies the signatures on a query response. It calls into the Wormhole contract. - * IWormhole.Signature expects the last byte to be bumped by 27 - * see https://github.com/wormhole-foundation/wormhole/blob/637b1ee657de7de05f783cbb2078dd7d8bfda4d0/ethereum/contracts/Messages.sol#L174 - */ - function verifyQueryResponseSignatures(bytes memory response, IWormhole.Signature[] memory signatures) public view { - // It might be worth adding a verifyCurrentQuorum call on the core bridge so that there is only 1 cross call instead of 4. - uint32 gsi = wormhole.getCurrentGuardianSetIndex(); - IWormhole.GuardianSet memory guardianSet = wormhole.getGuardianSet(gsi); - - bytes32 responseHash = getResponseDigest(response); - - /** - * @dev Checks whether the guardianSet has zero keys - * WARNING: This keys check is critical to ensure the guardianSet has keys present AND to ensure - * that guardianSet key size doesn't fall to zero and negatively impact quorum assessment. If guardianSet - * key length is 0 and vm.signatures length is 0, this could compromise the integrity of both vm and - * signature verification. - */ - if(guardianSet.keys.length == 0){ - revert("invalid guardian set"); - } - - /** - * @dev We're using a fixed point number transformation with 1 decimal to deal with rounding. - * WARNING: This quorum check is critical to assessing whether we have enough Guardian signatures to validate a VM - * if making any changes to this, obtain additional peer review. If guardianSet key length is 0 and - * vm.signatures length is 0, this could compromise the integrity of both vm and signature verification. - */ - if (signatures.length < wormhole.quorum(guardianSet.keys.length)){ - revert("no quorum"); - } - - /// @dev Verify the proposed vm.signatures against the guardianSet - (bool signaturesValid, string memory invalidReason) = wormhole.verifySignatures(responseHash, signatures, guardianSet); - if(!signaturesValid){ - revert(invalidReason); - } - - /// If we are here, we've validated the VM is a valid multi-sig that matches the current guardianSet. - } - - /// @dev checkLength verifies that the message was fully consumed. - function checkLength(bytes memory encoded, uint256 expected) private pure { - if (encoded.length != expected) { - revert InvalidPayloadLength(encoded.length, expected); - } + checkLength(pcr.request, reqOff); + checkLength(pcr.response, respOff); + }} + + function validateBlockTime( + uint64 blockTimeInMicroSeconds, + uint256 minBlockTimeInSeconds + ) internal pure { + uint256 blockTimeInSeconds = blockTimeInMicroSeconds / MICROSECONDS_PER_SECOND; // Rounds down + + if (blockTimeInSeconds < minBlockTimeInSeconds) + revert StaleBlockTime(); + } + + function validateBlockNum(uint64 blockNum, uint256 minBlockNum) internal pure { + if (blockNum < minBlockNum) + revert StaleBlockNum(); + } + + function validateChainId( + uint16 chainId, + uint16[] memory validChainIds + ) internal pure { unchecked { + uint len = validChainIds.length; + for (uint i; i < len; ++i) + if (chainId == validChainIds[i]) + return; + + revert InvalidChainId(); + }} + + function validateEthCallRecord( + EthCallRecord[] memory ecrs, + address[] memory validContractAddresses, + bytes4[] memory validFunctionSignatures + ) internal pure { unchecked { + uint len = ecrs.length; + for (uint i; i < len; ++i) + validateEthCallRecord(ecrs[i], validContractAddresses, validFunctionSignatures); + }} + + //validates that EthCallRecord a valid function signature and contract address + //An empty array means we accept all addresses/function signatures + // Example 1: To accept signatures 0xaaaaaaaa and 0xbbbbbbbb from `address(abcd)` + // you'd pass in [0xaaaaaaaa, 0xbbbbbbbb], [address(abcd)] + // Example 2: To accept any function signatures from `address(abcd)` or `address(efab)` + // you'd pass in [], [address(abcd), address(efab)] + // Example 3: To accept function signature 0xaaaaaaaa from any address + // you'd pass in [0xaaaaaaaa], [] + // + // WARNING Example 4: If you want to accept signature 0xaaaaaaaa from `address(abcd)` + // and signature 0xbbbbbbbb from `address(efab)` the following input would be incorrect: + // [0xaaaaaaaa, 0xbbbbbbbb], [address(abcd), address(efab)] + // This would accept both 0xaaaaaaaa and 0xbbbbbbbb from `address(abcd)` AND `address(efab)`. + // Instead you should make 2 calls to this method using the pattern in Example 1. + // [0xaaaaaaaa], [address(abcd)] OR [0xbbbbbbbb], [address(efab)] + function validateEthCallRecord( + EthCallRecord memory ecd, + address[] memory validContractAddresses, //empty array means accept all + bytes4[] memory validFunctionSignatures //empty array means accept all + ) internal pure { unchecked { + if (validContractAddresses.length > 0) + validateContractAddress(ecd.contractAddress, validContractAddresses); + + if (validFunctionSignatures.length > 0) { + (bytes4 funcSig,) = ecd.callData.asBytes4(0); + validateFunctionSignature(funcSig, validFunctionSignatures); } + }} + + function validateContractAddress( + address contractAddress, + address[] memory validContractAddresses + ) private pure { unchecked { + uint len = validContractAddresses.length; + for (uint i; i < len; ++i) + if (contractAddress == validContractAddresses[i]) + return; + + revert InvalidContractAddress(); + }} + + function validateFunctionSignature( + bytes4 functionSignature, + bytes4[] memory validFunctionSignatures + ) private pure { unchecked { + uint len = validFunctionSignatures.length; + for (uint i; i < len; ++i) + if (functionSignature == validFunctionSignatures[i]) + return; + + revert InvalidFunctionSignature(); + }} + + //we use this over BytesParsing.checkLength to return our custom errors in all error cases + function checkLength(bytes memory encoded, uint256 expected) private pure { + if (encoded.length != expected) + revert InvalidPayloadLength(encoded.length, expected); + } } diff --git a/src/testing/helpers/QueryTest.sol b/src/testing/helpers/QueryTest.sol index 7005d31..204dc67 100644 --- a/src/testing/helpers/QueryTest.sol +++ b/src/testing/helpers/QueryTest.sol @@ -2,351 +2,270 @@ pragma solidity ^0.8.4; -// @dev QueryTest is a library to build Cross Chain Query (CCQ) responses for testing purposes. +//build Cross Chain Query (CCQ) responses for testing purposes. library QueryTest { - // Custom errors - error SolanaTooManyPDAs(); - error SolanaTooManySeeds(); - error SolanaSeedTooLong(); - - // - // Query Request stuff - // - - /// @dev buildOffChainQueryRequestBytes builds an off chain query request from the specified fields. - function buildOffChainQueryRequestBytes( - uint8 _version, - uint32 _nonce, - uint8 _numPerChainQueries, - bytes memory _perChainQueries - ) internal pure returns (bytes memory){ - return abi.encodePacked( - _version, - _nonce, - _numPerChainQueries, - _perChainQueries // Each created by buildPerChainRequestBytes() - ); - } + error SolanaTooManyPDAs(); + error SolanaTooManySeeds(); + error SolanaSeedTooLong(); - /// @dev buildPerChainRequestBytes builds a per chain request from the specified fields. - function buildPerChainRequestBytes( - uint16 _chainId, - uint8 _queryType, - bytes memory _queryBytes - ) internal pure returns (bytes memory){ - return abi.encodePacked( - _chainId, - _queryType, - uint32(_queryBytes.length), - _queryBytes - ); - } + //According to the spec, there may be at most 16 seeds. + // https://github.com/gagliardetto/solana-go/blob/6fe3aea02e3660d620433444df033fc3fe6e64c1/keys.go#L559 + uint public constant SOLANA_MAX_SEEDS = 16; - /// @dev buildEthCallRequestBytes builds an eth_call query request from the specified fields. - function buildEthCallRequestBytes( - bytes memory _blockId, - uint8 _numCallData, - bytes memory _callData // Created with buildEthCallDataBytes() - ) internal pure returns (bytes memory){ - return abi.encodePacked( - uint32(_blockId.length), - _blockId, - _numCallData, - _callData - ); - } + //According to the spec, a seed may be at most 32 bytes. + // https://github.com/gagliardetto/solana-go/blob/6fe3aea02e3660d620433444df033fc3fe6e64c1/keys.go#L557 + uint public constant SOLANA_MAX_SEED_LEN = 32; - /// @dev buildEthCallByTimestampRequestBytes builds an eth_call_by_timestamp query request from the specified fields. - function buildEthCallByTimestampRequestBytes( - uint64 _targetTimeUs, - bytes memory _targetBlockHint, - bytes memory _followingBlockHint, - uint8 _numCallData, - bytes memory _callData // Created with buildEthCallDataBytes() - ) internal pure returns (bytes memory){ - return abi.encodePacked( - _targetTimeUs, - uint32(_targetBlockHint.length), - _targetBlockHint, - uint32(_followingBlockHint.length), - _followingBlockHint, - _numCallData, - _callData - ); - } + // --- Query Request --- - /// @dev buildEthCallWithFinalityRequestBytes builds an eth_call_with_finality query request from the specified fields. - function buildEthCallWithFinalityRequestBytes( - bytes memory _blockId, - bytes memory _finality, - uint8 _numCallData, - bytes memory _callData // Created with buildEthCallDataBytes() - ) internal pure returns (bytes memory){ - return abi.encodePacked( - uint32(_blockId.length), - _blockId, - uint32(_finality.length), - _finality, - _numCallData, - _callData - ); - } + function buildOffChainQueryRequestBytes( + uint8 version, + uint32 nonce, + uint8 numPerChainQueries, + bytes memory perChainQueries + ) internal pure returns (bytes memory){ + return abi.encodePacked( + version, + nonce, + numPerChainQueries, + perChainQueries // Each created by buildPerChainRequestBytes() + ); + } - /// @dev buildEthCallDataBytes builds the call data associated with one of the eth_call family of queries. - function buildEthCallDataBytes( - address _contractAddress, - bytes memory _callData - ) internal pure returns (bytes memory){ - return abi.encodePacked( - _contractAddress, - uint32(_callData.length), - _callData - ); - } - - /// @dev buildSolanaAccountRequestBytes builds a sol_account query request from the specified fields. - function buildSolanaAccountRequestBytes( - bytes memory _commitment, - uint64 _minContextSlot, - uint64 _dataSliceOffset, - uint64 _dataSliceLength, - uint8 _numAccounts, - bytes memory _accounts // Each account is 32 bytes. - ) internal pure returns (bytes memory){ - return abi.encodePacked( - uint32(_commitment.length), - _commitment, - _minContextSlot, - _dataSliceOffset, - _dataSliceLength, - _numAccounts, - _accounts - ); - } - - /// @dev buildSolanaPdaRequestBytes builds a sol_pda query request from the specified fields. - function buildSolanaPdaRequestBytes( - bytes memory _commitment, - uint64 _minContextSlot, - uint64 _dataSliceOffset, - uint64 _dataSliceLength, - bytes[] memory _pdas // Created with multiple calls to buildSolanaPdaEntry() - ) internal pure returns (bytes memory){ - uint numPdas = _pdas.length; - if (numPdas > 255) { - revert SolanaTooManyPDAs(); - } - bytes memory result = abi.encodePacked( - uint32(_commitment.length), - _commitment, - _minContextSlot, - _dataSliceOffset, - _dataSliceLength, - uint8(numPdas) - ); - - for (uint idx; idx < numPdas;) { - result = abi.encodePacked( - result, - _pdas[idx] - ); - - unchecked { ++idx; } - } - - return result; - } + function buildPerChainRequestBytes( + uint16 chainId, + uint8 queryType, + bytes memory queryBytes + ) internal pure returns (bytes memory){ + return abi.encodePacked(chainId, queryType, uint32(queryBytes.length), queryBytes); + } - /// @dev buildSolanaPdaEntry builds a PDA entry for a sol_pda query. - function buildSolanaPdaEntry( - bytes32 _programId, - uint8 _numSeeds, - bytes memory _seeds // Created with buildSolanaPdaSeedBytes() - ) internal pure returns (bytes memory){ - if (_numSeeds > SolanaMaxSeeds) { - revert SolanaTooManySeeds(); - } - return abi.encodePacked( - _programId, - _numSeeds, - _seeds - ); - } + function buildEthCallRequestBytes( + bytes memory blockId, + uint8 numCallData, + bytes memory callData // Created with buildEthCallRecordBytes() + ) internal pure returns (bytes memory){ + return abi.encodePacked(uint32(blockId.length), blockId, numCallData, callData); + } - // According to the spec, there may be at most 16 seeds. - // https://github.com/gagliardetto/solana-go/blob/6fe3aea02e3660d620433444df033fc3fe6e64c1/keys.go#L559 - uint public constant SolanaMaxSeeds = 16; - - // According to the spec, a seed may be at most 32 bytes. - // https://github.com/gagliardetto/solana-go/blob/6fe3aea02e3660d620433444df033fc3fe6e64c1/keys.go#L557 - uint public constant SolanaMaxSeedLen = 32; - - /// @dev buildSolanaPdaSeedBytes packs the seeds for a PDA entry into an array of bytes. - function buildSolanaPdaSeedBytes( - bytes[] memory _seeds - ) internal pure returns (bytes memory, uint8){ - uint numSeeds = _seeds.length; - if (numSeeds > SolanaMaxSeeds) { - revert SolanaTooManySeeds(); - } - - bytes memory result; - - for (uint idx; idx < numSeeds;) { - uint seedLen = _seeds[idx].length; - if (seedLen > SolanaMaxSeedLen) { - revert SolanaSeedTooLong(); - } - result = abi.encodePacked( - result, - abi.encodePacked( - uint32(seedLen), - _seeds[idx] - ) - ); - - unchecked { ++idx; } - } - - return (result, uint8(numSeeds)); - } + function buildEthCallByTimestampRequestBytes( + uint64 targetTimeUs, + bytes memory targetBlockHint, + bytes memory followingBlockHint, + uint8 numCallData, + bytes memory callData // Created with buildEthCallRecordBytes() + ) internal pure returns (bytes memory){ + return abi.encodePacked( + targetTimeUs, + uint32(targetBlockHint.length), + targetBlockHint, + uint32(followingBlockHint.length), + followingBlockHint, + numCallData, + callData + ); + } - // - // Query Response stuff - // - - /// @dev buildQueryResponseBytes builds a query response from the specified fields. - function buildQueryResponseBytes( - uint8 _version, - uint16 _senderChainId, - bytes memory _signature, - bytes memory _queryRequest, - uint8 _numPerChainResponses, - bytes memory _perChainResponses - ) internal pure returns (bytes memory){ - return abi.encodePacked( - _version, - _senderChainId, - _signature, - uint32(_queryRequest.length), - _queryRequest, - _numPerChainResponses, - _perChainResponses // Each created by buildPerChainResponseBytes() - ); - } + function buildEthCallWithFinalityRequestBytes( + bytes memory blockId, + bytes memory finality, + uint8 numCallData, + bytes memory callData // Created with buildEthCallRecordBytes() + ) internal pure returns (bytes memory){ + return abi.encodePacked( + uint32(blockId.length), + blockId, + uint32(finality.length), + finality, + numCallData, + callData + ); + } - /// @dev buildPerChainResponseBytes builds a per chain response from the specified fields. - function buildPerChainResponseBytes( - uint16 _chainId, - uint8 _queryType, - bytes memory _responseBytes - ) internal pure returns (bytes memory){ - return abi.encodePacked( - _chainId, - _queryType, - uint32(_responseBytes.length), - _responseBytes - ); - } + function buildEthCallRecordBytes( + address contractAddress, + bytes memory callData + ) internal pure returns (bytes memory){ + return abi.encodePacked(contractAddress, uint32(callData.length), callData); + } - /// @dev buildEthCallResponseBytes builds an eth_call response from the specified fields. - function buildEthCallResponseBytes( - uint64 _blockNumber, - bytes32 _blockHash, - uint64 _blockTimeUs, - uint8 _numResults, - bytes memory _results // Created with buildEthCallResultBytes() - ) internal pure returns (bytes memory){ - return abi.encodePacked( - _blockNumber, - _blockHash, - _blockTimeUs, - _numResults, - _results - ); - } + function buildSolanaAccountRequestBytes( + bytes memory commitment, + uint64 minContextSlot, + uint64 dataSliceOffset, + uint64 dataSliceLength, + uint8 numAccounts, + bytes memory accounts // Each account is 32 bytes. + ) internal pure returns (bytes memory){ + return abi.encodePacked( + uint32(commitment.length), + commitment, + minContextSlot, + dataSliceOffset, + dataSliceLength, + numAccounts, + accounts + ); + } + + function buildSolanaPdaRequestBytes( + bytes memory commitment, + uint64 minContextSlot, + uint64 dataSliceOffset, + uint64 dataSliceLength, + bytes[] memory pdas // Created with multiple calls to buildSolanaPdaEntry() + ) internal pure returns (bytes memory){ + uint numPdas = pdas.length; + if (numPdas > type(uint8).max) + revert SolanaTooManyPDAs(); + + bytes memory result = abi.encodePacked( + uint32(commitment.length), + commitment, + minContextSlot, + dataSliceOffset, + dataSliceLength, + uint8(numPdas) + ); + + for (uint idx; idx < numPdas;) { + result = abi.encodePacked(result, pdas[idx]); - /// @dev buildEthCallByTimestampResponseBytes builds an eth_call_by_timestamp response from the specified fields. - function buildEthCallByTimestampResponseBytes( - uint64 _targetBlockNumber, - bytes32 _targetBlockHash, - uint64 _targetBlockTimeUs, - uint64 _followingBlockNumber, - bytes32 _followingBlockHash, - uint64 _followingBlockTimeUs, - uint8 _numResults, - bytes memory _results // Created with buildEthCallResultBytes() - ) internal pure returns (bytes memory){ - return abi.encodePacked( - _targetBlockNumber, - _targetBlockHash, - _targetBlockTimeUs, - _followingBlockNumber, - _followingBlockHash, - _followingBlockTimeUs, - _numResults, - _results - ); + unchecked { ++idx; } } - /// @dev buildEthCallWithFinalityResponseBytes builds an eth_call_with_finality response from the specified fields. Note that it is currently the same as buildEthCallResponseBytes. - function buildEthCallWithFinalityResponseBytes( - uint64 _blockNumber, - bytes32 _blockHash, - uint64 _blockTimeUs, - uint8 _numResults, - bytes memory _results // Created with buildEthCallResultBytes() - ) internal pure returns (bytes memory){ - return abi.encodePacked( - _blockNumber, - _blockHash, - _blockTimeUs, - _numResults, - _results - ); - } - - /// @dev buildEthCallResultBytes builds an eth_call result from the specified fields. - function buildEthCallResultBytes( - bytes memory _result - ) internal pure returns (bytes memory){ - return abi.encodePacked( - uint32(_result.length), - _result - ); + return result; + } + + function buildSolanaPdaEntry( + bytes32 programId, + uint8 numSeeds, + bytes memory seeds // Created with buildSolanaPdaSeedBytes() + ) internal pure returns (bytes memory){ + if (numSeeds > SOLANA_MAX_SEEDS) + revert SolanaTooManySeeds(); + + return abi.encodePacked(programId, numSeeds, seeds); + } + + //packs the seeds for a PDA entry into an array of bytes. + function buildSolanaPdaSeedBytes( + bytes[] memory seeds + ) internal pure returns (bytes memory, uint8){ + uint numSeeds = seeds.length; + if (numSeeds > SOLANA_MAX_SEEDS) + revert SolanaTooManySeeds(); + + bytes memory result; + + for (uint idx; idx < numSeeds;) { + uint seedLen = seeds[idx].length; + if (seedLen > SOLANA_MAX_SEED_LEN) + revert SolanaSeedTooLong(); + + result = abi.encodePacked(result, abi.encodePacked(uint32(seedLen), seeds[idx])); + + unchecked { ++idx; } } - /// @dev buildSolanaAccountResponseBytes builds a sol_account response from the specified fields. - function buildSolanaAccountResponseBytes( - uint64 _slotNumber, - uint64 _blockTimeUs, - bytes32 _blockHash, - uint8 _numResults, - bytes memory _results // Created with buildEthCallResultBytes() - ) internal pure returns (bytes memory){ - return abi.encodePacked( - _slotNumber, - _blockTimeUs, - _blockHash, - _numResults, - _results - ); - } - - /// @dev buildSolanaPdaResponseBytes builds a sol_pda response from the specified fields. - function buildSolanaPdaResponseBytes( - uint64 _slotNumber, - uint64 _blockTimeUs, - bytes32 _blockHash, - uint8 _numResults, - bytes memory _results // Created with buildEthCallResultBytes() - ) internal pure returns (bytes memory){ - return abi.encodePacked( - _slotNumber, - _blockTimeUs, - _blockHash, - _numResults, - _results - ); - } + return (result, uint8(numSeeds)); + } + + // --- Query Response --- + + function buildQueryResponseBytes( + uint8 version, + uint16 senderChainId, + bytes memory signature, + bytes memory queryRequest, + uint8 numPerChainResponses, + bytes memory perChainResponses + ) internal pure returns (bytes memory){ + return abi.encodePacked( + version, + senderChainId, + signature, + uint32(queryRequest.length), + queryRequest, + numPerChainResponses, + perChainResponses // Each created by buildPerChainResponseBytes() + ); + } + + function buildPerChainResponseBytes( + uint16 chainId, + uint8 queryType, + bytes memory responseBytes + ) internal pure returns (bytes memory){ + return abi.encodePacked(chainId, queryType, uint32(responseBytes.length), responseBytes); + } + + function buildEthCallResponseBytes( + uint64 blockNumber, + bytes32 blockHash, + uint64 blockTimeUs, + uint8 numResults, + bytes memory results // Created with buildEthCallResultBytes() + ) internal pure returns (bytes memory){ + return abi.encodePacked(blockNumber, blockHash, blockTimeUs, numResults, results); + } + + function buildEthCallByTimestampResponseBytes( + uint64 targetBlockNumber, + bytes32 targetBlockHash, + uint64 targetBlockTimeUs, + uint64 followingBlockNumber, + bytes32 followingBlockHash, + uint64 followingBlockTimeUs, + uint8 numResults, + bytes memory results // Created with buildEthCallResultBytes() + ) internal pure returns (bytes memory){ + return abi.encodePacked( + targetBlockNumber, + targetBlockHash, + targetBlockTimeUs, + followingBlockNumber, + followingBlockHash, + followingBlockTimeUs, + numResults, + results + ); + } + + //Note this it is currently the same as buildEthCallResponseBytes. + function buildEthCallWithFinalityResponseBytes( + uint64 blockNumber, + bytes32 blockHash, + uint64 blockTimeUs, + uint8 numResults, + bytes memory results // Created with buildEthCallResultBytes() + ) internal pure returns (bytes memory){ + return abi.encodePacked(blockNumber, blockHash, blockTimeUs, numResults, results); + } + + function buildEthCallResultBytes( + bytes memory result + ) internal pure returns (bytes memory){ + return abi.encodePacked(uint32(result.length), result); + } + + function buildSolanaAccountResponseBytes( + uint64 slotNumber, + uint64 blockTimeUs, + bytes32 blockHash, + uint8 numResults, + bytes memory results // Created with buildEthCallResultBytes() + ) internal pure returns (bytes memory){ + return abi.encodePacked(slotNumber, blockTimeUs, blockHash, numResults, results); + } + + function buildSolanaPdaResponseBytes( + uint64 slotNumber, + uint64 blockTimeUs, + bytes32 blockHash, + uint8 numResults, + bytes memory results // Created with buildEthCallResultBytes() + ) internal pure returns (bytes memory){ + return abi.encodePacked(slotNumber, blockTimeUs, blockHash, numResults, results); + } } diff --git a/test/QueryResponse.t.sol b/test/QueryResponse.t.sol index 6c92977..a79b8c3 100644 --- a/test/QueryResponse.t.sol +++ b/test/QueryResponse.t.sol @@ -9,813 +9,849 @@ import "forge-std/Test.sol"; import "../src/testing/helpers/QueryTest.sol"; import {WormholeMock} from "../src/testing/helpers/WormholeMock.sol"; - -// @dev A non-abstract QueryResponse contract -contract QueryResponseContract is QueryResponse { - constructor(address _wormhole) QueryResponse(_wormhole) {} +//wrap library to allow testing via vm.expectRevert +contract QueryResponseLibWrapper { + function calcPrefixedResponseHash(bytes memory response) external pure returns (bytes32) { + return QueryResponseLib.calcPrefixedResponseHash(response); + } + + function parseAndVerifyQueryResponse( + address wormhole, + bytes memory response, + IWormhole.Signature[] memory signatures + ) external view returns (QueryResponse memory ret) { + return QueryResponseLib.parseAndVerifyQueryResponse(wormhole, response, signatures); + } + + function verifyQueryResponse( + address wormhole, + bytes memory response, + IWormhole.Signature[] memory signatures + ) external view { + return QueryResponseLib.verifyQueryResponse(wormhole, response, signatures); + } + + function parseQueryResponse( + bytes memory response + ) external pure returns (QueryResponse memory ret) { + return QueryResponseLib.parseQueryResponse(response); + } + + function parseEthCallQueryResponse( + PerChainQueryResponse memory pcr + ) external pure returns (EthCallQueryResponse memory ret) { + return QueryResponseLib.parseEthCallQueryResponse(pcr); + } + + function parseEthCallByTimestampQueryResponse( + PerChainQueryResponse memory pcr + ) external pure returns (EthCallByTimestampQueryResponse memory ret) { + return QueryResponseLib.parseEthCallByTimestampQueryResponse(pcr); + } + + function parseEthCallWithFinalityQueryResponse( + PerChainQueryResponse memory pcr + ) external pure returns (EthCallWithFinalityQueryResponse memory ret) { + return QueryResponseLib.parseEthCallWithFinalityQueryResponse(pcr); + } + + function parseSolanaAccountQueryResponse( + PerChainQueryResponse memory pcr + ) external pure returns (SolanaAccountQueryResponse memory ret) { + return QueryResponseLib.parseSolanaAccountQueryResponse(pcr); + } + + function parseSolanaPdaQueryResponse( + PerChainQueryResponse memory pcr + ) external pure returns (SolanaPdaQueryResponse memory ret) { + return QueryResponseLib.parseSolanaPdaQueryResponse(pcr); + } + + function validateBlockTime( + uint64 blockTimeInMicroSeconds, + uint256 minBlockTimeInSeconds + ) external pure { + QueryResponseLib.validateBlockTime(blockTimeInMicroSeconds, minBlockTimeInSeconds); + } + + function validateBlockNum(uint64 blockNum, uint256 minBlockNum) external pure { + QueryResponseLib.validateBlockNum(blockNum, minBlockNum); + } + + function validateChainId( + uint16 chainId, + uint16[] memory validChainIds + ) external pure { + QueryResponseLib.validateChainId(chainId, validChainIds); + } + + function validateEthCallRecord( + EthCallRecord[] memory ecds, + address[] memory validContractAddresses, + bytes4[] memory validFunctionSignatures + ) external pure { + QueryResponseLib.validateEthCallRecord(ecds, validContractAddresses, validFunctionSignatures); + } + + function validateEthCallRecord( + EthCallRecord memory ecd, + address[] memory validContractAddresses, //empty array means accept all + bytes4[] memory validFunctionSignatures //empty array means accept all + ) external pure { + QueryResponseLib.validateEthCallRecord(ecd, validContractAddresses, validFunctionSignatures); + } } contract TestQueryResponse is Test { - // Some happy case defaults - uint8 version = 0x01; - uint16 senderChainId = 0x0000; - bytes signature = hex"ff0c222dc9e3655ec38e212e9792bf1860356d1277462b6bf747db865caca6fc08e6317b64ee3245264e371146b1d315d38c867fe1f69614368dc4430bb560f200"; - uint32 queryRequestLen = 0x00000053; - uint8 queryRequestVersion = 0x01; - uint32 queryRequestNonce = 0xdd9914c6; - uint8 numPerChainQueries = 0x01; - bytes perChainQueries = hex"0005010000004600000009307832613631616334020d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000406fdde030d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000418160ddd"; - bytes perChainQueriesInner = hex"00000009307832613631616334020d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000406fdde030d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000418160ddd"; - uint8 numPerChainResponses = 0x01; - bytes perChainResponses = hex"000501000000b90000000002a61ac4c1adff9f6e180309e7d0d94c063338ddc61c1c4474cd6957c960efe659534d040005ff312e4f90c002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d57726170706564204d6174696300000000000000000000000000000000000000000000200000000000000000000000000000000000000000007ae5649beabeddf889364a"; - bytes perChainResponsesInner = hex"00000009307832613631616334020d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000406fdde030d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000418160ddd"; - - bytes solanaAccountSignature = hex"acb1d93cdfe60f9776e3e05d7fafaf9d83a1d14db70317230f6b0b6f3a60708a1a64dddac02d3843f4c516f2509b89454a2e73c360fea47beee1c1a091ff9f3201"; - uint32 solanaAccountQueryRequestLen = 0x00000073; - uint8 solanaAccountQueryRequestVersion = 0x01; - uint32 solanaAccountQueryRequestNonce = 0x0000002a; - uint8 solanaAccountNumPerChainQueries = 0x01; - bytes solanaAccountPerChainQueries = hex"000104000000660000000966696e616c697a656400000000000000000000000000000000000000000000000002165809739240a0ac03b98440fe8985548e3aa683cd0d4d9df5b5659669faa3019c006c48c8cbf33849cb07a3f936159cc523f9591cb1999abd45890ec5fee9b7"; - bytes solanaAccountPerChainQueriesInner = hex"0000000966696e616c697a656400000000000000000000000000000000000000000000000002165809739240a0ac03b98440fe8985548e3aa683cd0d4d9df5b5659669faa3019c006c48c8cbf33849cb07a3f936159cc523f9591cb1999abd45890ec5fee9b7"; - uint8 solanaAccountNumPerChainResponses = 0x01; - bytes solanaAccountPerChainResponses = hex"010001040000013f000000000000d85f00060f3e9915ddc03a8de2b1de609020bb0a0dcee594a8c06801619cf9ea2a498b9d910f9a25772b020000000000164d6000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90000005201000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d0000e8890423c78a09010000000000000000000000000000000000000000000000000000000000000000000000000000000000164d6000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90000005201000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d01000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000"; - bytes solanaAccountPerChainResponsesInner = hex"000000000000d85f00060f3e9915ddc03a8de2b1de609020bb0a0dcee594a8c06801619cf9ea2a498b9d910f9a25772b020000000000164d6000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90000005201000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d0000e8890423c78a09010000000000000000000000000000000000000000000000000000000000000000000000000000000000164d6000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90000005201000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d01000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000"; - - bytes solanaPdaSignature = hex"0c8418d81c00aad6283ba3eb30e141ccdd9296e013ca44e5cc713418921253004b93107ba0d858a548ce989e2bca4132e4c2f9a57a9892e3a87a8304cdb36d8f00"; - uint32 solanaPdaQueryRequestLen = 0x0000006b; - uint8 solanaPdaQueryRequestVersion = 0x01; - uint32 solanaPdaQueryRequestNonce = 0x0000002b; - uint8 solanaPdaNumPerChainQueries = 0x01; - bytes solanaPdaPerChainQueries = hex"010001050000005e0000000966696e616c697a656400000000000008ff000000000000000c00000000000000140102c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa020000000b477561726469616e5365740000000400000000"; - bytes solanaPdaPerChainQueriesInner = hex"0000000966696e616c697a656400000000000008ff000000000000000c00000000000000140102c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa020000000b477561726469616e5365740000000400000000"; - uint8 solanaPdaNumPerChainResponses = 0x01; - bytes solanaPdaPerChainResponses = hex"0001050000009b00000000000008ff0006115e3f6d7540e05035785e15056a8559815e71343ce31db2abf23f65b19c982b68aee7bf207b014fa9188b339cfd573a0778c5deaeeee94d4bcfb12b345bf8e417e5119dae773efd0000000000116ac000000000000000000002c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa0000001457cd18b7f8a4d91a2da9ab4af05d0fbece2dcd65"; - bytes solanaPdaPerChainResponsesInner = hex"00000000000008ff0006115e3f6d7540e05035785e15056a8559815e71343ce31db2abf23f65b19c982b68aee7bf207b014fa9188b339cfd573a0778c5deaeeee94d4bcfb12b345bf8e417e5119dae773efd0000000000116ac000000000000000000002c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa0000001457cd18b7f8a4d91a2da9ab4af05d0fbece2dcd65"; - - uint8 sigGuardianIndex = 0; - - QueryResponse queryResponse; - - function setUp() public { - WormholeMock wormholeMock = new WormholeMock(); - queryResponse = new QueryResponseContract(address(wormholeMock)); - } - - uint16 constant TEST_CHAIN_ID = 2; - address constant DEVNET_GUARDIAN = 0xbeFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe; - uint256 constant DEVNET_GUARDIAN_PRIVATE_KEY = 0xcfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0; - uint16 constant GOVERNANCE_CHAIN_ID = 1; - bytes32 constant GOVERNANCE_CONTRACT = 0x0000000000000000000000000000000000000000000000000000000000000004; - - function getSignature(bytes memory response) internal view returns (uint8 v, bytes32 r, bytes32 s) { - bytes32 responseDigest = queryResponse.getResponseDigest(response); - (v, r, s) = vm.sign(DEVNET_GUARDIAN_PRIVATE_KEY, responseDigest); - } - - function concatenateQueryResponseBytesOffChain( - uint8 _version, - uint16 _senderChainId, - bytes memory _signature, - uint8 _queryRequestVersion, - uint32 _queryRequestNonce, - uint8 _numPerChainQueries, - bytes memory _perChainQueries, - uint8 _numPerChainResponses, - bytes memory _perChainResponses - ) internal pure returns (bytes memory){ - bytes memory queryRequest = QueryTest.buildOffChainQueryRequestBytes( - _queryRequestVersion, - _queryRequestNonce, - _numPerChainQueries, - _perChainQueries - ); - return QueryTest.buildQueryResponseBytes( - _version, - _senderChainId, - _signature, - queryRequest, - _numPerChainResponses, - _perChainResponses - ); - } - - function test_getResponseHash() public { - bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); - bytes32 hash = queryResponse.getResponseHash(resp); - bytes32 expectedHash = 0xed18e80906ffa80ce953a132a9cbbcf84186955f8fc8ce0322cd68622a58570e; - assertEq(hash, expectedHash); - } - - function test_getResponseDigest() public { - bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); - bytes32 digest = queryResponse.getResponseDigest(resp); - bytes32 expectedDigest = 0x5b84b19c68ee0b37899230175a92ee6eda4c5192e8bffca1d057d811bb3660e2; - assertEq(digest, expectedDigest); - } - - function test_verifyQueryResponseSignatures() public view { - bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); - (uint8 sigV, bytes32 sigR, bytes32 sigS) = getSignature(resp); - IWormhole.Signature[] memory signatures = new IWormhole.Signature[](1); - signatures[0] = IWormhole.Signature({r: sigR, s: sigS, v: sigV, guardianIndex: sigGuardianIndex}); - queryResponse.verifyQueryResponseSignatures(resp, signatures); - // TODO: There are no assertions for this test - } - - function test_parseAndVerifyQueryResponse() public { - bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); - (uint8 sigV, bytes32 sigR, bytes32 sigS) = getSignature(resp); - IWormhole.Signature[] memory signatures = new IWormhole.Signature[](1); - signatures[0] = IWormhole.Signature({r: sigR, s: sigS, v: sigV, guardianIndex: sigGuardianIndex}); - ParsedQueryResponse memory r = queryResponse.parseAndVerifyQueryResponse(resp, signatures); - assertEq(r.version, 1); - assertEq(r.senderChainId, 0); - assertEq(r.requestId, hex"ff0c222dc9e3655ec38e212e9792bf1860356d1277462b6bf747db865caca6fc08e6317b64ee3245264e371146b1d315d38c867fe1f69614368dc4430bb560f200"); - assertEq(r.nonce, 3717797062); - assertEq(r.responses.length, 1); - assertEq(r.responses[0].chainId, 5); - assertEq(r.responses[0].queryType, 1); - assertEq(r.responses[0].request, hex"00000009307832613631616334020d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000406fdde030d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000418160ddd"); - assertEq(r.responses[0].response, hex"0000000002a61ac4c1adff9f6e180309e7d0d94c063338ddc61c1c4474cd6957c960efe659534d040005ff312e4f90c002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d57726170706564204d6174696300000000000000000000000000000000000000000000200000000000000000000000000000000000000000007ae5649beabeddf889364a"); - } - - function test_parseEthCallQueryResponse() public { - // Take the data extracted by the previous test and break it down even further. - ParsedPerChainQueryResponse memory r = ParsedPerChainQueryResponse({ - chainId: 5, - queryType: 1, - request: hex"00000009307832613631616334020d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000406fdde030d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000418160ddd", - response: hex"0000000002a61ac4c1adff9f6e180309e7d0d94c063338ddc61c1c4474cd6957c960efe659534d040005ff312e4f90c002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d57726170706564204d6174696300000000000000000000000000000000000000000000200000000000000000000000000000000000000000007ae5649beabeddf889364a" - }); - - EthCallQueryResponse memory eqr = queryResponse.parseEthCallQueryResponse(r); - assertEq(eqr.requestBlockId, hex"307832613631616334"); - assertEq(eqr.blockNum, 44440260); - assertEq(eqr.blockHash, hex"c1adff9f6e180309e7d0d94c063338ddc61c1c4474cd6957c960efe659534d04"); - assertEq(eqr.blockTime, 1687961579000000); - assertEq(eqr.result.length, 2); - - assertEq(eqr.result[0].contractAddress, address(0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270)); - assertEq(eqr.result[0].callData, hex"06fdde03"); - assertEq(eqr.result[0].result, hex"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d57726170706564204d6174696300000000000000000000000000000000000000"); - - assertEq(eqr.result[1].contractAddress, address(0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270)); - assertEq(eqr.result[1].callData, hex"18160ddd"); - assertEq(eqr.result[1].result, hex"0000000000000000000000000000000000000000007ae5649beabeddf889364a"); - } - - function test_parseEthCallQueryResponseRevertWrongQueryType() public { - // Take the data extracted by the previous test and break it down even further. - ParsedPerChainQueryResponse memory r = ParsedPerChainQueryResponse({ - chainId: 5, - queryType: 2, - request: hex"00000009307832613631616334020d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000406fdde030d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000418160ddd", - response: hex"0000000002a61ac4c1adff9f6e180309e7d0d94c063338ddc61c1c4474cd6957c960efe659534d040005ff312e4f90c002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d57726170706564204d6174696300000000000000000000000000000000000000000000200000000000000000000000000000000000000000007ae5649beabeddf889364a" - }); - - vm.expectRevert(abi.encodeWithSelector(WrongQueryType.selector, 2, queryResponse.QT_ETH_CALL())); - queryResponse.parseEthCallQueryResponse(r); - } - - function test_parseEthCallQueryResponseComparison() public { - ParsedPerChainQueryResponse memory r = ParsedPerChainQueryResponse({ - chainId: 23, - queryType: 1, - request: hex"00000009307832376433333433013ce792601c936b1c81f73ea2fa77208c0a478bae00000004916d5743", - response: hex"00000000027d3343b9848f128b3658a0b9b50aa174e3ddc15ac4e54c84ee534b6d247adbdfc300c90006056cda47a84001000000200000000000000000000000000000000000000000000000000000000000000004" - }); - - EthCallQueryResponse memory eqr = queryResponse.parseEthCallQueryResponse(r); - assertEq(eqr.requestBlockId, "0x27d3343"); - assertEq(eqr.blockNum, 0x27d3343); - assertEq(eqr.blockHash, hex"b9848f128b3658a0b9b50aa174e3ddc15ac4e54c84ee534b6d247adbdfc300c9"); - vm.warp(1694814937); - assertEq(eqr.blockTime / 1_000_000, block.timestamp); - assertEq(eqr.result.length, 1); - - assertEq(eqr.result[0].contractAddress, address(0x3ce792601c936b1c81f73Ea2fa77208C0A478BaE)); - assertEq(eqr.result[0].callData, hex"916d5743"); - bytes memory callData = eqr.result[0].callData; - bytes4 callSignature; - assembly { - callSignature := mload(add(callData, 32)) - } - assertEq(callSignature, bytes4(keccak256("getMyCounter()"))); - assertEq(eqr.result[0].result, hex"0000000000000000000000000000000000000000000000000000000000000004"); - assertEq(abi.decode(eqr.result[0].result, (uint256)), 4); - } - - function test_parseEthCallByTimestampQueryResponse() public { - // Take the data extracted by the previous test and break it down even further. - ParsedPerChainQueryResponse memory r = ParsedPerChainQueryResponse({ - chainId: 2, - queryType: 2, - request: hex"00000003f4810cc0000000063078343237310000000630783432373202ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000418160ddd", - response: hex"0000000000004271ec70d2f70cf1933770ae760050a75334ce650aa091665ee43a6ed488cd154b0800000003f4810cc000000000000042720b1608c2cddfd9d7fb4ec94f79ec1389e2410e611a2c2bbde94e9ad37519ebbb00000003f4904f0002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000" - }); - - EthCallByTimestampQueryResponse memory eqr = queryResponse.parseEthCallByTimestampQueryResponse(r); - assertEq(eqr.requestTargetBlockIdHint, hex"307834323731"); - assertEq(eqr.requestFollowingBlockIdHint, hex"307834323732"); - assertEq(eqr.requestTargetTimestamp, 0x03f4810cc0); - assertEq(eqr.targetBlockNum, 0x0000000000004271); - assertEq(eqr.targetBlockHash, hex"ec70d2f70cf1933770ae760050a75334ce650aa091665ee43a6ed488cd154b08"); - assertEq(eqr.targetBlockTime, 0x03f4810cc0); - assertEq(eqr.followingBlockNum, 0x0000000000004272); - assertEq(eqr.followingBlockHash, hex"0b1608c2cddfd9d7fb4ec94f79ec1389e2410e611a2c2bbde94e9ad37519ebbb"); - assertEq(eqr.followingBlockTime, 0x03f4904f00); - assertEq(eqr.result.length, 2); - - assertEq(eqr.result[0].contractAddress, address(0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E)); - assertEq(eqr.result[0].callData, hex"06fdde03"); - assertEq(eqr.result[0].result, hex"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000"); - - assertEq(eqr.result[1].contractAddress, address(0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E)); - assertEq(eqr.result[1].callData, hex"18160ddd"); - assertEq(eqr.result[1].result, hex"0000000000000000000000000000000000000000000000000000000000000000"); - } - - function test_parseEthCallByTimestampQueryResponseRevertWrongQueryType() public { - // Take the data extracted by the previous test and break it down even further. - ParsedPerChainQueryResponse memory r = ParsedPerChainQueryResponse({ - chainId: 2, - queryType: 1, - request: hex"00000003f4810cc0000000063078343237310000000630783432373202ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000418160ddd", - response: hex"0000000000004271ec70d2f70cf1933770ae760050a75334ce650aa091665ee43a6ed488cd154b0800000003f4810cc000000000000042720b1608c2cddfd9d7fb4ec94f79ec1389e2410e611a2c2bbde94e9ad37519ebbb00000003f4904f0002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000" - }); - - vm.expectRevert(abi.encodeWithSelector(WrongQueryType.selector, 1, queryResponse.QT_ETH_CALL_BY_TIMESTAMP())); - queryResponse.parseEthCallByTimestampQueryResponse(r); - } - - function test_parseEthCallWithFinalityQueryResponse() public { - // Take the data extracted by the previous test and break it down even further. - ParsedPerChainQueryResponse memory r = ParsedPerChainQueryResponse({ - chainId: 2, - queryType: 3, - request: hex"000000063078363032390000000966696e616c697a656402ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000418160ddd", - response: hex"00000000000060299eb9c56ffdae81214867ed217f5ab37e295c196b4f04b23a795d3e4aea6ff3d700000005bb1bd58002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000" - }); - - EthCallWithFinalityQueryResponse memory eqr = queryResponse.parseEthCallWithFinalityQueryResponse(r); - assertEq(eqr.requestBlockId, hex"307836303239"); - assertEq(eqr.requestFinality, hex"66696e616c697a6564"); - assertEq(eqr.blockNum, 0x6029); - assertEq(eqr.blockHash, hex"9eb9c56ffdae81214867ed217f5ab37e295c196b4f04b23a795d3e4aea6ff3d7"); - assertEq(eqr.blockTime, 0x05bb1bd580); - assertEq(eqr.result.length, 2); - - assertEq(eqr.result[0].contractAddress, address(0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E)); - assertEq(eqr.result[0].callData, hex"06fdde03"); - assertEq(eqr.result[0].result, hex"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000"); - - assertEq(eqr.result[1].contractAddress, address(0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E)); - assertEq(eqr.result[1].callData, hex"18160ddd"); - assertEq(eqr.result[1].result, hex"0000000000000000000000000000000000000000000000000000000000000000"); - } - - function test_parseEthCallWithFinalityQueryResponseRevertWrongQueryType() public { - // Take the data extracted by the previous test and break it down even further. - ParsedPerChainQueryResponse memory r = ParsedPerChainQueryResponse({ - chainId: 2, - queryType: 1, - request: hex"000000063078363032390000000966696e616c697a656402ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000418160ddd", - response: hex"00000000000060299eb9c56ffdae81214867ed217f5ab37e295c196b4f04b23a795d3e4aea6ff3d700000005bb1bd58002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000" - }); - - vm.expectRevert(abi.encodeWithSelector(WrongQueryType.selector, 1, queryResponse.QT_ETH_CALL_WITH_FINALITY())); - queryResponse.parseEthCallWithFinalityQueryResponse(r); - } - - // Start of Solana Stuff /////////////////////////////////////////////////////////////////////////// - - function test_verifyQueryResponseSignaturesForSolana() public view { - bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, solanaAccountSignature, solanaAccountQueryRequestVersion, solanaAccountQueryRequestNonce, solanaAccountNumPerChainQueries, solanaAccountPerChainQueries, solanaAccountNumPerChainResponses, solanaAccountPerChainResponses); - (uint8 sigV, bytes32 sigR, bytes32 sigS) = getSignature(resp); - IWormhole.Signature[] memory signatures = new IWormhole.Signature[](1); - signatures[0] = IWormhole.Signature({r: sigR, s: sigS, v: sigV, guardianIndex: sigGuardianIndex}); - queryResponse.verifyQueryResponseSignatures(resp, signatures); - // TODO: There are no assertions for this test - } - - function test_parseSolanaAccountQueryResponse() public { - // Take the data extracted by the previous test and break it down even further. - ParsedPerChainQueryResponse memory r = ParsedPerChainQueryResponse({ - chainId: 1, - queryType: 4, - request: solanaAccountPerChainQueriesInner, - response: solanaAccountPerChainResponsesInner - }); - - SolanaAccountQueryResponse memory sar = queryResponse.parseSolanaAccountQueryResponse(r); - - assertEq(sar.requestCommitment, "finalized"); - assertEq(sar.requestMinContextSlot, 0); - assertEq(sar.requestDataSliceOffset, 0); - assertEq(sar.requestDataSliceLength, 0); - assertEq(sar.slotNumber, 0xd85f); - assertEq(sar.blockTime, 0x00060f3e9915ddc0); - assertEq(sar.blockHash, hex"3a8de2b1de609020bb0a0dcee594a8c06801619cf9ea2a498b9d910f9a25772b"); - assertEq(sar.results.length, 2); - - assertEq(sar.results[0].account, hex"165809739240a0ac03b98440fe8985548e3aa683cd0d4d9df5b5659669faa301"); - assertEq(sar.results[0].lamports, 0x164d60); - assertEq(sar.results[0].rentEpoch, 0); - assertEq(sar.results[0].executable, false); - assertEq(sar.results[0].owner, hex"06ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a9"); - assertEq(sar.results[0].data, hex"01000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d0000e8890423c78a0901000000000000000000000000000000000000000000000000000000000000000000000000"); - - assertEq(sar.results[1].account, hex"9c006c48c8cbf33849cb07a3f936159cc523f9591cb1999abd45890ec5fee9b7"); - assertEq(sar.results[1].lamports, 0x164d60); - assertEq(sar.results[1].rentEpoch, 0); - assertEq(sar.results[1].executable, false); - assertEq(sar.results[1].owner, hex"06ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a9"); - assertEq(sar.results[1].data, hex"01000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d01000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000"); - } - - function test_parseSolanaAccountQueryResponseRevertWrongQueryType() public { - // Pass an ETH per chain response into the Solana parser. - ParsedPerChainQueryResponse memory r = ParsedPerChainQueryResponse({ - chainId: 2, - queryType: 1, - request: solanaAccountPerChainQueriesInner, - response: solanaAccountPerChainResponsesInner - }); - - vm.expectRevert(abi.encodeWithSelector(WrongQueryType.selector, 1, queryResponse.QT_SOL_ACCOUNT())); - queryResponse.parseSolanaAccountQueryResponse(r); - } - - function test_parseSolanaAccountQueryResponseRevertUnexpectedNumberOfResults() public { - // Only one account on the request but two in the response. - bytes memory requestWithOnlyOneAccount = hex"0000000966696e616c697a656400000000000000000000000000000000000000000000000001165809739240a0ac03b98440fe8985548e3aa683cd0d4d9df5b5659669faa301"; - ParsedPerChainQueryResponse memory r = ParsedPerChainQueryResponse({ - chainId: 1, - queryType: 4, - request: requestWithOnlyOneAccount, - response: solanaAccountPerChainResponsesInner - }); - - vm.expectRevert(UnexpectedNumberOfResults.selector); - queryResponse.parseSolanaAccountQueryResponse(r); - } - - function test_parseSolanaAccountQueryResponseExtraRequestBytesRevertInvalidPayloadLength() public { - // Extra bytes at the end of the request. - bytes memory requestWithExtraBytes = hex"0000000966696e616c697a656400000000000000000000000000000000000000000000000002165809739240a0ac03b98440fe8985548e3aa683cd0d4d9df5b5659669faa3019c006c48c8cbf33849cb07a3f936159cc523f9591cb1999abd45890ec5fee9b7DEADBEEF"; - ParsedPerChainQueryResponse memory r = ParsedPerChainQueryResponse({ - chainId: 1, - queryType: 4, - request: requestWithExtraBytes, - response: solanaAccountPerChainResponsesInner - }); - - vm.expectRevert(abi.encodeWithSelector(InvalidPayloadLength.selector, 106, 102)); - queryResponse.parseSolanaAccountQueryResponse(r); - } - - function test_parseSolanaAccountQueryResponseExtraResponseBytesRevertInvalidPayloadLength() public { - // Extra bytes at the end of the response. - bytes memory responseWithExtraBytes = hex"000000000000d85f00060f3e9915ddc03a8de2b1de609020bb0a0dcee594a8c06801619cf9ea2a498b9d910f9a25772b020000000000164d6000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90000005201000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d0000e8890423c78a09010000000000000000000000000000000000000000000000000000000000000000000000000000000000164d6000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90000005201000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d01000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000DEADBEEF"; - ParsedPerChainQueryResponse memory r = ParsedPerChainQueryResponse({ - chainId: 1, - queryType: 4, - request: solanaAccountPerChainQueriesInner, - response: responseWithExtraBytes - }); - - vm.expectRevert(abi.encodeWithSelector(InvalidPayloadLength.selector, 323, 319)); - queryResponse.parseSolanaAccountQueryResponse(r); - } - - function test_parseSolanaPdaQueryResponse() public { - // Take the data extracted by the previous test and break it down even further. - ParsedPerChainQueryResponse memory r = ParsedPerChainQueryResponse({ - chainId: 1, - queryType: 5, - request: solanaPdaPerChainQueriesInner, - response: solanaPdaPerChainResponsesInner - }); - - SolanaPdaQueryResponse memory sar = queryResponse.parseSolanaPdaQueryResponse(r); - - assertEq(sar.requestCommitment, "finalized"); - assertEq(sar.requestMinContextSlot, 2303); - assertEq(sar.requestDataSliceOffset, 12); - assertEq(sar.requestDataSliceLength, 20); - assertEq(sar.slotNumber, 2303); - assertEq(sar.blockTime, 0x0006115e3f6d7540); - assertEq(sar.blockHash, hex"e05035785e15056a8559815e71343ce31db2abf23f65b19c982b68aee7bf207b"); - assertEq(sar.results.length, 1); - - assertEq(sar.results[0].programId, hex"02c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa"); - assertEq(sar.results[0].seeds.length, 2); - assertEq(sar.results[0].seeds[0], hex"477561726469616e536574"); - assertEq(sar.results[0].seeds[1], hex"00000000"); - - assertEq(sar.results[0].account, hex"4fa9188b339cfd573a0778c5deaeeee94d4bcfb12b345bf8e417e5119dae773e"); - assertEq(sar.results[0].bump, 253); - assertEq(sar.results[0].lamports, 0x116ac0); - assertEq(sar.results[0].rentEpoch, 0); - assertEq(sar.results[0].executable, false); - assertEq(sar.results[0].owner, hex"02c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa"); - assertEq(sar.results[0].data, hex"57cd18b7f8a4d91a2da9ab4af05d0fbece2dcd65"); - } + // Some happy case defaults + uint8 version = 0x01; + uint16 senderChainId = 0x0000; + bytes signature = hex"ff0c222dc9e3655ec38e212e9792bf1860356d1277462b6bf747db865caca6fc08e6317b64ee3245264e371146b1d315d38c867fe1f69614368dc4430bb560f200"; + uint32 queryRequestLen = 0x00000053; + uint8 queryRequestVersion = 0x01; + uint32 queryRequestNonce = 0xdd9914c6; + uint8 numPerChainQueries = 0x01; + bytes perChainQueries = hex"0005010000004600000009307832613631616334020d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000406fdde030d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000418160ddd"; + bytes perChainQueriesInner = hex"00000009307832613631616334020d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000406fdde030d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000418160ddd"; + uint8 numPerChainResponses = 0x01; + bytes perChainResponses = hex"000501000000b90000000002a61ac4c1adff9f6e180309e7d0d94c063338ddc61c1c4474cd6957c960efe659534d040005ff312e4f90c002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d57726170706564204d6174696300000000000000000000000000000000000000000000200000000000000000000000000000000000000000007ae5649beabeddf889364a"; + bytes perChainResponsesInner = hex"00000009307832613631616334020d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000406fdde030d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000418160ddd"; + + bytes solanaAccountSignature = hex"acb1d93cdfe60f9776e3e05d7fafaf9d83a1d14db70317230f6b0b6f3a60708a1a64dddac02d3843f4c516f2509b89454a2e73c360fea47beee1c1a091ff9f3201"; + uint32 solanaAccountQueryRequestLen = 0x00000073; + uint8 solanaAccountQueryRequestVersion = 0x01; + uint32 solanaAccountQueryRequestNonce = 0x0000002a; + uint8 solanaAccountNumPerChainQueries = 0x01; + bytes solanaAccountPerChainQueries = hex"000104000000660000000966696e616c697a656400000000000000000000000000000000000000000000000002165809739240a0ac03b98440fe8985548e3aa683cd0d4d9df5b5659669faa3019c006c48c8cbf33849cb07a3f936159cc523f9591cb1999abd45890ec5fee9b7"; + bytes solanaAccountPerChainQueriesInner = hex"0000000966696e616c697a656400000000000000000000000000000000000000000000000002165809739240a0ac03b98440fe8985548e3aa683cd0d4d9df5b5659669faa3019c006c48c8cbf33849cb07a3f936159cc523f9591cb1999abd45890ec5fee9b7"; + uint8 solanaAccountNumPerChainResponses = 0x01; + bytes solanaAccountPerChainResponses = hex"010001040000013f000000000000d85f00060f3e9915ddc03a8de2b1de609020bb0a0dcee594a8c06801619cf9ea2a498b9d910f9a25772b020000000000164d6000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90000005201000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d0000e8890423c78a09010000000000000000000000000000000000000000000000000000000000000000000000000000000000164d6000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90000005201000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d01000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000"; + bytes solanaAccountPerChainResponsesInner = hex"000000000000d85f00060f3e9915ddc03a8de2b1de609020bb0a0dcee594a8c06801619cf9ea2a498b9d910f9a25772b020000000000164d6000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90000005201000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d0000e8890423c78a09010000000000000000000000000000000000000000000000000000000000000000000000000000000000164d6000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90000005201000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d01000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000"; + + bytes solanaPdaSignature = hex"0c8418d81c00aad6283ba3eb30e141ccdd9296e013ca44e5cc713418921253004b93107ba0d858a548ce989e2bca4132e4c2f9a57a9892e3a87a8304cdb36d8f00"; + uint32 solanaPdaQueryRequestLen = 0x0000006b; + uint8 solanaPdaQueryRequestVersion = 0x01; + uint32 solanaPdaQueryRequestNonce = 0x0000002b; + uint8 solanaPdaNumPerChainQueries = 0x01; + bytes solanaPdaPerChainQueries = hex"010001050000005e0000000966696e616c697a656400000000000008ff000000000000000c00000000000000140102c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa020000000b477561726469616e5365740000000400000000"; + bytes solanaPdaPerChainQueriesInner = hex"0000000966696e616c697a656400000000000008ff000000000000000c00000000000000140102c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa020000000b477561726469616e5365740000000400000000"; + uint8 solanaPdaNumPerChainResponses = 0x01; + bytes solanaPdaPerChainResponses = hex"0001050000009b00000000000008ff0006115e3f6d7540e05035785e15056a8559815e71343ce31db2abf23f65b19c982b68aee7bf207b014fa9188b339cfd573a0778c5deaeeee94d4bcfb12b345bf8e417e5119dae773efd0000000000116ac000000000000000000002c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa0000001457cd18b7f8a4d91a2da9ab4af05d0fbece2dcd65"; + bytes solanaPdaPerChainResponsesInner = hex"00000000000008ff0006115e3f6d7540e05035785e15056a8559815e71343ce31db2abf23f65b19c982b68aee7bf207b014fa9188b339cfd573a0778c5deaeeee94d4bcfb12b345bf8e417e5119dae773efd0000000000116ac000000000000000000002c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa0000001457cd18b7f8a4d91a2da9ab4af05d0fbece2dcd65"; + + uint8 constant SIG_GUARDIAN_INDEX = 0; + + address wormhole; + QueryResponseLibWrapper wrapper; + + function setUp() public { + wormhole = address(new WormholeMock()); + wrapper = new QueryResponseLibWrapper(); + } + + uint16 constant TEST_CHAIN_ID = 2; + address constant DEVNET_GUARDIAN = 0xbeFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe; + uint256 constant DEVNET_GUARDIAN_PRIVATE_KEY = 0xcfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0; + uint16 constant GOVERNANCE_CHAIN_ID = 1; + bytes32 constant GOVERNANCE_CONTRACT = 0x0000000000000000000000000000000000000000000000000000000000000004; + + function getSignature( + bytes memory response + ) internal pure returns (IWormhole.Signature[] memory signatures) { + bytes32 responseDigest = QueryResponseLib.calcPrefixedResponseHash(response); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(DEVNET_GUARDIAN_PRIVATE_KEY, responseDigest); + signatures = new IWormhole.Signature[](1); + signatures[0] = IWormhole.Signature(r, s, v, SIG_GUARDIAN_INDEX); + } + + function concatenateQueryResponseBytesOffChain( + uint8 _version, + uint16 _senderChainId, + bytes memory _signature, + uint8 _queryRequestVersion, + uint32 _queryRequestNonce, + uint8 _numPerChainQueries, + bytes memory _perChainQueries, + uint8 _numPerChainResponses, + bytes memory _perChainResponses + ) internal pure returns (bytes memory){ + bytes memory queryRequest = QueryTest.buildOffChainQueryRequestBytes( + _queryRequestVersion, + _queryRequestNonce, + _numPerChainQueries, + _perChainQueries + ); + return QueryTest.buildQueryResponseBytes( + _version, + _senderChainId, + _signature, + queryRequest, + _numPerChainResponses, + _perChainResponses + ); + } + + function test_calcPrefixedResponseHash() public { + bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); + bytes32 digest = wrapper.calcPrefixedResponseHash(resp); + bytes32 expectedDigest = 0x5b84b19c68ee0b37899230175a92ee6eda4c5192e8bffca1d057d811bb3660e2; + assertEq(digest, expectedDigest); + } + + function test_verifyQueryResponse() public view { + bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); + wrapper.verifyQueryResponse(wormhole, resp, getSignature(resp)); + // TODO: There are no assertions for this test + } + + function test_parseAndVerifyQueryResponse() public { + bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); + QueryResponse memory r = wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); + assertEq(r.version, 1); + assertEq(r.senderChainId, 0); + assertEq(r.requestId, hex"ff0c222dc9e3655ec38e212e9792bf1860356d1277462b6bf747db865caca6fc08e6317b64ee3245264e371146b1d315d38c867fe1f69614368dc4430bb560f200"); + assertEq(r.nonce, 3717797062); + assertEq(r.responses.length, 1); + assertEq(r.responses[0].chainId, 5); + assertEq(r.responses[0].queryType, 1); + assertEq(r.responses[0].request, hex"00000009307832613631616334020d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000406fdde030d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000418160ddd"); + assertEq(r.responses[0].response, hex"0000000002a61ac4c1adff9f6e180309e7d0d94c063338ddc61c1c4474cd6957c960efe659534d040005ff312e4f90c002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d57726170706564204d6174696300000000000000000000000000000000000000000000200000000000000000000000000000000000000000007ae5649beabeddf889364a"); + } + + function test_parseEthCallQueryResponse() public { + // Take the data extracted by the previous test and break it down even further. + PerChainQueryResponse memory r = PerChainQueryResponse({ + chainId: 5, + queryType: 1, + request: hex"00000009307832613631616334020d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000406fdde030d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000418160ddd", + response: hex"0000000002a61ac4c1adff9f6e180309e7d0d94c063338ddc61c1c4474cd6957c960efe659534d040005ff312e4f90c002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d57726170706564204d6174696300000000000000000000000000000000000000000000200000000000000000000000000000000000000000007ae5649beabeddf889364a" + }); + + EthCallQueryResponse memory eqr = wrapper.parseEthCallQueryResponse(r); + assertEq(eqr.requestBlockId, hex"307832613631616334"); + assertEq(eqr.blockNum, 44440260); + assertEq(eqr.blockHash, hex"c1adff9f6e180309e7d0d94c063338ddc61c1c4474cd6957c960efe659534d04"); + assertEq(eqr.blockTime, 1687961579000000); + assertEq(eqr.results.length, 2); + + assertEq(eqr.results[0].contractAddress, address(0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270)); + assertEq(eqr.results[0].callData, hex"06fdde03"); + assertEq(eqr.results[0].result, hex"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d57726170706564204d6174696300000000000000000000000000000000000000"); + + assertEq(eqr.results[1].contractAddress, address(0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270)); + assertEq(eqr.results[1].callData, hex"18160ddd"); + assertEq(eqr.results[1].result, hex"0000000000000000000000000000000000000000007ae5649beabeddf889364a"); + } + + function test_parseEthCallQueryResponseRevertWrongQueryType() public { + // Take the data extracted by the previous test and break it down even further. + PerChainQueryResponse memory r = PerChainQueryResponse({ + chainId: 5, + queryType: 2, + request: hex"00000009307832613631616334020d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000406fdde030d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000418160ddd", + response: hex"0000000002a61ac4c1adff9f6e180309e7d0d94c063338ddc61c1c4474cd6957c960efe659534d040005ff312e4f90c002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d57726170706564204d6174696300000000000000000000000000000000000000000000200000000000000000000000000000000000000000007ae5649beabeddf889364a" + }); + + vm.expectRevert(abi.encodeWithSelector(WrongQueryType.selector, 2, QueryType.ETH_CALL)); + wrapper.parseEthCallQueryResponse(r); + } + + function test_parseEthCallQueryResponseComparison() public { + PerChainQueryResponse memory r = PerChainQueryResponse({ + chainId: 23, + queryType: 1, + request: hex"00000009307832376433333433013ce792601c936b1c81f73ea2fa77208c0a478bae00000004916d5743", + response: hex"00000000027d3343b9848f128b3658a0b9b50aa174e3ddc15ac4e54c84ee534b6d247adbdfc300c90006056cda47a84001000000200000000000000000000000000000000000000000000000000000000000000004" + }); + + EthCallQueryResponse memory eqr = wrapper.parseEthCallQueryResponse(r); + assertEq(eqr.requestBlockId, "0x27d3343"); + assertEq(eqr.blockNum, 0x27d3343); + assertEq(eqr.blockHash, hex"b9848f128b3658a0b9b50aa174e3ddc15ac4e54c84ee534b6d247adbdfc300c9"); + vm.warp(1694814937); + assertEq(eqr.blockTime / 1_000_000, block.timestamp); + assertEq(eqr.results.length, 1); + + assertEq(eqr.results[0].contractAddress, address(0x3ce792601c936b1c81f73Ea2fa77208C0A478BaE)); + assertEq(eqr.results[0].callData, hex"916d5743"); + bytes memory callData = eqr.results[0].callData; + bytes4 callSignature; + assembly { callSignature := mload(add(callData, 32)) } + assertEq(callSignature, bytes4(keccak256("getMyCounter()"))); + assertEq(eqr.results[0].result, hex"0000000000000000000000000000000000000000000000000000000000000004"); + assertEq(abi.decode(eqr.results[0].result, (uint256)), 4); + } + + function test_parseEthCallByTimestampQueryResponse() public { + // Take the data extracted by the previous test and break it down even further. + PerChainQueryResponse memory r = PerChainQueryResponse({ + chainId: 2, + queryType: 2, + request: hex"00000003f4810cc0000000063078343237310000000630783432373202ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000418160ddd", + response: hex"0000000000004271ec70d2f70cf1933770ae760050a75334ce650aa091665ee43a6ed488cd154b0800000003f4810cc000000000000042720b1608c2cddfd9d7fb4ec94f79ec1389e2410e611a2c2bbde94e9ad37519ebbb00000003f4904f0002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000" + }); + + EthCallByTimestampQueryResponse memory eqr = wrapper.parseEthCallByTimestampQueryResponse(r); + assertEq(eqr.requestTargetBlockIdHint, hex"307834323731"); + assertEq(eqr.requestFollowingBlockIdHint, hex"307834323732"); + assertEq(eqr.requestTargetTimestamp, 0x03f4810cc0); + assertEq(eqr.targetBlockNum, 0x0000000000004271); + assertEq(eqr.targetBlockHash, hex"ec70d2f70cf1933770ae760050a75334ce650aa091665ee43a6ed488cd154b08"); + assertEq(eqr.targetBlockTime, 0x03f4810cc0); + assertEq(eqr.followingBlockNum, 0x0000000000004272); + assertEq(eqr.followingBlockHash, hex"0b1608c2cddfd9d7fb4ec94f79ec1389e2410e611a2c2bbde94e9ad37519ebbb"); + assertEq(eqr.followingBlockTime, 0x03f4904f00); + assertEq(eqr.results.length, 2); + + assertEq(eqr.results[0].contractAddress, address(0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E)); + assertEq(eqr.results[0].callData, hex"06fdde03"); + assertEq(eqr.results[0].result, hex"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000"); + + assertEq(eqr.results[1].contractAddress, address(0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E)); + assertEq(eqr.results[1].callData, hex"18160ddd"); + assertEq(eqr.results[1].result, hex"0000000000000000000000000000000000000000000000000000000000000000"); + } + + function test_parseEthCallByTimestampQueryResponseRevertWrongQueryType() public { + // Take the data extracted by the previous test and break it down even further. + PerChainQueryResponse memory r = PerChainQueryResponse({ + chainId: 2, + queryType: 1, + request: hex"00000003f4810cc0000000063078343237310000000630783432373202ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000418160ddd", + response: hex"0000000000004271ec70d2f70cf1933770ae760050a75334ce650aa091665ee43a6ed488cd154b0800000003f4810cc000000000000042720b1608c2cddfd9d7fb4ec94f79ec1389e2410e611a2c2bbde94e9ad37519ebbb00000003f4904f0002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000" + }); + + vm.expectRevert(abi.encodeWithSelector(WrongQueryType.selector, 1, QueryType.ETH_CALL_BY_TIMESTAMP)); + wrapper.parseEthCallByTimestampQueryResponse(r); + } + + function test_parseEthCallWithFinalityQueryResponse() public { + // Take the data extracted by the previous test and break it down even further. + PerChainQueryResponse memory r = PerChainQueryResponse({ + chainId: 2, + queryType: 3, + request: hex"000000063078363032390000000966696e616c697a656402ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000418160ddd", + response: hex"00000000000060299eb9c56ffdae81214867ed217f5ab37e295c196b4f04b23a795d3e4aea6ff3d700000005bb1bd58002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000" + }); + + EthCallWithFinalityQueryResponse memory eqr = wrapper.parseEthCallWithFinalityQueryResponse(r); + assertEq(eqr.requestBlockId, hex"307836303239"); + assertEq(eqr.requestFinality, hex"66696e616c697a6564"); + assertEq(eqr.blockNum, 0x6029); + assertEq(eqr.blockHash, hex"9eb9c56ffdae81214867ed217f5ab37e295c196b4f04b23a795d3e4aea6ff3d7"); + assertEq(eqr.blockTime, 0x05bb1bd580); + assertEq(eqr.results.length, 2); + + assertEq(eqr.results[0].contractAddress, address(0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E)); + assertEq(eqr.results[0].callData, hex"06fdde03"); + assertEq(eqr.results[0].result, hex"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000"); + + assertEq(eqr.results[1].contractAddress, address(0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E)); + assertEq(eqr.results[1].callData, hex"18160ddd"); + assertEq(eqr.results[1].result, hex"0000000000000000000000000000000000000000000000000000000000000000"); + } + + function test_parseEthCallWithFinalityQueryResponseRevertWrongQueryType() public { + // Take the data extracted by the previous test and break it down even further. + PerChainQueryResponse memory r = PerChainQueryResponse({ + chainId: 2, + queryType: 1, + request: hex"000000063078363032390000000966696e616c697a656402ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000418160ddd", + response: hex"00000000000060299eb9c56ffdae81214867ed217f5ab37e295c196b4f04b23a795d3e4aea6ff3d700000005bb1bd58002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000" + }); + + vm.expectRevert(abi.encodeWithSelector(WrongQueryType.selector, 1, QueryType.ETH_CALL_WITH_FINALITY)); + wrapper.parseEthCallWithFinalityQueryResponse(r); + } + + // Start of Solana Stuff /////////////////////////////////////////////////////////////////////////// + + function test_verifyQueryResponseForSolana() public view { + bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, solanaAccountSignature, solanaAccountQueryRequestVersion, solanaAccountQueryRequestNonce, solanaAccountNumPerChainQueries, solanaAccountPerChainQueries, solanaAccountNumPerChainResponses, solanaAccountPerChainResponses); + wrapper.verifyQueryResponse(wormhole, resp, getSignature(resp)); + // TODO: There are no assertions for this test + } + + function test_parseSolanaAccountQueryResponse() public { + // Take the data extracted by the previous test and break it down even further. + PerChainQueryResponse memory r = PerChainQueryResponse({ + chainId: 1, + queryType: 4, + request: solanaAccountPerChainQueriesInner, + response: solanaAccountPerChainResponsesInner + }); + + SolanaAccountQueryResponse memory sar = wrapper.parseSolanaAccountQueryResponse(r); + + assertEq(sar.requestCommitment, "finalized"); + assertEq(sar.requestMinContextSlot, 0); + assertEq(sar.requestDataSliceOffset, 0); + assertEq(sar.requestDataSliceLength, 0); + assertEq(sar.slotNumber, 0xd85f); + assertEq(sar.blockTime, 0x00060f3e9915ddc0); + assertEq(sar.blockHash, hex"3a8de2b1de609020bb0a0dcee594a8c06801619cf9ea2a498b9d910f9a25772b"); + assertEq(sar.results.length, 2); + + assertEq(sar.results[0].account, hex"165809739240a0ac03b98440fe8985548e3aa683cd0d4d9df5b5659669faa301"); + assertEq(sar.results[0].lamports, 0x164d60); + assertEq(sar.results[0].rentEpoch, 0); + assertEq(sar.results[0].executable, false); + assertEq(sar.results[0].owner, hex"06ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a9"); + assertEq(sar.results[0].data, hex"01000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d0000e8890423c78a0901000000000000000000000000000000000000000000000000000000000000000000000000"); + + assertEq(sar.results[1].account, hex"9c006c48c8cbf33849cb07a3f936159cc523f9591cb1999abd45890ec5fee9b7"); + assertEq(sar.results[1].lamports, 0x164d60); + assertEq(sar.results[1].rentEpoch, 0); + assertEq(sar.results[1].executable, false); + assertEq(sar.results[1].owner, hex"06ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a9"); + assertEq(sar.results[1].data, hex"01000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d01000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000"); + } + + function test_parseSolanaAccountQueryResponseRevertWrongQueryType() public { + // Pass an ETH per chain response into the Solana parser. + PerChainQueryResponse memory r = PerChainQueryResponse({ + chainId: 2, + queryType: 1, + request: solanaAccountPerChainQueriesInner, + response: solanaAccountPerChainResponsesInner + }); + + vm.expectRevert(abi.encodeWithSelector(WrongQueryType.selector, 1, QueryType.SOLANA_ACCOUNT)); + wrapper.parseSolanaAccountQueryResponse(r); + } + + function test_parseSolanaAccountQueryResponseRevertUnexpectedNumberOfResults() public { + // Only one account on the request but two in the response. + bytes memory requestWithOnlyOneAccount = hex"0000000966696e616c697a656400000000000000000000000000000000000000000000000001165809739240a0ac03b98440fe8985548e3aa683cd0d4d9df5b5659669faa301"; + PerChainQueryResponse memory r = PerChainQueryResponse({ + chainId: 1, + queryType: 4, + request: requestWithOnlyOneAccount, + response: solanaAccountPerChainResponsesInner + }); + + vm.expectRevert(UnexpectedNumberOfResults.selector); + wrapper.parseSolanaAccountQueryResponse(r); + } + + function test_parseSolanaAccountQueryResponseExtraRequestBytesRevertInvalidPayloadLength() public { + // Extra bytes at the end of the request. + bytes memory requestWithExtraBytes = hex"0000000966696e616c697a656400000000000000000000000000000000000000000000000002165809739240a0ac03b98440fe8985548e3aa683cd0d4d9df5b5659669faa3019c006c48c8cbf33849cb07a3f936159cc523f9591cb1999abd45890ec5fee9b7DEADBEEF"; + PerChainQueryResponse memory r = PerChainQueryResponse({ + chainId: 1, + queryType: 4, + request: requestWithExtraBytes, + response: solanaAccountPerChainResponsesInner + }); + + vm.expectRevert(abi.encodeWithSelector(InvalidPayloadLength.selector, 106, 102)); + wrapper.parseSolanaAccountQueryResponse(r); + } + + function test_parseSolanaAccountQueryResponseExtraResponseBytesRevertInvalidPayloadLength() public { + // Extra bytes at the end of the response. + bytes memory responseWithExtraBytes = hex"000000000000d85f00060f3e9915ddc03a8de2b1de609020bb0a0dcee594a8c06801619cf9ea2a498b9d910f9a25772b020000000000164d6000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90000005201000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d0000e8890423c78a09010000000000000000000000000000000000000000000000000000000000000000000000000000000000164d6000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90000005201000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d01000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000DEADBEEF"; + PerChainQueryResponse memory r = PerChainQueryResponse({ + chainId: 1, + queryType: 4, + request: solanaAccountPerChainQueriesInner, + response: responseWithExtraBytes + }); + + vm.expectRevert(abi.encodeWithSelector(InvalidPayloadLength.selector, 323, 319)); + wrapper.parseSolanaAccountQueryResponse(r); + } + + function test_parseSolanaPdaQueryResponse() public { + // Take the data extracted by the previous test and break it down even further. + PerChainQueryResponse memory r = PerChainQueryResponse({ + chainId: 1, + queryType: 5, + request: solanaPdaPerChainQueriesInner, + response: solanaPdaPerChainResponsesInner + }); + + SolanaPdaQueryResponse memory sar = wrapper.parseSolanaPdaQueryResponse(r); + + assertEq(sar.requestCommitment, "finalized"); + assertEq(sar.requestMinContextSlot, 2303); + assertEq(sar.requestDataSliceOffset, 12); + assertEq(sar.requestDataSliceLength, 20); + assertEq(sar.slotNumber, 2303); + assertEq(sar.blockTime, 0x0006115e3f6d7540); + assertEq(sar.blockHash, hex"e05035785e15056a8559815e71343ce31db2abf23f65b19c982b68aee7bf207b"); + assertEq(sar.results.length, 1); + + assertEq(sar.results[0].programId, hex"02c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa"); + assertEq(sar.results[0].seeds.length, 2); + assertEq(sar.results[0].seeds[0], hex"477561726469616e536574"); + assertEq(sar.results[0].seeds[1], hex"00000000"); + + assertEq(sar.results[0].account, hex"4fa9188b339cfd573a0778c5deaeeee94d4bcfb12b345bf8e417e5119dae773e"); + assertEq(sar.results[0].bump, 253); + assertEq(sar.results[0].lamports, 0x116ac0); + assertEq(sar.results[0].rentEpoch, 0); + assertEq(sar.results[0].executable, false); + assertEq(sar.results[0].owner, hex"02c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa"); + assertEq(sar.results[0].data, hex"57cd18b7f8a4d91a2da9ab4af05d0fbece2dcd65"); + } + + function test_parseSolanaPdaQueryResponseRevertWrongQueryType() public { + // Pass an ETH per chain response into the Solana parser. + PerChainQueryResponse memory r = PerChainQueryResponse({ + chainId: 2, + queryType: 1, + request: solanaPdaPerChainQueriesInner, + response: solanaPdaPerChainResponsesInner + }); + + vm.expectRevert(abi.encodeWithSelector(WrongQueryType.selector, 1, QueryType.SOLANA_PDA)); + wrapper.parseSolanaPdaQueryResponse(r); + } + + function test_parseSolanaPdaQueryResponseRevertUnexpectedNumberOfResults() public { + // Only one Pda on the request but two in the response. + bytes memory requestWithTwoPdas = hex"0000000966696e616c697a656400000000000008ff000000000000000c00000000000000140202c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa020000000b477561726469616e536574000000040000000002c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa020000000b477561726469616e5365740000000400000000"; + PerChainQueryResponse memory r = PerChainQueryResponse({ + chainId: 1, + queryType: 5, + request: requestWithTwoPdas, + response: solanaPdaPerChainResponsesInner + }); + + vm.expectRevert(UnexpectedNumberOfResults.selector); + wrapper.parseSolanaPdaQueryResponse(r); + } + + function test_parseSolanaPdaQueryResponseExtraRequestBytesRevertInvalidPayloadLength() public { + // Extra bytes at the end of the request. + bytes memory requestWithExtraBytes = hex"0000000966696e616c697a656400000000000008ff000000000000000c00000000000000140102c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa020000000b477561726469616e5365740000000400000000DEADBEEF"; + PerChainQueryResponse memory r = PerChainQueryResponse({ + chainId: 1, + queryType: 5, + request: requestWithExtraBytes, + response: solanaPdaPerChainResponsesInner + }); + + vm.expectRevert(abi.encodeWithSelector(InvalidPayloadLength.selector, 98, 94)); + wrapper.parseSolanaPdaQueryResponse(r); + } + + function test_parseSolanaPdaQueryResponseExtraResponseBytesRevertInvalidPayloadLength() public { + // Extra bytes at the end of the response. + bytes memory responseWithExtraBytes = hex"00000000000008ff0006115e3f6d7540e05035785e15056a8559815e71343ce31db2abf23f65b19c982b68aee7bf207b014fa9188b339cfd573a0778c5deaeeee94d4bcfb12b345bf8e417e5119dae773efd0000000000116ac000000000000000000002c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa0000001457cd18b7f8a4d91a2da9ab4af05d0fbece2dcd65DEADBEEF"; + PerChainQueryResponse memory r = PerChainQueryResponse({ + chainId: 1, + queryType: 5, + request: solanaPdaPerChainQueriesInner, + response: responseWithExtraBytes + }); + + vm.expectRevert(abi.encodeWithSelector(InvalidPayloadLength.selector, 159, 155)); + wrapper.parseSolanaPdaQueryResponse(r); + } + + /*********************************** + *********** FUZZ TESTS ************* + ***********************************/ + + function testFuzz_parseAndVerifyQueryResponse_fuzzVersion(uint8 _version) public { + vm.assume(_version != 1); + + bytes memory resp = concatenateQueryResponseBytesOffChain(_version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); + vm.expectRevert(InvalidResponseVersion.selector); + wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); + } + + function testFuzz_parseAndVerifyQueryResponse_fuzzSenderChainId(uint16 _senderChainId) public { + vm.assume(_senderChainId != 0); + + bytes memory resp = concatenateQueryResponseBytesOffChain(version, _senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); + // This could revert for multiple reasons. But the checkLength to ensure all the bytes are consumed is the backstop. + vm.expectRevert(); + wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); + } + + function testFuzz_parseAndVerifyQueryResponse_fuzzSignatureHappyCase(bytes memory _signature) public { + // This signature isn't validated in the QueryResponse library, therefore it could be an 65 byte hex string + vm.assume(_signature.length == 65); + + bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, _signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); + QueryResponse memory r = wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); + + assertEq(r.requestId, _signature); + } + + function testFuzz_parseAndVerifyQueryResponse_fuzzSignatureUnhappyCase(bytes memory _signature) public { + // A signature that isn't 65 bytes long will always lead to a revert. The type of revert is unknown since it could be one of many. + vm.assume(_signature.length != 65); + + bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, _signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); + vm.expectRevert(); + wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); + } + + function testFuzz_parseAndVerifyQueryResponse_fuzzQueryRequestLen(uint32 _queryRequestLen, bytes calldata _perChainQueries) public { + // We add 6 to account for version + nonce + numPerChainQueries + vm.assume(_queryRequestLen != _perChainQueries.length + 6); + + bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, _perChainQueries, numPerChainResponses, perChainResponses); + vm.expectRevert(); + wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); + } + + function testFuzz_parseAndVerifyQueryResponse_fuzzQueryRequestVersion(uint8 _version, uint8 _queryRequestVersion) public { + vm.assume(_version != _queryRequestVersion); + + bytes memory resp = concatenateQueryResponseBytesOffChain(_version, senderChainId, signature, _queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); + vm.expectRevert(); + wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); + } + + function testFuzz_parseAndVerifyQueryResponse_fuzzQueryRequestNonce(uint32 _queryRequestNonce) public { + bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, _queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); + QueryResponse memory r = wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); - function test_parseSolanaPdaQueryResponseRevertWrongQueryType() public { - // Pass an ETH per chain response into the Solana parser. - ParsedPerChainQueryResponse memory r = ParsedPerChainQueryResponse({ - chainId: 2, - queryType: 1, - request: solanaPdaPerChainQueriesInner, - response: solanaPdaPerChainResponsesInner - }); - - vm.expectRevert(abi.encodeWithSelector(WrongQueryType.selector, 1, queryResponse.QT_SOL_PDA())); - queryResponse.parseSolanaPdaQueryResponse(r); - } - - function test_parseSolanaPdaQueryResponseRevertUnexpectedNumberOfResults() public { - // Only one Pda on the request but two in the response. - bytes memory requestWithTwoPdas = hex"0000000966696e616c697a656400000000000008ff000000000000000c00000000000000140202c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa020000000b477561726469616e536574000000040000000002c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa020000000b477561726469616e5365740000000400000000"; - ParsedPerChainQueryResponse memory r = ParsedPerChainQueryResponse({ - chainId: 1, - queryType: 5, - request: requestWithTwoPdas, - response: solanaPdaPerChainResponsesInner - }); - - vm.expectRevert(UnexpectedNumberOfResults.selector); - queryResponse.parseSolanaPdaQueryResponse(r); - } - - function test_parseSolanaPdaQueryResponseExtraRequestBytesRevertInvalidPayloadLength() public { - // Extra bytes at the end of the request. - bytes memory requestWithExtraBytes = hex"0000000966696e616c697a656400000000000008ff000000000000000c00000000000000140102c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa020000000b477561726469616e5365740000000400000000DEADBEEF"; - ParsedPerChainQueryResponse memory r = ParsedPerChainQueryResponse({ - chainId: 1, - queryType: 5, - request: requestWithExtraBytes, - response: solanaPdaPerChainResponsesInner - }); - - vm.expectRevert(abi.encodeWithSelector(InvalidPayloadLength.selector, 98, 94)); - queryResponse.parseSolanaPdaQueryResponse(r); - } - - function test_parseSolanaPdaQueryResponseExtraResponseBytesRevertInvalidPayloadLength() public { - // Extra bytes at the end of the response. - bytes memory responseWithExtraBytes = hex"00000000000008ff0006115e3f6d7540e05035785e15056a8559815e71343ce31db2abf23f65b19c982b68aee7bf207b014fa9188b339cfd573a0778c5deaeeee94d4bcfb12b345bf8e417e5119dae773efd0000000000116ac000000000000000000002c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa0000001457cd18b7f8a4d91a2da9ab4af05d0fbece2dcd65DEADBEEF"; - ParsedPerChainQueryResponse memory r = ParsedPerChainQueryResponse({ - chainId: 1, - queryType: 5, - request: solanaPdaPerChainQueriesInner, - response: responseWithExtraBytes - }); - - vm.expectRevert(abi.encodeWithSelector(InvalidPayloadLength.selector, 159, 155)); - queryResponse.parseSolanaPdaQueryResponse(r); - } - - /*********************************** - *********** FUZZ TESTS ************* - ***********************************/ - + assertEq(r.nonce, _queryRequestNonce); + } + + function testFuzz_parseAndVerifyQueryResponse_fuzzNumPerChainQueriesAndResponses(uint8 _numPerChainQueries, uint8 _numPerChainResponses) public { + vm.assume(_numPerChainQueries != _numPerChainResponses); + + bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, _numPerChainQueries, perChainQueries, _numPerChainResponses, perChainResponses); + vm.expectRevert(); + wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); + } + + function testFuzz_parseAndVerifyQueryResponse_fuzzChainIds(uint16 _requestChainId, uint16 _responseChainId, uint256 _requestQueryType) public { + vm.assume(_requestChainId != _responseChainId); + _requestQueryType = bound(_requestQueryType, QueryType.min(), QueryType.max()); + + bytes memory packedPerChainQueries = abi.encodePacked(_requestChainId, uint8(_requestQueryType), uint32(perChainQueriesInner.length), perChainQueriesInner); + bytes memory packedPerChainResponses = abi.encodePacked(_responseChainId, uint8(_requestQueryType), uint32(perChainResponsesInner.length), perChainResponsesInner); + bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, packedPerChainQueries, numPerChainResponses, packedPerChainResponses); + vm.expectRevert(ChainIdMismatch.selector); + wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); + } + + function testFuzz_parseAndVerifyQueryResponse_fuzzMistmatchedRequestType(uint256 _requestQueryType, uint256 _responseQueryType) public { + _requestQueryType = bound(_requestQueryType, QueryType.min(), QueryType.max()); + _responseQueryType = bound(_responseQueryType, QueryType.min(), QueryType.max()); + vm.assume(_requestQueryType != _responseQueryType); + + bytes memory packedPerChainQueries = abi.encodePacked(uint16(0x0005), uint8(_requestQueryType), uint32(perChainQueriesInner.length), perChainQueriesInner); + bytes memory packedPerChainResponses = abi.encodePacked(uint16(0x0005), uint8(_responseQueryType), uint32(perChainResponsesInner.length), perChainResponsesInner); + bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, packedPerChainQueries, numPerChainResponses, packedPerChainResponses); + vm.expectRevert(RequestTypeMismatch.selector); + wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); + } + + function testFuzz_parseAndVerifyQueryResponse_fuzzUnsupportedRequestType(uint8 _requestQueryType) public { + vm.assume(!QueryType.isValid(_requestQueryType)); + + bytes memory packedPerChainQueries = abi.encodePacked(uint16(0x0005), uint8(_requestQueryType), uint32(perChainQueriesInner.length), perChainQueriesInner); + bytes memory packedPerChainResponses = abi.encodePacked(uint16(0x0005), uint8(_requestQueryType), uint32(perChainResponsesInner.length), perChainResponsesInner); + bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, packedPerChainQueries, numPerChainResponses, packedPerChainResponses); + vm.expectRevert(abi.encodeWithSelector(UnsupportedQueryType.selector, _requestQueryType)); + wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); + } + + function testFuzz_parseAndVerifyQueryResponse_fuzzQueryBytesLength(uint32 _queryLength) public { + vm.assume(_queryLength != uint32(perChainQueriesInner.length)); + + bytes memory packedPerChainQueries = abi.encodePacked(uint16(0x0005), uint8(0x01), _queryLength, perChainQueriesInner); + bytes memory packedPerChainResponses = abi.encodePacked(uint16(0x0005), uint8(0x01), uint32(perChainResponsesInner.length), perChainResponsesInner); + bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, packedPerChainQueries, numPerChainResponses, packedPerChainResponses); + vm.expectRevert(); + wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); + } + + function testFuzz_verifyQueryResponse_validSignature(bytes calldata resp) public view { + // This should pass with a valid signature of any payload + wrapper.verifyQueryResponse(wormhole, resp, getSignature(resp)); + } + + function testFuzz_verifyQueryResponse_invalidSignature(bytes calldata resp, uint256 privateKey) public { + vm.assume(privateKey != DEVNET_GUARDIAN_PRIVATE_KEY); + // Less than secp256k1 curve + vm.assume(privateKey < 115792089237316195423570985008687907852837564279074904382605163141518161494337); + vm.assume(privateKey != 0); + + (uint8 sigV, bytes32 sigR, bytes32 sigS) = vm.sign(privateKey, wrapper.calcPrefixedResponseHash(resp)); + IWormhole.Signature[] memory signatures = new IWormhole.Signature[](1); + signatures[0] = IWormhole.Signature(sigR, sigS, sigV, SIG_GUARDIAN_INDEX); + vm.expectRevert(VerificationFailed.selector); + wrapper.verifyQueryResponse(wormhole, resp, signatures); + } + + function testFuzz_verifyQueryResponse_validSignatureWrongPrefix(bytes calldata responsePrefix) public { + vm.assume(keccak256(responsePrefix) != keccak256(QueryResponseLib.RESPONSE_PREFIX)); + bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); + bytes32 responseDigest = keccak256(abi.encodePacked(responsePrefix, keccak256(resp))); + + (uint8 sigV, bytes32 sigR, bytes32 sigS) = vm.sign(DEVNET_GUARDIAN_PRIVATE_KEY, responseDigest); + IWormhole.Signature[] memory signatures = new IWormhole.Signature[](1); + signatures[0] = IWormhole.Signature(sigR, sigS, sigV, SIG_GUARDIAN_INDEX); + vm.expectRevert(VerificationFailed.selector); + wrapper.verifyQueryResponse(wormhole, resp, signatures); + } - function testFuzz_parseAndVerifyQueryResponse_fuzzVersion(uint8 _version) public { - vm.assume(_version != 1); - - bytes memory resp = concatenateQueryResponseBytesOffChain(_version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); - (uint8 sigV, bytes32 sigR, bytes32 sigS) = getSignature(resp); - IWormhole.Signature[] memory signatures = new IWormhole.Signature[](1); - signatures[0] = IWormhole.Signature({r: sigR, s: sigS, v: sigV, guardianIndex: sigGuardianIndex}); - vm.expectRevert(InvalidResponseVersion.selector); - queryResponse.parseAndVerifyQueryResponse(resp, signatures); - } - - function testFuzz_parseAndVerifyQueryResponse_fuzzSenderChainId(uint16 _senderChainId) public { - vm.assume(_senderChainId != 0); - - bytes memory resp = concatenateQueryResponseBytesOffChain(version, _senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); - (uint8 sigV, bytes32 sigR, bytes32 sigS) = getSignature(resp); - IWormhole.Signature[] memory signatures = new IWormhole.Signature[](1); - signatures[0] = IWormhole.Signature({r: sigR, s: sigS, v: sigV, guardianIndex: sigGuardianIndex}); - // This could revert for multiple reasons. But the checkLength to ensure all the bytes are consumed is the backstop. - vm.expectRevert(); - queryResponse.parseAndVerifyQueryResponse(resp, signatures); - } - - function testFuzz_parseAndVerifyQueryResponse_fuzzSignatureHappyCase(bytes memory _signature) public { - // This signature isn't validated in the QueryResponse library, therefore it could be an 65 byte hex string - vm.assume(_signature.length == 65); - - bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, _signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); - (uint8 sigV, bytes32 sigR, bytes32 sigS) = getSignature(resp); - IWormhole.Signature[] memory signatures = new IWormhole.Signature[](1); - signatures[0] = IWormhole.Signature({r: sigR, s: sigS, v: sigV, guardianIndex: sigGuardianIndex}); - ParsedQueryResponse memory r = queryResponse.parseAndVerifyQueryResponse(resp, signatures); - - assertEq(r.requestId, _signature); - } - - function testFuzz_parseAndVerifyQueryResponse_fuzzSignatureUnhappyCase(bytes memory _signature) public { - // A signature that isn't 65 bytes long will always lead to a revert. The type of revert is unknown since it could be one of many. - vm.assume(_signature.length != 65); - - bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, _signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); - (uint8 sigV, bytes32 sigR, bytes32 sigS) = getSignature(resp); - IWormhole.Signature[] memory signatures = new IWormhole.Signature[](1); - signatures[0] = IWormhole.Signature({r: sigR, s: sigS, v: sigV, guardianIndex: sigGuardianIndex}); - vm.expectRevert(); - queryResponse.parseAndVerifyQueryResponse(resp, signatures); - } - - function testFuzz_parseAndVerifyQueryResponse_fuzzQueryRequestLen(uint32 _queryRequestLen, bytes calldata _perChainQueries) public { - // We add 6 to account for version + nonce + numPerChainQueries - vm.assume(_queryRequestLen != _perChainQueries.length + 6); - - bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, _perChainQueries, numPerChainResponses, perChainResponses); - (uint8 sigV, bytes32 sigR, bytes32 sigS) = getSignature(resp); - IWormhole.Signature[] memory signatures = new IWormhole.Signature[](1); - signatures[0] = IWormhole.Signature({r: sigR, s: sigS, v: sigV, guardianIndex: sigGuardianIndex}); - vm.expectRevert(); - queryResponse.parseAndVerifyQueryResponse(resp, signatures); - } - - function testFuzz_parseAndVerifyQueryResponse_fuzzQueryRequestVersion(uint8 _version, uint8 _queryRequestVersion) public { - vm.assume(_version != _queryRequestVersion); - - bytes memory resp = concatenateQueryResponseBytesOffChain(_version, senderChainId, signature, _queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); - (uint8 sigV, bytes32 sigR, bytes32 sigS) = getSignature(resp); - IWormhole.Signature[] memory signatures = new IWormhole.Signature[](1); - signatures[0] = IWormhole.Signature({r: sigR, s: sigS, v: sigV, guardianIndex: sigGuardianIndex}); - vm.expectRevert(); - queryResponse.parseAndVerifyQueryResponse(resp, signatures); - } - - function testFuzz_parseAndVerifyQueryResponse_fuzzQueryRequestNonce(uint32 _queryRequestNonce) public { - bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, _queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); - (uint8 sigV, bytes32 sigR, bytes32 sigS) = getSignature(resp); - IWormhole.Signature[] memory signatures = new IWormhole.Signature[](1); - signatures[0] = IWormhole.Signature({r: sigR, s: sigS, v: sigV, guardianIndex: sigGuardianIndex}); - ParsedQueryResponse memory r = queryResponse.parseAndVerifyQueryResponse(resp, signatures); - - assertEq(r.nonce, _queryRequestNonce); - } - - function testFuzz_parseAndVerifyQueryResponse_fuzzNumPerChainQueriesAndResponses(uint8 _numPerChainQueries, uint8 _numPerChainResponses) public { - vm.assume(_numPerChainQueries != _numPerChainResponses); - - bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, _numPerChainQueries, perChainQueries, _numPerChainResponses, perChainResponses); - (uint8 sigV, bytes32 sigR, bytes32 sigS) = getSignature(resp); - IWormhole.Signature[] memory signatures = new IWormhole.Signature[](1); - signatures[0] = IWormhole.Signature({r: sigR, s: sigS, v: sigV, guardianIndex: sigGuardianIndex}); - vm.expectRevert(); - queryResponse.parseAndVerifyQueryResponse(resp, signatures); - } - - function testFuzz_parseAndVerifyQueryResponse_fuzzChainIds(uint16 _requestChainId, uint16 _responseChainId, uint256 _requestQueryType) public { - vm.assume(_requestChainId != _responseChainId); - _requestQueryType = bound({x: _requestQueryType, min: queryResponse.QT_ETH_CALL(), max: queryResponse.QT_MAX() - 1}); - - bytes memory packedPerChainQueries = abi.encodePacked(_requestChainId, uint8(_requestQueryType), uint32(perChainQueriesInner.length), perChainQueriesInner); - bytes memory packedPerChainResponses = abi.encodePacked(_responseChainId, uint8(_requestQueryType), uint32(perChainResponsesInner.length), perChainResponsesInner); - bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, packedPerChainQueries, numPerChainResponses, packedPerChainResponses); - (uint8 sigV, bytes32 sigR, bytes32 sigS) = getSignature(resp); - IWormhole.Signature[] memory signatures = new IWormhole.Signature[](1); - signatures[0] = IWormhole.Signature({r: sigR, s: sigS, v: sigV, guardianIndex: sigGuardianIndex}); - vm.expectRevert(ChainIdMismatch.selector); - queryResponse.parseAndVerifyQueryResponse(resp, signatures); - } - - function testFuzz_parseAndVerifyQueryResponse_fuzzMistmatchedRequestType(uint256 _requestQueryType, uint256 _responseQueryType) public { - _requestQueryType = bound({x: _requestQueryType, min: queryResponse.QT_ETH_CALL(), max: queryResponse.QT_MAX() - 1}); - _responseQueryType = bound({x: _responseQueryType, min: queryResponse.QT_ETH_CALL(), max: queryResponse.QT_MAX() - 1}); - vm.assume(_requestQueryType != _responseQueryType); - - bytes memory packedPerChainQueries = abi.encodePacked(uint16(0x0005), uint8(_requestQueryType), uint32(perChainQueriesInner.length), perChainQueriesInner); - bytes memory packedPerChainResponses = abi.encodePacked(uint16(0x0005), uint8(_responseQueryType), uint32(perChainResponsesInner.length), perChainResponsesInner); - bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, packedPerChainQueries, numPerChainResponses, packedPerChainResponses); - (uint8 sigV, bytes32 sigR, bytes32 sigS) = getSignature(resp); - IWormhole.Signature[] memory signatures = new IWormhole.Signature[](1); - signatures[0] = IWormhole.Signature({r: sigR, s: sigS, v: sigV, guardianIndex: sigGuardianIndex}); - vm.expectRevert(RequestTypeMismatch.selector); - queryResponse.parseAndVerifyQueryResponse(resp, signatures); - } - - function testFuzz_parseAndVerifyQueryResponse_fuzzUnsupportedRequestType(uint8 _requestQueryType) public { - vm.assume(_requestQueryType < queryResponse.QT_ETH_CALL() || _requestQueryType >= queryResponse.QT_MAX()); - - bytes memory packedPerChainQueries = abi.encodePacked(uint16(0x0005), uint8(_requestQueryType), uint32(perChainQueriesInner.length), perChainQueriesInner); - bytes memory packedPerChainResponses = abi.encodePacked(uint16(0x0005), uint8(_requestQueryType), uint32(perChainResponsesInner.length), perChainResponsesInner); - bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, packedPerChainQueries, numPerChainResponses, packedPerChainResponses); - (uint8 sigV, bytes32 sigR, bytes32 sigS) = getSignature(resp); - IWormhole.Signature[] memory signatures = new IWormhole.Signature[](1); - signatures[0] = IWormhole.Signature({r: sigR, s: sigS, v: sigV, guardianIndex: sigGuardianIndex}); - vm.expectRevert(abi.encodeWithSelector(UnsupportedQueryType.selector, _requestQueryType)); - queryResponse.parseAndVerifyQueryResponse(resp, signatures); - } - - function testFuzz_parseAndVerifyQueryResponse_fuzzQueryBytesLength(uint32 _queryLength) public { - vm.assume(_queryLength != uint32(perChainQueriesInner.length)); - - bytes memory packedPerChainQueries = abi.encodePacked(uint16(0x0005), uint8(0x01), _queryLength, perChainQueriesInner); - bytes memory packedPerChainResponses = abi.encodePacked(uint16(0x0005), uint8(0x01), uint32(perChainResponsesInner.length), perChainResponsesInner); - bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, packedPerChainQueries, numPerChainResponses, packedPerChainResponses); - (uint8 sigV, bytes32 sigR, bytes32 sigS) = getSignature(resp); - IWormhole.Signature[] memory signatures = new IWormhole.Signature[](1); - signatures[0] = IWormhole.Signature({r: sigR, s: sigS, v: sigV, guardianIndex: sigGuardianIndex}); - vm.expectRevert(); - queryResponse.parseAndVerifyQueryResponse(resp, signatures); - } - - function testFuzz_verifyQueryResponseSignatures_validSignature(bytes calldata resp) public view { - // This should pass with a valid signature of any payload - (uint8 sigV, bytes32 sigR, bytes32 sigS) = getSignature(resp); - IWormhole.Signature[] memory signatures = new IWormhole.Signature[](1); - signatures[0] = IWormhole.Signature({r: sigR, s: sigS, v: sigV, guardianIndex: sigGuardianIndex}); - queryResponse.verifyQueryResponseSignatures(resp, signatures); - } - - function testFuzz_verifyQueryResponseSignatures_invalidSignature(bytes calldata resp, uint256 privateKey) public { - vm.assume(privateKey != DEVNET_GUARDIAN_PRIVATE_KEY); - // Less than secp256k1 curve - vm.assume(privateKey < 115792089237316195423570985008687907852837564279074904382605163141518161494337); - vm.assume(privateKey != 0); - - (uint8 sigV, bytes32 sigR, bytes32 sigS) = vm.sign(privateKey, queryResponse.getResponseDigest(resp)); - IWormhole.Signature[] memory signatures = new IWormhole.Signature[](1); - signatures[0] = IWormhole.Signature({r: sigR, s: sigS, v: sigV, guardianIndex: sigGuardianIndex}); - vm.expectRevert("VM signature invalid"); - queryResponse.verifyQueryResponseSignatures(resp, signatures); - } - - function testFuzz_verifyQueryResponseSignatures_validSignatureWrongPrefix(bytes calldata responsePrefix) public { - vm.assume(keccak256(responsePrefix) != keccak256(queryResponse.responsePrefix())); - - bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); - bytes32 responseDigest = keccak256(abi.encodePacked(responsePrefix, keccak256(resp))); - - (uint8 sigV, bytes32 sigR, bytes32 sigS) = vm.sign(DEVNET_GUARDIAN_PRIVATE_KEY, responseDigest); - IWormhole.Signature[] memory signatures = new IWormhole.Signature[](1); - signatures[0] = IWormhole.Signature({r: sigR, s: sigS, v: sigV, guardianIndex: sigGuardianIndex}); - vm.expectRevert("VM signature invalid"); - queryResponse.verifyQueryResponseSignatures(resp, signatures); - } - - uint64 constant private SECOND_RESOLUTION = 1_000_000; - uint64 constant private MAX_SECONDS = type(uint64).max/SECOND_RESOLUTION; - - function testFuzz_validateBlockTime_success(uint64 _blockTime, uint64 _minBlockTime) public view { - //assure: blockTime >= minBlockTime - _minBlockTime = uint64(bound(_minBlockTime, 0, MAX_SECONDS)); - _blockTime = uint64(bound(_blockTime, _minBlockTime, MAX_SECONDS)); - - queryResponse.validateBlockTime(_blockTime * SECOND_RESOLUTION, _minBlockTime); - } - - function testFuzz_validateBlockTime_fail(uint64 _blockTime, uint256 _minBlockTime) public { - //assure: blockTime < minBlockTime - vm.assume(_minBlockTime > 0); - uint upperBound = _minBlockTime <= MAX_SECONDS ? _minBlockTime-1 : MAX_SECONDS; - _blockTime = uint64(bound(_blockTime, 0, upperBound)); - - vm.expectRevert(StaleBlockTime.selector); - queryResponse.validateBlockTime(_blockTime * SECOND_RESOLUTION, _minBlockTime); - } - - function testFuzz_validateBlockNum_success(uint64 _blockNum, uint64 _minBlockNum) public view { - //assure: blockNum >= minBlockNum - _blockNum = uint64(bound(_blockNum, _minBlockNum, type(uint64).max)); - - queryResponse.validateBlockNum(_blockNum, _minBlockNum); - } - - function testFuzz_validateBlockNum_fail(uint256 _blockNum, uint256 _minBlockNum) public { - //assure: blockNum < minBlockNum - vm.assume(_minBlockNum > 0); - _blockNum = uint64(bound(_blockNum, 0, _minBlockNum-1)); - - vm.expectRevert(StaleBlockNum.selector); - queryResponse.validateBlockNum(uint64(_blockNum), _minBlockNum); - } - - function testFuzz_validateChainId_success(uint256 _validChainIndex, uint16[] memory _validChainIds) public view { - vm.assume(_validChainIds.length > 0); - _validChainIndex %= _validChainIds.length; - - queryResponse.validateChainId(_validChainIds[_validChainIndex], _validChainIds); - } - - function testFuzz_validateChainId_fail(uint16 _chainId, uint16[] memory _validChainIds) public { - for (uint16 i = 0; i < _validChainIds.length; ++i) { - vm.assume(_chainId != _validChainIds[i]); - } - - vm.expectRevert(InvalidChainId.selector); - queryResponse.validateChainId(_chainId, _validChainIds); - } - - function testFuzz_validateEthCallData_success(bytes memory randomBytes, uint256 _contractAddressIndex, uint256 _functionSignatureIndex, address[] memory _expectedContractAddresses, bytes4[] memory _expectedFunctionSignatures) public view { - vm.assume(_expectedContractAddresses.length > 0); - _contractAddressIndex %= _expectedContractAddresses.length; - vm.assume(_expectedFunctionSignatures.length > 0); - _functionSignatureIndex %= _expectedFunctionSignatures.length; - - EthCallData memory callData = EthCallData({ - contractAddress: _expectedContractAddresses[_contractAddressIndex], - callData: bytes.concat(_expectedFunctionSignatures[_functionSignatureIndex], randomBytes), - result: randomBytes - }); - - queryResponse.validateEthCallData(callData, _expectedContractAddresses, _expectedFunctionSignatures); - } - - function testFuzz_validateEthCallData_successZeroSignatures(bytes4 randomSignature, bytes memory randomBytes, uint256 _contractAddressIndex, address[] memory _expectedContractAddresses) public view { - vm.assume(_expectedContractAddresses.length > 0); - _contractAddressIndex %= _expectedContractAddresses.length; - - EthCallData memory callData = EthCallData({ - contractAddress: _expectedContractAddresses[_contractAddressIndex], - callData: bytes.concat(randomSignature, randomBytes), - result: randomBytes - }); + uint64 constant private MICROSECONDS_PER_SECOND = QueryResponseLib.MICROSECONDS_PER_SECOND; + uint64 constant private MAX_SECONDS = type(uint64).max/MICROSECONDS_PER_SECOND; + + function testFuzz_validateBlockTime_success(uint64 _blockTime, uint64 _minBlockTime) public view { + //assure: blockTime >= minBlockTime + _minBlockTime = uint64(bound(_minBlockTime, 0, MAX_SECONDS)); + _blockTime = uint64(bound(_blockTime, _minBlockTime, MAX_SECONDS)); + + wrapper.validateBlockTime(_blockTime * MICROSECONDS_PER_SECOND, _minBlockTime); + } - bytes4[] memory validSignatures = new bytes4[](0); + function testFuzz_validateBlockTime_fail(uint64 _blockTime, uint256 _minBlockTime) public { + //assure: blockTime < minBlockTime + vm.assume(_minBlockTime > 0); + uint upperBound = _minBlockTime <= MAX_SECONDS ? _minBlockTime-1 : MAX_SECONDS; + _blockTime = uint64(bound(_blockTime, 0, upperBound)); + + vm.expectRevert(StaleBlockTime.selector); + wrapper.validateBlockTime(_blockTime * MICROSECONDS_PER_SECOND, _minBlockTime); + } + + function testFuzz_validateBlockNum_success(uint64 _blockNum, uint64 _minBlockNum) public view { + //assure: blockNum >= minBlockNum + _blockNum = uint64(bound(_blockNum, _minBlockNum, type(uint64).max)); + + wrapper.validateBlockNum(_blockNum, _minBlockNum); + } - queryResponse.validateEthCallData(callData, _expectedContractAddresses, validSignatures); + function testFuzz_validateBlockNum_fail(uint256 _blockNum, uint256 _minBlockNum) public { + //assure: blockNum < minBlockNum + vm.assume(_minBlockNum > 0); + _blockNum = uint64(bound(_blockNum, 0, _minBlockNum-1)); + + vm.expectRevert(StaleBlockNum.selector); + wrapper.validateBlockNum(uint64(_blockNum), _minBlockNum); + } + + function testFuzz_validateChainId_success(uint256 _validChainIndex, uint16[] memory _validChainIds) public view { + vm.assume(_validChainIds.length > 0); + _validChainIndex %= _validChainIds.length; + + wrapper.validateChainId(_validChainIds[_validChainIndex], _validChainIds); + } + + function testFuzz_validateChainId_fail(uint16 _chainId, uint16[] memory _validChainIds) public { + for (uint16 i = 0; i < _validChainIds.length; ++i) { + vm.assume(_chainId != _validChainIds[i]); } - function testFuzz_validateEthCallData_successZeroAddresses(address randomAddress, bytes memory randomBytes, uint256 _functionSignatureIndex, bytes4[] memory _expectedFunctionSignatures) public view { - vm.assume(_expectedFunctionSignatures.length > 0); - _functionSignatureIndex %= _expectedFunctionSignatures.length; + vm.expectRevert(InvalidChainId.selector); + wrapper.validateChainId(_chainId, _validChainIds); + } + + function testFuzz_validateEthCallRecord_success(bytes memory randomBytes, uint256 _contractAddressIndex, uint256 _functionSignatureIndex, address[] memory _validContractAddresses, bytes4[] memory _validFunctionSignatures) public view { + vm.assume(randomBytes.length >= 4); + vm.assume(_validContractAddresses.length > 0); + _contractAddressIndex %= _validContractAddresses.length; + vm.assume(_validFunctionSignatures.length > 0); + _functionSignatureIndex %= _validFunctionSignatures.length; + + EthCallRecord memory callData = EthCallRecord({ + contractAddress: _validContractAddresses[_contractAddressIndex], + callData: bytes.concat(_validFunctionSignatures[_functionSignatureIndex], randomBytes), + result: randomBytes + }); + + wrapper.validateEthCallRecord(callData, _validContractAddresses, _validFunctionSignatures); + } + + function testFuzz_validateEthCallRecord_successZeroSignatures(bytes4 randomSignature, bytes memory randomBytes, uint256 _contractAddressIndex, address[] memory _validContractAddresses) public view { + vm.assume(_validContractAddresses.length > 0); + _contractAddressIndex %= _validContractAddresses.length; + + EthCallRecord memory callData = EthCallRecord({ + contractAddress: _validContractAddresses[_contractAddressIndex], + callData: bytes.concat(randomSignature, randomBytes), + result: randomBytes + }); + + bytes4[] memory validSignatures = new bytes4[](0); + + wrapper.validateEthCallRecord(callData, _validContractAddresses, validSignatures); + } + + function testFuzz_validateEthCallRecord_successZeroAddresses(address randomAddress, bytes memory randomBytes, uint256 _functionSignatureIndex, bytes4[] memory _validFunctionSignatures) public view { + vm.assume(randomBytes.length >= 4); + vm.assume(_validFunctionSignatures.length > 0); + _functionSignatureIndex %= _validFunctionSignatures.length; + + EthCallRecord memory callData = EthCallRecord({ + contractAddress: randomAddress, + callData: bytes.concat(_validFunctionSignatures[_functionSignatureIndex], randomBytes), + result: randomBytes + }); + + address[] memory validAddresses = new address[](0); - EthCallData memory callData = EthCallData({ - contractAddress: randomAddress, - callData: bytes.concat(_expectedFunctionSignatures[_functionSignatureIndex], randomBytes), - result: randomBytes - }); + wrapper.validateEthCallRecord(callData, validAddresses, _validFunctionSignatures); + } - address[] memory validAddresses = new address[](0); + function testFuzz_validateEthCallRecord_failSignature(bytes memory randomBytes, uint256 _contractAddressIndex, address[] memory _validContractAddresses, bytes4[] memory _validFunctionSignatures) public { + vm.assume(randomBytes.length >= 4); + vm.assume(_validContractAddresses.length > 0); + _contractAddressIndex %= _validContractAddresses.length; + vm.assume(_validFunctionSignatures.length > 0); - queryResponse.validateEthCallData(callData, validAddresses, _expectedFunctionSignatures); + for (uint256 i = 0; i < _validFunctionSignatures.length; ++i) { + vm.assume(bytes4(randomBytes) != _validFunctionSignatures[i]); } - function testFuzz_validateEthCallData_failSignature(bytes memory randomBytes, uint256 _contractAddressIndex, address[] memory _expectedContractAddresses, bytes4[] memory _expectedFunctionSignatures) public { - vm.assume(_expectedContractAddresses.length > 0); - _contractAddressIndex %= _expectedContractAddresses.length; - vm.assume(_expectedFunctionSignatures.length > 0); + EthCallRecord memory callData = EthCallRecord({ + contractAddress: _validContractAddresses[_contractAddressIndex], + callData: randomBytes, + result: randomBytes + }); - for (uint256 i = 0; i < _expectedFunctionSignatures.length; ++i) { - vm.assume(bytes4(randomBytes) != _expectedFunctionSignatures[i]); - } + vm.expectRevert(InvalidFunctionSignature.selector); + wrapper.validateEthCallRecord(callData, _validContractAddresses, _validFunctionSignatures); + } - EthCallData memory callData = EthCallData({ - contractAddress: _expectedContractAddresses[_contractAddressIndex], - callData: randomBytes, - result: randomBytes - }); + function testFuzz_validateEthCallRecord_failAddress(bytes memory randomBytes, address randomAddress, uint256 _functionSignatureIndex, address[] memory _validContractAddresses, bytes4[] memory _validFunctionSignatures) public { + vm.assume(_validFunctionSignatures.length > 0); + _functionSignatureIndex %= _validFunctionSignatures.length; + vm.assume(_validContractAddresses.length > 0); - vm.expectRevert(InvalidFunctionSignature.selector); - queryResponse.validateEthCallData(callData, _expectedContractAddresses, _expectedFunctionSignatures); + for (uint256 i = 0; i < _validContractAddresses.length; ++i) { + vm.assume(randomAddress != _validContractAddresses[i]); } - function testFuzz_validateEthCallData_failAddress(bytes memory randomBytes, address randomAddress, uint256 _functionSignatureIndex, address[] memory _expectedContractAddresses, bytes4[] memory _expectedFunctionSignatures) public { - vm.assume(_expectedFunctionSignatures.length > 0); - _functionSignatureIndex %= _expectedFunctionSignatures.length; - vm.assume(_expectedContractAddresses.length > 0); + EthCallRecord memory callData = EthCallRecord({ + contractAddress: randomAddress, + callData: bytes.concat(_validFunctionSignatures[_functionSignatureIndex], randomBytes), + result: randomBytes + }); - for (uint256 i = 0; i < _expectedContractAddresses.length; ++i) { - vm.assume(randomAddress != _expectedContractAddresses[i]); - } + vm.expectRevert(InvalidContractAddress.selector); + wrapper.validateEthCallRecord(callData, _validContractAddresses, _validFunctionSignatures); + } - EthCallData memory callData = EthCallData({ - contractAddress: randomAddress, - callData: bytes.concat(_expectedFunctionSignatures[_functionSignatureIndex], randomBytes), - result: randomBytes - }); + function testFuzz_validateMultipleEthCallRecord_success(uint8 numInputs, bytes memory randomBytes, uint256 _contractAddressIndex, uint256 _functionSignatureIndex, address[] memory _validContractAddresses, bytes4[] memory _validFunctionSignatures) public view { + vm.assume(_validContractAddresses.length > 0); + _contractAddressIndex %= _validContractAddresses.length; + vm.assume(_validFunctionSignatures.length > 0); + _functionSignatureIndex %= _validFunctionSignatures.length; - vm.expectRevert(InvalidContractAddress.selector); - queryResponse.validateEthCallData(callData, _expectedContractAddresses, _expectedFunctionSignatures); - } + EthCallRecord[] memory callDatas = new EthCallRecord[](numInputs); - function testFuzz_validateMultipleEthCallData_success(uint8 numInputs, bytes memory randomBytes, uint256 _contractAddressIndex, uint256 _functionSignatureIndex, address[] memory _expectedContractAddresses, bytes4[] memory _expectedFunctionSignatures) public view { - vm.assume(_expectedContractAddresses.length > 0); - _contractAddressIndex %= _expectedContractAddresses.length; - vm.assume(_expectedFunctionSignatures.length > 0); - _functionSignatureIndex %= _expectedFunctionSignatures.length; + for (uint256 i = 0; i < numInputs; ++i) { + callDatas[i] = EthCallRecord({ + contractAddress: _validContractAddresses[_contractAddressIndex], + callData: bytes.concat(_validFunctionSignatures[_functionSignatureIndex], randomBytes), + result: randomBytes + }); + } - EthCallData[] memory callDatas = new EthCallData[](numInputs); - - for (uint256 i = 0; i < numInputs; ++i) { - callDatas[i] = EthCallData({ - contractAddress: _expectedContractAddresses[_contractAddressIndex], - callData: bytes.concat(_expectedFunctionSignatures[_functionSignatureIndex], randomBytes), - result: randomBytes - }); - } - - queryResponse.validateMultipleEthCallData(callDatas, _expectedContractAddresses, _expectedFunctionSignatures); - } + wrapper.validateEthCallRecord(callDatas, _validContractAddresses, _validFunctionSignatures); + } } diff --git a/test/QueryTest.t.sol b/test/QueryTest.t.sol index 1d63ac6..91d6965 100644 --- a/test/QueryTest.t.sol +++ b/test/QueryTest.t.sol @@ -1,292 +1,284 @@ // SPDX-License-Identifier: Apache 2 -// forge test --match-contract QueryTest - pragma solidity ^0.8.4; import "forge-std/Test.sol"; -import "../src/testing/helpers/QueryTest.sol"; - -contract TestQueryTest is Test { - // - // Query Request tests - // - - function test_buildOffChainQueryRequestBytes() public { - bytes memory req = QueryTest.buildOffChainQueryRequestBytes( - /* version */ 1, - /* nonce */ 1, - /* numPerChainQueries */ 1, - /* perChainQueries */ hex"0002010000004200000005307837343402ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567" - ); - assertEq(req, hex"0100000001010002010000004200000005307837343402ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567"); - } - - function test_buildPerChainRequestBytes() public { - bytes memory pcr = QueryTest.buildPerChainRequestBytes( - /* chainId */ 2, - /* queryType */ 1, - /* queryBytes */ hex"00000005307837343402ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567" - ); - assertEq(pcr, hex"0002010000004200000005307837343402ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567"); - } - - function test_buildEthCallRequestBytes() public { - bytes memory ecr = QueryTest.buildEthCallRequestBytes( - /* blockId */ "0x744", - /* numCallData */ 2, - /* callData */ hex"ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567" - ); - assertEq(ecr, hex"00000005307837343402ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567"); - } - - function test_buildEthCallByTimestampRequestBytes() public { - bytes memory ecr = QueryTest.buildEthCallByTimestampRequestBytes( - /* targetTimeUs */ 0x10642ac0, - /* targetBlockHint */ "0x15d", - /* followingBlockHint */ "0x15e", - /* numCallData */ 2, - /* callData */ hex"ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567" - ); - assertEq(ecr, hex"0000000010642ac000000005307831356400000005307831356502ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567"); - } +import "wormhole-sdk/testing/helpers/QueryTest.sol"; + +contract QueryRequestTest is Test { + function test_buildOffChainQueryRequestBytes() public { + bytes memory req = QueryTest.buildOffChainQueryRequestBytes( + /* version */ 1, + /* nonce */ 1, + /* numPerChainQueries */ 1, + /* perChainQueries */ hex"0002010000004200000005307837343402ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567" + ); + assertEq(req, hex"0100000001010002010000004200000005307837343402ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567"); + } + + function test_buildPerChainRequestBytes() public { + bytes memory pcr = QueryTest.buildPerChainRequestBytes( + /* chainId */ 2, + /* queryType */ 1, + /* queryBytes */ hex"00000005307837343402ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567" + ); + assertEq(pcr, hex"0002010000004200000005307837343402ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567"); + } + + function test_buildEthCallRequestBytes() public { + bytes memory ecr = QueryTest.buildEthCallRequestBytes( + /* blockId */ "0x744", + /* numCallData */ 2, + /* callData */ hex"ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567" + ); + assertEq(ecr, hex"00000005307837343402ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567"); + } + + function test_buildEthCallByTimestampRequestBytes() public { + bytes memory ecr = QueryTest.buildEthCallByTimestampRequestBytes( + /* targetTimeUs */ 0x10642ac0, + /* targetBlockHint */ "0x15d", + /* followingBlockHint */ "0x15e", + /* numCallData */ 2, + /* callData */ hex"ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567" + ); + assertEq(ecr, hex"0000000010642ac000000005307831356400000005307831356502ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567"); + } + + function test_buildEthCallWithFinalityRequestBytes() public { + bytes memory ecr = QueryTest.buildEthCallWithFinalityRequestBytes( + /* blockId */ "0x1f8", + /* finality */ "finalized", + /* numCallData */ 2, + /* callData */ hex"ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567" + ); + assertEq(ecr, hex"0000000530783166380000000966696e616c697a656402ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567"); + } + + function test_buildEthCallRecordBytes() public { + bytes memory ecd1 = QueryTest.buildEthCallRecordBytes( + /* contractAddress */ 0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E, + /* callData */ hex"06fdde03" + ); + assertEq(ecd1, hex"ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03"); + bytes memory ecd2 = QueryTest.buildEthCallRecordBytes( + /* contractAddress */ 0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E, + /* callData */ hex"313ce567" + ); + assertEq(ecd2, hex"ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567"); + } - function test_buildEthCallWithFinalityRequestBytes() public { - bytes memory ecr = QueryTest.buildEthCallWithFinalityRequestBytes( - /* blockId */ "0x1f8", - /* finality */ "finalized", - /* numCallData */ 2, - /* callData */ hex"ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567" - ); - assertEq(ecr, hex"0000000530783166380000000966696e616c697a656402ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567"); + function test_buildSolanaAccountRequestBytes() public { + bytes memory ecr = QueryTest.buildSolanaAccountRequestBytes( + /* commitment */ "finalized", + /* minContextSlot */ 8069, + /* dataSliceOffset */ 10, + /* dataSliceLength */ 20, + /* numAccounts */ 2, + /* accounts */ hex"165809739240a0ac03b98440fe8985548e3aa683cd0d4d9df5b5659669faa3019c006c48c8cbf33849cb07a3f936159cc523f9591cb1999abd45890ec5fee9b7" + ); + assertEq(ecr, hex"0000000966696e616c697a65640000000000001f85000000000000000a000000000000001402165809739240a0ac03b98440fe8985548e3aa683cd0d4d9df5b5659669faa3019c006c48c8cbf33849cb07a3f936159cc523f9591cb1999abd45890ec5fee9b7"); + } + + function test_buildSolanaPdaRequestBytes() public { + bytes32 programId = hex"02c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa"; + bytes[] memory pdas = new bytes[](2); + + bytes[] memory seeds = new bytes[](2); + seeds[0] = hex"477561726469616e536574"; + seeds[1] = hex"00000000"; + (bytes memory seedBytes, uint8 numSeeds) = QueryTest.buildSolanaPdaSeedBytes(seeds); + assertEq(seedBytes, hex"0000000b477561726469616e5365740000000400000000"); + + pdas[0] = QueryTest.buildSolanaPdaEntry( + programId, + numSeeds, + seedBytes + ); + assertEq(pdas[0], hex"02c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa020000000b477561726469616e5365740000000400000000"); + assertEq(numSeeds, uint8(seeds.length)); + + bytes[] memory seeds2 = new bytes[](2); + seeds2[0] = hex"477561726469616e536574"; + seeds2[1] = hex"00000001"; + (bytes memory seedBytes2, uint8 numSeeds2) = QueryTest.buildSolanaPdaSeedBytes(seeds2); + assertEq(seedBytes2, hex"0000000b477561726469616e5365740000000400000001"); + + pdas[1] = QueryTest.buildSolanaPdaEntry( + programId, + numSeeds2, + seedBytes2 + ); + assertEq(pdas[1], hex"02c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa020000000b477561726469616e5365740000000400000001"); + assertEq(numSeeds2, uint8(seeds2.length)); + + bytes memory ecr = QueryTest.buildSolanaPdaRequestBytes( + /* commitment */ "finalized", + /* minContextSlot */ 2303, + /* dataSliceOffset */ 12, + /* dataSliceLength */ 20, + /* pdas */ pdas + ); + assertEq(ecr, hex"0000000966696e616c697a656400000000000008ff000000000000000c00000000000000140202c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa020000000b477561726469616e536574000000040000000002c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa020000000b477561726469616e5365740000000400000001"); + } + + function test_buildSolanaPdaRequestBytesTooManyPDAs() public { + bytes32 programId = hex"02c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa"; + bytes[] memory pdas = new bytes[](256); + + uint numPDAs = pdas.length; + for (uint idx; idx < numPDAs;) { + bytes[] memory seeds = new bytes[](2); + seeds[0] = hex"477561726469616e536574"; + seeds[1] = hex"00000000"; + (bytes memory seedBytes, uint8 numSeeds) = QueryTest.buildSolanaPdaSeedBytes(seeds); + + pdas[idx] = QueryTest.buildSolanaPdaEntry( + programId, + numSeeds, + seedBytes + ); + + unchecked { ++idx; } } - function test_buildEthCallDataBytes() public { - bytes memory ecd1 = QueryTest.buildEthCallDataBytes( - /* contractAddress */ 0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E, - /* callData */ hex"06fdde03" - ); - assertEq(ecd1, hex"ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03"); - bytes memory ecd2 = QueryTest.buildEthCallDataBytes( - /* contractAddress */ 0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E, - /* callData */ hex"313ce567" - ); - assertEq(ecd2, hex"ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567"); - } - - function test_buildSolanaAccountRequestBytes() public { - bytes memory ecr = QueryTest.buildSolanaAccountRequestBytes( - /* commitment */ "finalized", - /* minContextSlot */ 8069, - /* dataSliceOffset */ 10, - /* dataSliceLength */ 20, - /* numAccounts */ 2, - /* accounts */ hex"165809739240a0ac03b98440fe8985548e3aa683cd0d4d9df5b5659669faa3019c006c48c8cbf33849cb07a3f936159cc523f9591cb1999abd45890ec5fee9b7" - ); - assertEq(ecr, hex"0000000966696e616c697a65640000000000001f85000000000000000a000000000000001402165809739240a0ac03b98440fe8985548e3aa683cd0d4d9df5b5659669faa3019c006c48c8cbf33849cb07a3f936159cc523f9591cb1999abd45890ec5fee9b7"); + vm.expectRevert(QueryTest.SolanaTooManyPDAs.selector); + QueryTest.buildSolanaPdaRequestBytes( + /* commitment */ "finalized", + /* minContextSlot */ 2303, + /* dataSliceOffset */ 12, + /* dataSliceLength */ 20, + /* pdas */ pdas + ); + } + + function test_buildSolanaPdaEntryTooManySeeds() public { + bytes[] memory seeds = new bytes[](2); + seeds[0] = hex"477561726469616e536574"; + seeds[1] = hex"00000000"; + (bytes memory seedBytes,) = QueryTest.buildSolanaPdaSeedBytes(seeds); + assertEq(seedBytes, hex"0000000b477561726469616e5365740000000400000000"); + + bytes32 programId = hex"02c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa"; + + vm.expectRevert(QueryTest.SolanaTooManySeeds.selector); + QueryTest.buildSolanaPdaEntry( + programId, + uint8(QueryTest.SOLANA_MAX_SEEDS + 1), + seedBytes + ); + } + + function test_buildSolanaPdaSeedBytesTooManySeeds() public { + bytes[] memory seeds = new bytes[](QueryTest.SOLANA_MAX_SEEDS + 1); + uint numSeeds = seeds.length; + for (uint idx; idx < numSeeds;) { + seeds[idx] = "junk"; + unchecked { ++idx; } } - function test_buildSolanaPdaRequestBytes() public { - bytes32 programId = hex"02c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa"; - bytes[] memory pdas = new bytes[](2); - - bytes[] memory seeds = new bytes[](2); - seeds[0] = hex"477561726469616e536574"; - seeds[1] = hex"00000000"; - (bytes memory seedBytes, uint8 numSeeds) = QueryTest.buildSolanaPdaSeedBytes(seeds); - assertEq(seedBytes, hex"0000000b477561726469616e5365740000000400000000"); - - pdas[0] = QueryTest.buildSolanaPdaEntry( - programId, - numSeeds, - seedBytes - ); - assertEq(pdas[0], hex"02c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa020000000b477561726469616e5365740000000400000000"); - assertEq(numSeeds, uint8(seeds.length)); - - bytes[] memory seeds2 = new bytes[](2); - seeds2[0] = hex"477561726469616e536574"; - seeds2[1] = hex"00000001"; - (bytes memory seedBytes2, uint8 numSeeds2) = QueryTest.buildSolanaPdaSeedBytes(seeds2); - assertEq(seedBytes2, hex"0000000b477561726469616e5365740000000400000001"); - - pdas[1] = QueryTest.buildSolanaPdaEntry( - programId, - numSeeds2, - seedBytes2 - ); - assertEq(pdas[1], hex"02c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa020000000b477561726469616e5365740000000400000001"); - assertEq(numSeeds2, uint8(seeds2.length)); - - bytes memory ecr = QueryTest.buildSolanaPdaRequestBytes( - /* commitment */ "finalized", - /* minContextSlot */ 2303, - /* dataSliceOffset */ 12, - /* dataSliceLength */ 20, - /* pdas */ pdas - ); - assertEq(ecr, hex"0000000966696e616c697a656400000000000008ff000000000000000c00000000000000140202c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa020000000b477561726469616e536574000000040000000002c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa020000000b477561726469616e5365740000000400000001"); - } + vm.expectRevert(QueryTest.SolanaTooManySeeds.selector); + QueryTest.buildSolanaPdaSeedBytes(seeds); + } - function test_buildSolanaPdaRequestBytesTooManyPDAs() public { - bytes32 programId = hex"02c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa"; - bytes[] memory pdas = new bytes[](256); - - uint numPDAs = pdas.length; - for (uint idx; idx < numPDAs;) { - bytes[] memory seeds = new bytes[](2); - seeds[0] = hex"477561726469616e536574"; - seeds[1] = hex"00000000"; - (bytes memory seedBytes, uint8 numSeeds) = QueryTest.buildSolanaPdaSeedBytes(seeds); - - pdas[idx] = QueryTest.buildSolanaPdaEntry( - programId, - numSeeds, - seedBytes - ); - - unchecked { ++idx; } - } - - vm.expectRevert(QueryTest.SolanaTooManyPDAs.selector); - QueryTest.buildSolanaPdaRequestBytes( - /* commitment */ "finalized", - /* minContextSlot */ 2303, - /* dataSliceOffset */ 12, - /* dataSliceLength */ 20, - /* pdas */ pdas - ); - } - - function test_buildSolanaPdaEntryTooManySeeds() public { - bytes[] memory seeds = new bytes[](2); - seeds[0] = hex"477561726469616e536574"; - seeds[1] = hex"00000000"; - (bytes memory seedBytes,) = QueryTest.buildSolanaPdaSeedBytes(seeds); - assertEq(seedBytes, hex"0000000b477561726469616e5365740000000400000000"); - - bytes32 programId = hex"02c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa"; - - vm.expectRevert(QueryTest.SolanaTooManySeeds.selector); - QueryTest.buildSolanaPdaEntry( - programId, - uint8(QueryTest.SolanaMaxSeeds + 1), - seedBytes - ); - } - - function test_buildSolanaPdaSeedBytesTooManySeeds() public { - bytes[] memory seeds = new bytes[](QueryTest.SolanaMaxSeeds + 1); - uint numSeeds = seeds.length; - for (uint idx; idx < numSeeds;) { - seeds[idx] = "junk"; - unchecked { ++idx; } - } + function test_buildSolanaPdaSeedBytesSeedTooLong() public { + bytes[] memory seeds = new bytes[](2); + seeds[0] = "junk"; + seeds[1] = "This seed is too long!!!!!!!!!!!!"; - vm.expectRevert(QueryTest.SolanaTooManySeeds.selector); - QueryTest.buildSolanaPdaSeedBytes(seeds); - } - - function test_buildSolanaPdaSeedBytesSeedTooLong() public { - bytes[] memory seeds = new bytes[](2); - seeds[0] = "junk"; - seeds[1] = "This seed is too long!!!!!!!!!!!!"; - - vm.expectRevert(QueryTest.SolanaSeedTooLong.selector); - QueryTest.buildSolanaPdaSeedBytes(seeds); - } - - // - // Query Response tests - // - - function test_buildQueryResponseBytes() public { - bytes memory resp = QueryTest.buildQueryResponseBytes( - /* version */ 1, - /* senderChainId */ 0, - /* signature */ hex"11b03bdbbe15a8f12b803d2193de5ddff72d92eaabd2763553ec3c3133182d1443719a05e2b65c87b923c6bd8aeff49f34937f90f3ab7cd33449388c60fa30a301", - /* queryRequest */ hex"0100000001010002010000004200000005307837343402ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567", - /* numPerChainResponses */ 1, - /* perChainResponses */ hex"000201000000b900000000000007446a0b819aee8945e659e37537a0bdbe03c06275be23e499819138d1eee8337e9b000000006ab13b8002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012" - ); - assertEq(resp, hex"01000011b03bdbbe15a8f12b803d2193de5ddff72d92eaabd2763553ec3c3133182d1443719a05e2b65c87b923c6bd8aeff49f34937f90f3ab7cd33449388c60fa30a3010000004f0100000001010002010000004200000005307837343402ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce56701000201000000b900000000000007446a0b819aee8945e659e37537a0bdbe03c06275be23e499819138d1eee8337e9b000000006ab13b8002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012"); - } - - function test_buildPerChainResponseBytes() public { - bytes memory pcr = QueryTest.buildPerChainResponseBytes( - /* chainId */ 2, - /* queryType */ 1, - /* responseBytes */ hex"00000000000007446a0b819aee8945e659e37537a0bdbe03c06275be23e499819138d1eee8337e9b000000006ab13b8002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012" - ); - assertEq(pcr, hex"000201000000b900000000000007446a0b819aee8945e659e37537a0bdbe03c06275be23e499819138d1eee8337e9b000000006ab13b8002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012"); - } - - function test_buildEthCallResponseBytes() public { - bytes memory ecr = QueryTest.buildEthCallResponseBytes( - /* blockNumber */ 1860, - /* blockHash */ hex"6a0b819aee8945e659e37537a0bdbe03c06275be23e499819138d1eee8337e9b", - /* blockTimeUs */ 0x6ab13b80, - /* numResults */ 2, - /* results */ hex"000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012" - ); - assertEq(ecr, hex"00000000000007446a0b819aee8945e659e37537a0bdbe03c06275be23e499819138d1eee8337e9b000000006ab13b8002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012"); - } - - function test_buildEthCallByTimestampResponseBytes() public { - bytes memory ecr = QueryTest.buildEthCallByTimestampResponseBytes( - /* targetBlockNumber */ 349, - /* targetBlockHash */ hex"966cd846f812be43c4ee2d310f962bc592ba944c66de878e53584b8e75c6051f", - /* targetBlockTimeUs */ 0x10642ac0, - /* followingBlockNumber */ 350, - /* followingBlockHash */ hex"04b022afaab8da2dd80bd8e6ae55e6303473a5e1de846a5de76d619e162429ce", - /* followingBlockTimeUs */ 0x10736d00, - /* numResults */ 2, - /* results */ hex"000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012" - ); - assertEq(ecr, hex"000000000000015d966cd846f812be43c4ee2d310f962bc592ba944c66de878e53584b8e75c6051f0000000010642ac0000000000000015e04b022afaab8da2dd80bd8e6ae55e6303473a5e1de846a5de76d619e162429ce0000000010736d0002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012"); - } - - function test_buildEthCallWithFinalityResponseBytes() public { - bytes memory ecr = QueryTest.buildEthCallWithFinalityResponseBytes( - /* blockNumber */ 1860, - /* blockHash */ hex"6a0b819aee8945e659e37537a0bdbe03c06275be23e499819138d1eee8337e9b", - /* blockTimeUs */ 0x6ab13b80, - /* numResults */ 2, - /* results */ hex"000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012" - ); - assertEq(ecr, hex"00000000000007446a0b819aee8945e659e37537a0bdbe03c06275be23e499819138d1eee8337e9b000000006ab13b8002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012"); - } - - function test_buildEthCallResultBytes() public { - bytes memory ecr1 = QueryTest.buildEthCallResultBytes( - /* result */ hex"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000" - ); - assertEq(ecr1, hex"000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000"); - bytes memory ecr2 = QueryTest.buildEthCallResultBytes( - /* result */ hex"0000000000000000000000000000000000000000000000000000000000000012" - ); - assertEq(ecr2, hex"000000200000000000000000000000000000000000000000000000000000000000000012"); - } - - function test_buildSolanaAccountResponseBytes() public { - bytes memory ecr = QueryTest.buildSolanaAccountResponseBytes( - /* slotNumber */ 5603, - /* blockTimeUs */ 0x610cdf2510500, - /* blockHash */ hex"e0eca895a92c0347e30538cd07c50777440de58e896dd13ff86ef0dae3e12552", - /* numResults */ 2, - /* results */ hex"0000000000164d6000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90000005201000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d0000e8890423c78a09010000000000000000000000000000000000000000000000000000000000000000000000000000000000164d6000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90000005201000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d01000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000" - ); - assertEq(ecr, hex"00000000000015e3000610cdf2510500e0eca895a92c0347e30538cd07c50777440de58e896dd13ff86ef0dae3e12552020000000000164d6000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90000005201000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d0000e8890423c78a09010000000000000000000000000000000000000000000000000000000000000000000000000000000000164d6000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90000005201000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d01000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000"); - } + vm.expectRevert(QueryTest.SolanaSeedTooLong.selector); + QueryTest.buildSolanaPdaSeedBytes(seeds); + } +} - function test_buildSolanaPdaResponseBytes() public { - bytes memory ecr = QueryTest.buildSolanaPdaResponseBytes( - /* slotNumber */ 2303, - /* blockTimeUs */ 0x6115e3f6d7540, - /* blockHash */ hex"e05035785e15056a8559815e71343ce31db2abf23f65b19c982b68aee7bf207b", - /* numResults */ 1, - /* results */ hex"4fa9188b339cfd573a0778c5deaeeee94d4bcfb12b345bf8e417e5119dae773efd0000000000116ac000000000000000000002c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa0000001457cd18b7f8a4d91a2da9ab4af05d0fbece2dcd65" - ); - assertEq(ecr, hex"00000000000008ff0006115e3f6d7540e05035785e15056a8559815e71343ce31db2abf23f65b19c982b68aee7bf207b014fa9188b339cfd573a0778c5deaeeee94d4bcfb12b345bf8e417e5119dae773efd0000000000116ac000000000000000000002c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa0000001457cd18b7f8a4d91a2da9ab4af05d0fbece2dcd65"); - } +contract QueryResponseTest is Test { + function test_buildQueryResponseBytes() public { + bytes memory resp = QueryTest.buildQueryResponseBytes( + /* version */ 1, + /* senderChainId */ 0, + /* signature */ hex"11b03bdbbe15a8f12b803d2193de5ddff72d92eaabd2763553ec3c3133182d1443719a05e2b65c87b923c6bd8aeff49f34937f90f3ab7cd33449388c60fa30a301", + /* queryRequest */ hex"0100000001010002010000004200000005307837343402ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567", + /* numPerChainResponses */ 1, + /* perChainResponses */ hex"000201000000b900000000000007446a0b819aee8945e659e37537a0bdbe03c06275be23e499819138d1eee8337e9b000000006ab13b8002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012" + ); + assertEq(resp, hex"01000011b03bdbbe15a8f12b803d2193de5ddff72d92eaabd2763553ec3c3133182d1443719a05e2b65c87b923c6bd8aeff49f34937f90f3ab7cd33449388c60fa30a3010000004f0100000001010002010000004200000005307837343402ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce56701000201000000b900000000000007446a0b819aee8945e659e37537a0bdbe03c06275be23e499819138d1eee8337e9b000000006ab13b8002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012"); + } + + function test_buildPerChainResponseBytes() public { + bytes memory pcr = QueryTest.buildPerChainResponseBytes( + /* chainId */ 2, + /* queryType */ 1, + /* responseBytes */ hex"00000000000007446a0b819aee8945e659e37537a0bdbe03c06275be23e499819138d1eee8337e9b000000006ab13b8002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012" + ); + assertEq(pcr, hex"000201000000b900000000000007446a0b819aee8945e659e37537a0bdbe03c06275be23e499819138d1eee8337e9b000000006ab13b8002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012"); + } + + function test_buildEthCallResponseBytes() public { + bytes memory ecr = QueryTest.buildEthCallResponseBytes( + /* blockNumber */ 1860, + /* blockHash */ hex"6a0b819aee8945e659e37537a0bdbe03c06275be23e499819138d1eee8337e9b", + /* blockTimeUs */ 0x6ab13b80, + /* numResults */ 2, + /* results */ hex"000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012" + ); + assertEq(ecr, hex"00000000000007446a0b819aee8945e659e37537a0bdbe03c06275be23e499819138d1eee8337e9b000000006ab13b8002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012"); + } + + function test_buildEthCallByTimestampResponseBytes() public { + bytes memory ecr = QueryTest.buildEthCallByTimestampResponseBytes( + /* targetBlockNumber */ 349, + /* targetBlockHash */ hex"966cd846f812be43c4ee2d310f962bc592ba944c66de878e53584b8e75c6051f", + /* targetBlockTimeUs */ 0x10642ac0, + /* followingBlockNumber */ 350, + /* followingBlockHash */ hex"04b022afaab8da2dd80bd8e6ae55e6303473a5e1de846a5de76d619e162429ce", + /* followingBlockTimeUs */ 0x10736d00, + /* numResults */ 2, + /* results */ hex"000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012" + ); + assertEq(ecr, hex"000000000000015d966cd846f812be43c4ee2d310f962bc592ba944c66de878e53584b8e75c6051f0000000010642ac0000000000000015e04b022afaab8da2dd80bd8e6ae55e6303473a5e1de846a5de76d619e162429ce0000000010736d0002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012"); + } + + function test_buildEthCallWithFinalityResponseBytes() public { + bytes memory ecr = QueryTest.buildEthCallWithFinalityResponseBytes( + /* blockNumber */ 1860, + /* blockHash */ hex"6a0b819aee8945e659e37537a0bdbe03c06275be23e499819138d1eee8337e9b", + /* blockTimeUs */ 0x6ab13b80, + /* numResults */ 2, + /* results */ hex"000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012" + ); + assertEq(ecr, hex"00000000000007446a0b819aee8945e659e37537a0bdbe03c06275be23e499819138d1eee8337e9b000000006ab13b8002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012"); + } + + function test_buildEthCallResultBytes() public { + bytes memory ecr1 = QueryTest.buildEthCallResultBytes( + /* result */ hex"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000" + ); + assertEq(ecr1, hex"000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000"); + bytes memory ecr2 = QueryTest.buildEthCallResultBytes( + /* result */ hex"0000000000000000000000000000000000000000000000000000000000000012" + ); + assertEq(ecr2, hex"000000200000000000000000000000000000000000000000000000000000000000000012"); + } + + function test_buildSolanaAccountResponseBytes() public { + bytes memory ecr = QueryTest.buildSolanaAccountResponseBytes( + /* slotNumber */ 5603, + /* blockTimeUs */ 0x610cdf2510500, + /* blockHash */ hex"e0eca895a92c0347e30538cd07c50777440de58e896dd13ff86ef0dae3e12552", + /* numResults */ 2, + /* results */ hex"0000000000164d6000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90000005201000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d0000e8890423c78a09010000000000000000000000000000000000000000000000000000000000000000000000000000000000164d6000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90000005201000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d01000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000" + ); + assertEq(ecr, hex"00000000000015e3000610cdf2510500e0eca895a92c0347e30538cd07c50777440de58e896dd13ff86ef0dae3e12552020000000000164d6000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90000005201000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d0000e8890423c78a09010000000000000000000000000000000000000000000000000000000000000000000000000000000000164d6000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90000005201000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d01000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000"); + } + + function test_buildSolanaPdaResponseBytes() public { + bytes memory ecr = QueryTest.buildSolanaPdaResponseBytes( + /* slotNumber */ 2303, + /* blockTimeUs */ 0x6115e3f6d7540, + /* blockHash */ hex"e05035785e15056a8559815e71343ce31db2abf23f65b19c982b68aee7bf207b", + /* numResults */ 1, + /* results */ hex"4fa9188b339cfd573a0778c5deaeeee94d4bcfb12b345bf8e417e5119dae773efd0000000000116ac000000000000000000002c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa0000001457cd18b7f8a4d91a2da9ab4af05d0fbece2dcd65" + ); + assertEq(ecr, hex"00000000000008ff0006115e3f6d7540e05035785e15056a8559815e71343ce31db2abf23f65b19c982b68aee7bf207b014fa9188b339cfd573a0778c5deaeeee94d4bcfb12b345bf8e417e5119dae773efd0000000000116ac000000000000000000002c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa0000001457cd18b7f8a4d91a2da9ab4af05d0fbece2dcd65"); + } } From 3a21d4db4ba8ce75313a65ff08e3c5d9fa317f0e Mon Sep 17 00:00:00 2001 From: nonergodic Date: Mon, 2 Sep 2024 13:40:57 -0700 Subject: [PATCH 2/6] remove missed trailing spaces --- src/QueryResponse.sol | 4 ++-- test/Proxy.t.sol | 4 ++-- test/QueryResponse.t.sol | 16 ++++++++-------- test/QueryTest.t.sol | 12 ++++++------ 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/QueryResponse.sol b/src/QueryResponse.sol index 6ce8e17..5e96c74 100644 --- a/src/QueryResponse.sol +++ b/src/QueryResponse.sol @@ -195,7 +195,7 @@ library QueryResponseLib { if(!signaturesValid) revert VerificationFailed(); }} - + function parseQueryResponse( bytes memory response ) internal pure returns (QueryResponse memory ret) { unchecked { @@ -537,7 +537,7 @@ library QueryResponseLib { ) internal pure { unchecked { if (validContractAddresses.length > 0) validateContractAddress(ecd.contractAddress, validContractAddresses); - + if (validFunctionSignatures.length > 0) { (bytes4 funcSig,) = ecd.callData.asBytes4(0); validateFunctionSignature(funcSig, validFunctionSignatures); diff --git a/test/Proxy.t.sol b/test/Proxy.t.sol index e666745..b49a804 100644 --- a/test/Proxy.t.sol +++ b/test/Proxy.t.sol @@ -34,7 +34,7 @@ contract LogicContractV1 is ProxyBase { function customUpgradeFun(address newImplementation, bytes calldata data) external { if (msg.sender != adminState().admin) revert NotAuthorized(); - + _upgradeTo(newImplementation, data); } } @@ -62,7 +62,7 @@ contract TestProxy is Test { abi.encodePacked(bytes4(NoValueAllowed.selector)) )); new Proxy{value: 1 ether}(logic1, abi.encode("v1")); - + //deploy LogicContractV1 contrct = LogicContractV1(address(new Proxy(logic1, abi.encode("v1")))); diff --git a/test/QueryResponse.t.sol b/test/QueryResponse.t.sol index a79b8c3..247efc2 100644 --- a/test/QueryResponse.t.sol +++ b/test/QueryResponse.t.sol @@ -30,7 +30,7 @@ contract QueryResponseLibWrapper { ) external view { return QueryResponseLib.verifyQueryResponse(wormhole, response, signatures); } - + function parseQueryResponse( bytes memory response ) external pure returns (QueryResponse memory ret) { @@ -300,7 +300,7 @@ contract TestQueryResponse is Test { assertEq(eqr.targetBlockTime, 0x03f4810cc0); assertEq(eqr.followingBlockNum, 0x0000000000004272); assertEq(eqr.followingBlockHash, hex"0b1608c2cddfd9d7fb4ec94f79ec1389e2410e611a2c2bbde94e9ad37519ebbb"); - assertEq(eqr.followingBlockTime, 0x03f4904f00); + assertEq(eqr.followingBlockTime, 0x03f4904f00); assertEq(eqr.results.length, 2); assertEq(eqr.results[0].contractAddress, address(0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E)); @@ -495,7 +495,7 @@ contract TestQueryResponse is Test { assertEq(sar.results[0].owner, hex"02c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa"); assertEq(sar.results[0].data, hex"57cd18b7f8a4d91a2da9ab4af05d0fbece2dcd65"); } - + function test_parseSolanaPdaQueryResponseRevertWrongQueryType() public { // Pass an ETH per chain response into the Solana parser. PerChainQueryResponse memory r = PerChainQueryResponse({ @@ -593,7 +593,7 @@ contract TestQueryResponse is Test { function testFuzz_parseAndVerifyQueryResponse_fuzzQueryRequestLen(uint32 _queryRequestLen, bytes calldata _perChainQueries) public { // We add 6 to account for version + nonce + numPerChainQueries - vm.assume(_queryRequestLen != _perChainQueries.length + 6); + vm.assume(_queryRequestLen != _perChainQueries.length + 6); bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, _perChainQueries, numPerChainResponses, perChainResponses); vm.expectRevert(); @@ -601,7 +601,7 @@ contract TestQueryResponse is Test { } function testFuzz_parseAndVerifyQueryResponse_fuzzQueryRequestVersion(uint8 _version, uint8 _queryRequestVersion) public { - vm.assume(_version != _queryRequestVersion); + vm.assume(_version != _queryRequestVersion); bytes memory resp = concatenateQueryResponseBytesOffChain(_version, senderChainId, signature, _queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); vm.expectRevert(); @@ -611,7 +611,7 @@ contract TestQueryResponse is Test { function testFuzz_parseAndVerifyQueryResponse_fuzzQueryRequestNonce(uint32 _queryRequestNonce) public { bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, _queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); QueryResponse memory r = wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); - + assertEq(r.nonce, _queryRequestNonce); } @@ -686,10 +686,10 @@ contract TestQueryResponse is Test { function testFuzz_verifyQueryResponse_validSignatureWrongPrefix(bytes calldata responsePrefix) public { vm.assume(keccak256(responsePrefix) != keccak256(QueryResponseLib.RESPONSE_PREFIX)); - + bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); bytes32 responseDigest = keccak256(abi.encodePacked(responsePrefix, keccak256(resp))); - + (uint8 sigV, bytes32 sigR, bytes32 sigS) = vm.sign(DEVNET_GUARDIAN_PRIVATE_KEY, responseDigest); IWormhole.Signature[] memory signatures = new IWormhole.Signature[](1); signatures[0] = IWormhole.Signature(sigR, sigS, sigV, SIG_GUARDIAN_INDEX); diff --git a/test/QueryTest.t.sol b/test/QueryTest.t.sol index 91d6965..6fa3916 100644 --- a/test/QueryTest.t.sol +++ b/test/QueryTest.t.sol @@ -38,17 +38,17 @@ contract QueryRequestTest is Test { bytes memory ecr = QueryTest.buildEthCallByTimestampRequestBytes( /* targetTimeUs */ 0x10642ac0, /* targetBlockHint */ "0x15d", - /* followingBlockHint */ "0x15e", + /* followingBlockHint */ "0x15e", /* numCallData */ 2, /* callData */ hex"ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567" ); assertEq(ecr, hex"0000000010642ac000000005307831356400000005307831356502ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567"); } - + function test_buildEthCallWithFinalityRequestBytes() public { bytes memory ecr = QueryTest.buildEthCallWithFinalityRequestBytes( /* blockId */ "0x1f8", - /* finality */ "finalized", + /* finality */ "finalized", /* numCallData */ 2, /* callData */ hex"ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567" ); @@ -66,8 +66,8 @@ contract QueryRequestTest is Test { /* callData */ hex"313ce567" ); assertEq(ecd2, hex"ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567"); - } - + } + function test_buildSolanaAccountRequestBytes() public { bytes memory ecr = QueryTest.buildSolanaAccountRequestBytes( /* commitment */ "finalized", @@ -231,7 +231,7 @@ contract QueryResponseTest is Test { /* targetBlockTimeUs */ 0x10642ac0, /* followingBlockNumber */ 350, /* followingBlockHash */ hex"04b022afaab8da2dd80bd8e6ae55e6303473a5e1de846a5de76d619e162429ce", - /* followingBlockTimeUs */ 0x10736d00, + /* followingBlockTimeUs */ 0x10736d00, /* numResults */ 2, /* results */ hex"000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012" ); From 0750963f5c3825f6fad7e31ea23d697710723dde Mon Sep 17 00:00:00 2001 From: nonergodic Date: Tue, 3 Sep 2024 01:37:04 -0700 Subject: [PATCH 3/6] fix guardian set rotation issue, stick to custom errors only --- src/QueryResponse.sol | 65 +++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/src/QueryResponse.sol b/src/QueryResponse.sol index 5e96c74..e03377a 100644 --- a/src/QueryResponse.sol +++ b/src/QueryResponse.sol @@ -128,8 +128,6 @@ struct SolanaPdaResult { uint8 bump; } -// Custom errors - error WrongQueryType(uint8 received, uint8 expected); error InvalidResponseVersion(); error VersionMismatch(); @@ -144,7 +142,6 @@ error InvalidFunctionSignature(); error InvalidChainId(); error StaleBlockNum(); error StaleBlockTime(); -error NoQuorum(); error VerificationFailed(); //QueryResponse is a library that implements the parsing and verification of @@ -163,7 +160,6 @@ library QueryResponseLib { return keccak256(abi.encodePacked(RESPONSE_PREFIX, keccak256(response))); } - //WARNING: see verifyQueryResponse WARNING function parseAndVerifyQueryResponse( address wormhole, bytes memory response, @@ -173,27 +169,33 @@ library QueryResponseLib { return parseQueryResponse(response); } - //WARNING: This call can fail during times of guardian set rotation. - // Unlikely, but possible: - // Since only the current guardian set is considered when verifying signatures here, a response - // will be rejected with a failed verification, even if the signing guardian is still within - // what would otherwise be the 24 hour transition window. function verifyQueryResponse( address wormhole, bytes memory response, IWormhole.Signature[] memory signatures ) internal view { unchecked { IWormhole wormhole_ = IWormhole(wormhole); - IWormhole.GuardianSet memory guardianSet = - wormhole_.getGuardianSet(wormhole_.getCurrentGuardianSetIndex()); - uint quorum = guardianSet.keys.length * 2 / 3 + 1; - if (signatures.length < quorum) - revert NoQuorum(); - - (bool signaturesValid, ) = - wormhole_.verifySignatures(calcPrefixedResponseHash(response), signatures, guardianSet); - if(!signaturesValid) - revert VerificationFailed(); + uint32 guardianSetIndex = wormhole_.getCurrentGuardianSetIndex(); + IWormhole.GuardianSet memory guardianSet = wormhole_.getGuardianSet(guardianSetIndex); + + while (true) { + uint quorum = guardianSet.keys.length * 2 / 3 + 1; + if (signatures.length >= quorum) { + (bool signaturesValid, ) = + wormhole_.verifySignatures(calcPrefixedResponseHash(response), signatures, guardianSet); + if (signaturesValid) + return; + } + + //check if the previous guardian set is still valid and if yes, try with that + if (guardianSetIndex > 0) { + guardianSet = wormhole_.getGuardianSet(--guardianSetIndex); + if (guardianSet.expirationTime < block.timestamp) + revert VerificationFailed(); + } + else + revert VerificationFailed(); + } }} function parseQueryResponse( @@ -207,8 +209,8 @@ library QueryResponseLib { (ret.senderChainId, offset) = response.asUint16Unchecked(offset); - //For off chain requests (chainID zero), the requestId is the 65 byte signature. - //For on chain requests, it is the 32 byte VAA hash. + //for off-chain requests (chainID zero), the requestId is the 65 byte signature + //for on-chain requests, it is the 32 byte VAA hash (ret.requestId, offset) = response.sliceUnchecked(offset, ret.senderChainId == 0 ? 65 : 32); uint32 queryReqLen; @@ -227,7 +229,7 @@ library QueryResponseLib { uint8 numPerChainQueries; (numPerChainQueries, reqOff) = response.asUint8Unchecked(reqOff); - //A valid query request has at least one per chain query + //a valid query request must have at least one per-chain-query if (numPerChainQueries == 0) revert ZeroQueries(); @@ -242,7 +244,7 @@ library QueryResponseLib { ret.responses = new PerChainQueryResponse[](numPerChainQueries); - //Walk through the requests and responses in lock step. + //walk through the requests and responses in lock step. for (uint i; i < numPerChainQueries; ++i) { (ret.responses[i].chainId, reqOff) = response.asUint16Unchecked(reqOff); uint16 respChainId; @@ -262,7 +264,7 @@ library QueryResponseLib { (ret.responses[i].response, respOff) = response.sliceUint32PrefixedUnchecked(respOff); } - //End of request body should align with start of response body + //end of request body should align with start of response body if (startOfResponse != reqOff) revert InvalidPayloadLength(startOfResponse, reqOff); @@ -294,7 +296,7 @@ library QueryResponseLib { ret.results = new EthCallRecord[](numBatchCallData); - //Walk through the call inputs and outputs in lock step. + //walk through the call inputs and outputs in lock step. for (uint i; i < numBatchCallData; ++i) { (ret.results[i].contractAddress, reqOff) = pcr.request.asAddressUnchecked(reqOff); (ret.results[i].callData, reqOff) = pcr.request.sliceUint32PrefixedUnchecked(reqOff); @@ -336,7 +338,7 @@ library QueryResponseLib { ret.results = new EthCallRecord[](numBatchCallData); - // Walk through the call inputs and outputs in lock step. + //walk through the call inputs and outputs in lock step. for (uint i; i < numBatchCallData; ++i) { (ret.results[i].contractAddress, reqOff) = pcr.request.asAddressUnchecked(reqOff); (ret.results[i].callData, reqOff) = pcr.request.sliceUint32PrefixedUnchecked(reqOff); @@ -373,7 +375,7 @@ library QueryResponseLib { ret.results = new EthCallRecord[](numBatchCallData); - //Walk through the call inputs and outputs in lock step. + //walk through the call inputs and outputs in lock step. for (uint i; i < numBatchCallData; ++i) { (ret.results[i].contractAddress, reqOff) = pcr.request.asAddressUnchecked(reqOff); (ret.results[i].callData, reqOff) = pcr.request.sliceUint32PrefixedUnchecked(reqOff); @@ -412,7 +414,7 @@ library QueryResponseLib { ret.results = new SolanaAccountResult[](numAccounts); - //Walk through the call inputs and outputs in lock step. + //walk through the call inputs and outputs in lock step. for (uint i; i < numAccounts; ++i) { (ret.results[i].account, reqOff) = pcr.request.asBytes32Unchecked(reqOff); @@ -455,7 +457,7 @@ library QueryResponseLib { ret.results = new SolanaPdaResult[](numPdas); - //Walk through the call inputs and outputs in lock step. + //walk through the call inputs and outputs in lock step. for (uint i; i < numPdas; ++i) { (ret.results[i].programId, reqOff) = pcr.request.asBytes32Unchecked(reqOff); @@ -539,7 +541,10 @@ library QueryResponseLib { validateContractAddress(ecd.contractAddress, validContractAddresses); if (validFunctionSignatures.length > 0) { - (bytes4 funcSig,) = ecd.callData.asBytes4(0); + if (ecd.callData.length < 4) + revert InvalidFunctionSignature(); + + (bytes4 funcSig, uint offset) = ecd.callData.asBytes4Unchecked(0); validateFunctionSignature(funcSig, validFunctionSignatures); } }} From d8adcea147727c773c87c90b6377ff2e2d1a865a Mon Sep 17 00:00:00 2001 From: nonergodic Date: Tue, 29 Oct 2024 17:22:38 -0700 Subject: [PATCH 4/6] rebase, rebuild tests on WormholeOverride, add missing NoQuorum test --- Makefile | 24 + foundry.toml | 2 +- gen/.gitignore | 3 +- gen/Makefile | 45 +- gen/fork_changed | 1 + gen/libraryTestWrapper.ts | 12 +- gen/package-lock.json | 132 ++-- gen/package.json | 6 +- gen/testing.env | 7 + gen/testingEnv.ts | 52 ++ src/constants/CCTPDomains.sol | 1 + src/constants/Chains.sol | 3 + src/{ => libraries}/QueryResponse.sol | 54 +- .../QueryTest.sol => QueryRequestBuilder.sol} | 72 +-- src/testing/WormholeOverride.sol | 35 +- .../WormholeRelayer/MockOffchainRelayer.sol | 10 +- src/testing/helpers/WormholeMock.sol | 132 ---- test/{QueryTest.t.sol => QueryRequest.t.sol} | 80 ++- test/QueryResponse.t.sol | 348 ++++++----- test/generated/BytesParsingTestWrapper.sol | 566 +++++++++--------- test/generated/QueryResponseTestWrapper.sol | 106 ++++ 21 files changed, 895 insertions(+), 796 deletions(-) create mode 100644 Makefile create mode 100644 gen/fork_changed create mode 100644 gen/testing.env create mode 100644 gen/testingEnv.ts rename src/{ => libraries}/QueryResponse.sol (93%) rename src/testing/{helpers/QueryTest.sol => QueryRequestBuilder.sol} (77%) delete mode 100644 src/testing/helpers/WormholeMock.sol rename test/{QueryTest.t.sol => QueryRequest.t.sol} (85%) create mode 100644 test/generated/QueryResponseTestWrapper.sol diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4f0ca34 --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +#the chain that will be forked for testing +TEST_FORK = Mainnet Ethereum + +.DEFAULT_GOAL = build +.PHONY: build test clean + +build: + @$(MAKE) -C gen build + forge build + +#include (and build if necessary) env/testing.env if we're running tests +ifneq (,$(filter test, $(MAKECMDGOALS))) +#hacky: +_ := $(shell $(MAKE) -C gen testing.env "TEST_FORK=$(strip $(TEST_FORK))") +include gen/testing.env +export +unexport TEST_FORK +endif +test: build + forge test + +clean: + @$(MAKE) -C gen clean + forge clean diff --git a/foundry.toml b/foundry.toml index 69d38c1..2ab3aca 100644 --- a/foundry.toml +++ b/foundry.toml @@ -18,4 +18,4 @@ remappings = [ "IERC20/=src/interfaces/token/", ] -# See more config options https://github.com/foundry-rs/foundry/tree/master/config +verbosity = 3 diff --git a/gen/.gitignore b/gen/.gitignore index b512c09..bcb300b 100644 --- a/gen/.gitignore +++ b/gen/.gitignore @@ -1 +1,2 @@ -node_modules \ No newline at end of file +node_modules +.package-lock.json \ No newline at end of file diff --git a/gen/Makefile b/gen/Makefile index 030586c..c6d694e 100644 --- a/gen/Makefile +++ b/gen/Makefile @@ -1,28 +1,49 @@ -GENEREATORS = chains cctpDomains -chains_TARGET = ../src/constants/Chains.sol -cctpDomains_TARGET = ../src/constants/CCTPDomains.sol +GENERATORS = chains cctpDomains +chains_TARGET = constants/Chains.sol +cctpDomains_TARGET = constants/CCTPDomains.sol -TEST_WRAPPERS = BytesParsing -BytesParsing_BASE_PATH = libraries +fnGeneratorTarget = ../src/$($(1)_TARGET) +GENERATOR_TARGETS = $(foreach generator,$(GENERATORS),$(call fnGeneratorTarget,$(generator))) + +TEST_WRAPPERS = BytesParsing QueryResponse fnTestWrapperTarget = ../test/generated/$(1)TestWrapper.sol TEST_WRAPPER_TARGETS =\ - $(foreach wrapper, $(TEST_WRAPPERS), $(call fnTestWrapperTarget,$(wrapper))) + $(foreach wrapper,$(TEST_WRAPPERS),$(call fnTestWrapperTarget,$(wrapper))) + +.DEFAULT_GOAL = build +.PHONY: build clean FORCE -.PHONY: generate $(GENEREATORS) +build: $(GENERATOR_TARGETS) $(TEST_WRAPPER_TARGETS) -build: $(GENEREATORS) $(TEST_WRAPPER_TARGETS) +clean: + rm -rf node_modules fork_changed testing.env -$(GENEREATORS): node_modules - npx ts-node $@.ts > $($@_TARGET) +FORCE: node_modules: package-lock.json npm ci +define ruleGenerator +$(call fnGeneratorTarget,$(1)): node_modules $(1).ts + npx ts-node $(1).ts > $$@ +endef +$(foreach generator,$(GENERATORS),$(eval $(call ruleGenerator,$(generator)))) + define ruleTestWrapper -$(call fnTestWrapperTarget,$(1)): ../src/$($(1)_BASE_PATH)/$(1).sol - npx ts-node libraryTestWrapper.ts $($(1)_BASE_PATH)/$(1) > $(call fnTestWrapperTarget,$(1)) +$(call fnTestWrapperTarget,$(1)): ../src/libraries/$(1).sol libraryTestWrapper.ts + npx ts-node libraryTestWrapper.ts libraries/$(1) > $$@ endef $(foreach wrapper,$(TEST_WRAPPERS),$(eval $(call ruleTestWrapper,$(wrapper)))) +ifneq ($(TEST_FORK), $(shell cat fork_changed 2>/dev/null)) +#if a different chain/network for testing was supplied last time then force an update +fork_changed: FORCE +endif + +testing.env: node_modules fork_changed testingEnv.ts + @echo "Generating testing.env for $(TEST_FORK)" + npx ts-node testingEnv.ts $(TEST_FORK) > $@ +fork_changed: + @echo $(TEST_FORK) > fork_changed diff --git a/gen/fork_changed b/gen/fork_changed new file mode 100644 index 0000000..c6cf180 --- /dev/null +++ b/gen/fork_changed @@ -0,0 +1 @@ +Mainnet Ethereum diff --git a/gen/libraryTestWrapper.ts b/gen/libraryTestWrapper.ts index 07ebe58..bac6643 100644 --- a/gen/libraryTestWrapper.ts +++ b/gen/libraryTestWrapper.ts @@ -107,12 +107,14 @@ for (const libs of libMatches) { const retParasRaw = modsRaw.match(retParasRegex); const collapseSpaceRegex = /\s\s+/g; - const paramsToArray = (paramList: string) => - paramList.replace(collapseSpaceRegex, ' ').trim().split(',').map(param => { + const paramsToArray = (paramList: string) => { + const cleaned = paramList.replace(collapseSpaceRegex, ' ').trim(); + return !cleaned ? [] : cleaned.split(',').map(param => { param = param.trim(); const paraType = param.match(/^(\w+)/)[1]; return structs.has(paraType) ? param.replace(paraType, `${name}.${paraType}`) : param; }); + } libraries[name].push({ name: funcName, @@ -142,8 +144,10 @@ for (const [libName, funcs] of Object.entries(libraries)) { const funcCode = []; for (const func of funcs) funcCode.push([ - ` function ${func.name}(${pConcat(func.paras)}) external ${func.stateMut}` + - ` ${func.rets ? `returns (${pConcat(func.rets)}) ` : ''} {`, + ` function ${func.name}(${pConcat(func.paras)}) external` + + (func.stateMut ? ` ${func.stateMut}` : "") + + (func.rets ? ` returns (${pConcat(func.rets)})` : "") + + " {", ` ${func.rets ? 'return ' : ''}${libName}.${func.name}(${pNames(func.paras).join(', ')});`, ` }` ].join('\n')); diff --git a/gen/package-lock.json b/gen/package-lock.json index c2e9f4e..f1874cb 100644 --- a/gen/package-lock.json +++ b/gen/package-lock.json @@ -10,9 +10,9 @@ "license": "Apache-2.0", "dependencies": { "@types/node": "^20.3.1", - "@wormhole-foundation/sdk-base": "^0.10.2", - "@wormhole-foundation/sdk-definitions": "^0.10.2", - "@wormhole-foundation/sdk-evm": "^0.10.2", + "@wormhole-foundation/sdk-base": "latest", + "@wormhole-foundation/sdk-definitions": "latest", + "@wormhole-foundation/sdk-evm": "latest", "ts-node": "^10.9.2", "typescript": "^5.5.4" } @@ -61,33 +61,36 @@ } }, "node_modules/@noble/curves": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.5.0.tgz", - "integrity": "sha512-J5EKamIHnKPyClwVrzmaf5wSdQXgdHcPZIZLu3bwnbeCx8/7NPK5q2ZBWF+5FvYGByjiQQsJYX6jfgB2wDPn3A==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.6.0.tgz", + "integrity": "sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ==", "license": "MIT", "dependencies": { - "@noble/hashes": "1.4.0" + "@noble/hashes": "1.5.0" + }, + "engines": { + "node": "^14.21.3 || >=16" }, "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/@noble/hashes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", - "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.5.0.tgz", + "integrity": "sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==", "license": "MIT", "engines": { - "node": ">= 16" + "node": "^14.21.3 || >=16" }, "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/@scure/base": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.7.tgz", - "integrity": "sha512-PPNYBslrLNNUQ/Yad37MHYsNQtK67EhWb6WtSvNLLPo7SdVZgkUjD6Dg+5On7zNwmskf8OX7I7Nx5oN+MIWE0g==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", "license": "MIT", "funding": { "url": "https://paulmillr.com/funding/" @@ -118,31 +121,31 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.16.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.5.tgz", - "integrity": "sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA==", + "version": "20.17.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.2.tgz", + "integrity": "sha512-OOHK4sjXqkL7yQ7VEEHcf6+0jSvKjWqwnaCtY7AKD/VLEvRHMsxxu7eI8ErnjxHS8VwmekD4PeVCpu4qZEZSxg==", "license": "MIT", "dependencies": { "undici-types": "~6.19.2" } }, "node_modules/@wormhole-foundation/sdk-base": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@wormhole-foundation/sdk-base/-/sdk-base-0.10.2.tgz", - "integrity": "sha512-8r/66xe/LqmaANxMeNwL34BU0nr4EI6jup9aCUyBir2XkTUgIBTAZMJotvUrmf42t4OhageujUAfaMFJy8rk+Q==", + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@wormhole-foundation/sdk-base/-/sdk-base-0.15.0.tgz", + "integrity": "sha512-Akc17+YuI+m0VWVIxI3F7AkeU5FR9l/OP30UT+VuYQCPxqMI/d/crMu5ZbXmYJWAzrsFpbu9/ohOS0WsDwj0EQ==", "license": "Apache-2.0", "dependencies": { "@scure/base": "^1.1.3" } }, "node_modules/@wormhole-foundation/sdk-connect": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@wormhole-foundation/sdk-connect/-/sdk-connect-0.10.2.tgz", - "integrity": "sha512-n67KxaEMhjdks9wnanylhKbzypShTPorumOUXnufn0fWODQwhuANWJ1WQXjFaFmu3p/TPAkMKtPa56MB5cFsNA==", + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@wormhole-foundation/sdk-connect/-/sdk-connect-0.15.0.tgz", + "integrity": "sha512-lXK5o4O0QOED7Q3nDKM7z2BwN7NZp+vYwW4xI4gs/v61jkx8Hfa0RTKu+RNxB2VTNl+XCA6rQbz5QGWSI0BP7g==", "license": "Apache-2.0", "dependencies": { - "@wormhole-foundation/sdk-base": "0.10.2", - "@wormhole-foundation/sdk-definitions": "0.10.2", + "@wormhole-foundation/sdk-base": "0.15.0", + "@wormhole-foundation/sdk-definitions": "0.15.0", "axios": "^1.4.0" }, "engines": { @@ -150,22 +153,22 @@ } }, "node_modules/@wormhole-foundation/sdk-definitions": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@wormhole-foundation/sdk-definitions/-/sdk-definitions-0.10.2.tgz", - "integrity": "sha512-UFVPGur/BIM1atlvl5cRU1hkqbYNvXjNt+fssVitlJ9MowGbSNy2nZz8srtFfbICQ5RjJFUAlBlsax9zW4gkZA==", + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@wormhole-foundation/sdk-definitions/-/sdk-definitions-0.15.0.tgz", + "integrity": "sha512-RfEXMQga4IXlPbKhldcWcMHVGdKfmIbTCbH2cf/TwSVTZp3dPrktLbqkfuB7+/JRML8Bg9b4xwb/YZ5gxNDMsA==", "dependencies": { "@noble/curves": "^1.4.0", "@noble/hashes": "^1.3.1", - "@wormhole-foundation/sdk-base": "0.10.2" + "@wormhole-foundation/sdk-base": "0.15.0" } }, "node_modules/@wormhole-foundation/sdk-evm": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@wormhole-foundation/sdk-evm/-/sdk-evm-0.10.2.tgz", - "integrity": "sha512-Pcfvt6dpnsqIaR48iysAPgFzzM4GGVQf7b7xPIhvZrjoGsiJlyu3k4os6L8rDKB25HjeB0im/9KE+Cs2eMhP5Q==", + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@wormhole-foundation/sdk-evm/-/sdk-evm-0.15.0.tgz", + "integrity": "sha512-m7H9S4DQcOUAfcrdj9dZAXhdSipHr4vn9j9YQgltbrZiY0/tG5MShcZbZzBtLg7/JfEgp29Z6lje7Q2zFJUmCw==", "license": "Apache-2.0", "dependencies": { - "@wormhole-foundation/sdk-connect": "0.10.2", + "@wormhole-foundation/sdk-connect": "0.15.0", "ethers": "^6.5.1" }, "engines": { @@ -173,9 +176,9 @@ } }, "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -185,9 +188,9 @@ } }, "node_modules/acorn-walk": { - "version": "8.3.3", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", - "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", "license": "MIT", "dependencies": { "acorn": "^8.11.0" @@ -215,9 +218,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", - "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -262,9 +265,9 @@ } }, "node_modules/ethers": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.13.2.tgz", - "integrity": "sha512-9VkriTTed+/27BGuY1s0hf441kqwHJ1wtN2edksEtiRvXx+soxRX3iSXTfFqq2+YwrOqbDoTHjIhQnjJRlzKmg==", + "version": "6.13.4", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.13.4.tgz", + "integrity": "sha512-21YtnZVg4/zKkCQPjrDj38B1r4nQvTZLopUGMLQ1ePU2zV/joCfDC3t3iKQjWRzjjjbzR+mdAIoikeBRNkdllA==", "funding": [ { "type": "individual", @@ -280,9 +283,9 @@ "@adraffy/ens-normalize": "1.10.1", "@noble/curves": "1.2.0", "@noble/hashes": "1.3.2", - "@types/node": "18.15.13", + "@types/node": "22.7.5", "aes-js": "4.0.0-beta.5", - "tslib": "2.4.0", + "tslib": "2.7.0", "ws": "8.17.1" }, "engines": { @@ -314,15 +317,18 @@ } }, "node_modules/ethers/node_modules/@types/node": { - "version": "18.15.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz", - "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==", - "license": "MIT" + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } }, "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "funding": [ { "type": "individual", @@ -340,9 +346,9 @@ } }, "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -430,15 +436,15 @@ } }, "node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", "license": "0BSD" }, "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", diff --git a/gen/package.json b/gen/package.json index 25578e1..bcea3e6 100644 --- a/gen/package.json +++ b/gen/package.json @@ -8,10 +8,10 @@ "build": "make" }, "dependencies": { + "@wormhole-foundation/sdk-base": "latest", + "@wormhole-foundation/sdk-definitions": "latest", + "@wormhole-foundation/sdk-evm": "latest", "@types/node": "^20.3.1", - "@wormhole-foundation/sdk-base": "^0.10.2", - "@wormhole-foundation/sdk-definitions": "^0.10.2", - "@wormhole-foundation/sdk-evm": "^0.10.2", "ts-node": "^10.9.2", "typescript": "^5.5.4" } diff --git a/gen/testing.env b/gen/testing.env new file mode 100644 index 0000000..614aa8c --- /dev/null +++ b/gen/testing.env @@ -0,0 +1,7 @@ +TEST_RPC_URL=https://rpc.ankr.com/eth +TEST_USDC_ADDRESS=0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 +TEST_CCTP_TOKEN_MESSENGER_ADDRESS=0xbd3fa81b58ba92a82136038b25adec7066af3155 +TEST_CCTP_MESSAGE_TRANSMITTER_ADDRESS=0x0a992d191deec32afe36203ad87d7d289a738f81 +TEST_WNATIVE_ADDRESS=0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 +TEST_WORMHOLE_ADDRESS=0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B +TEST_TOKEN_BRIDGE_ADDRESS=0x3ee18B2214AFF97000D974cf647E7C347E8fa585 diff --git a/gen/testingEnv.ts b/gen/testingEnv.ts new file mode 100644 index 0000000..f6a4fd9 --- /dev/null +++ b/gen/testingEnv.ts @@ -0,0 +1,52 @@ +import * as base from "@wormhole-foundation/sdk-base"; +import * as tokens from "@wormhole-foundation/sdk-base/tokens"; + +function errorExit(reason: string): never { + console.error(reason); + process.exit(1); +} + +if (process.argv.length != 4) + errorExit("Usage: "); + +const network = (() => { + const network = process.argv[2]; + if (!base.network.isNetwork(network)) + errorExit(`Invalid network: ${network}`); + + return network; +})(); + +const chain = (() => { + const chain = process.argv[3]; + if (!base.chain.isChain(chain)) + errorExit(`Invalid chain: ${chain}`); + + return chain; +})(); + +const testVars = ([ + ["RPC_URL", base.rpc.rpcAddress(network, chain)], + ["USDC_ADDRESS", base.circle.usdcContract.get(network, chain)], + [ + "CCTP_TOKEN_MESSENGER_ADDRESS", + base.contracts.circleContracts.get(network, chain)?.tokenMessenger + ], + [ + "CCTP_MESSAGE_TRANSMITTER_ADDRESS", + base.contracts.circleContracts.get(network, chain)?.messageTransmitter + ], + [ + "WNATIVE_ADDRESS", + tokens.getTokenByKey(network, chain, tokens.getNative(network, chain)?.wrappedKey)?.address + ], + ["WORMHOLE_ADDRESS", base.contracts.coreBridge.get(network, chain)], + ["TOKEN_BRIDGE_ADDRESS", base.contracts.tokenBridge.get(network, chain)] +] as const satisfies [string, string | undefined][]).map(([varName, val]) => { + if (!val) + errorExit(`No value for ${varName} for ${network} ${chain}`); + + return `TEST_${varName}=${val}`; +}); + +console.log(testVars.join("\n")); diff --git a/src/constants/CCTPDomains.sol b/src/constants/CCTPDomains.sol index c6d1a40..ee061c0 100644 --- a/src/constants/CCTPDomains.sol +++ b/src/constants/CCTPDomains.sol @@ -10,6 +10,7 @@ uint32 constant CCTP_DOMAIN_ARBITRUM = 3; uint32 constant CCTP_DOMAIN_SOLANA = 5; uint32 constant CCTP_DOMAIN_BASE = 6; uint32 constant CCTP_DOMAIN_POLYGON = 7; +uint32 constant CCTP_DOMAIN_SUI = 8; // Additional Testnet mappings: uint32 constant CCTP_DOMAIN_SEPOLIA = 0; diff --git a/src/constants/Chains.sol b/src/constants/Chains.sol index 426d9de..b7fa590 100644 --- a/src/constants/Chains.sol +++ b/src/constants/Chains.sol @@ -44,6 +44,8 @@ uint16 constant CHAIN_ID_LINEA = 38; uint16 constant CHAIN_ID_BERACHAIN = 39; uint16 constant CHAIN_ID_SEIEVM = 40; uint16 constant CHAIN_ID_SNAXCHAIN = 43; +uint16 constant CHAIN_ID_UNICHAIN = 44; +uint16 constant CHAIN_ID_WORLDCHAIN = 45; uint16 constant CHAIN_ID_WORMCHAIN = 3104; uint16 constant CHAIN_ID_COSMOSHUB = 4000; uint16 constant CHAIN_ID_EVMOS = 4001; @@ -60,3 +62,4 @@ uint16 constant CHAIN_ID_BASE_SEPOLIA = 10004; uint16 constant CHAIN_ID_OPTIMISM_SEPOLIA = 10005; uint16 constant CHAIN_ID_HOLESKY = 10006; uint16 constant CHAIN_ID_POLYGON_SEPOLIA = 10007; +uint16 constant CHAIN_ID_MONAD_DEVNET = 10008; diff --git a/src/QueryResponse.sol b/src/libraries/QueryResponse.sol similarity index 93% rename from src/QueryResponse.sol rename to src/libraries/QueryResponse.sol index e03377a..63c30ca 100644 --- a/src/QueryResponse.sol +++ b/src/libraries/QueryResponse.sol @@ -2,8 +2,8 @@ pragma solidity ^0.8.4; -import {BytesParsing} from "./libraries/BytesParsing.sol"; -import {IWormhole} from "./interfaces/IWormhole.sol"; +import {IWormhole} from "wormhole-sdk/interfaces/IWormhole.sol"; +import {BytesParsing} from "wormhole-sdk/libraries/BytesParsing.sol"; error UnsupportedQueryType(uint8 received); @@ -160,6 +160,7 @@ library QueryResponseLib { return keccak256(abi.encodePacked(RESPONSE_PREFIX, keccak256(response))); } + //TODO add calldata impl? (duplicated code but better gas efficiency) function parseAndVerifyQueryResponse( address wormhole, bytes memory response, @@ -173,6 +174,14 @@ library QueryResponseLib { address wormhole, bytes memory response, IWormhole.Signature[] memory signatures + ) internal view { + verifyQueryResponse(wormhole, calcPrefixedResponseHash(response), signatures); + } + + function verifyQueryResponse( + address wormhole, + bytes32 prefixedResponseHash, + IWormhole.Signature[] memory signatures ) internal view { unchecked { IWormhole wormhole_ = IWormhole(wormhole); uint32 guardianSetIndex = wormhole_.getCurrentGuardianSetIndex(); @@ -182,7 +191,7 @@ library QueryResponseLib { uint quorum = guardianSet.keys.length * 2 / 3 + 1; if (signatures.length >= quorum) { (bool signaturesValid, ) = - wormhole_.verifySignatures(calcPrefixedResponseHash(response), signatures, guardianSet); + wormhole_.verifySignatures(prefixedResponseHash, signatures, guardianSet); if (signaturesValid) return; } @@ -268,7 +277,7 @@ library QueryResponseLib { if (startOfResponse != reqOff) revert InvalidPayloadLength(startOfResponse, reqOff); - checkLength(response, respOff); + _checkLength(response, respOff); return ret; }} @@ -304,8 +313,8 @@ library QueryResponseLib { (ret.results[i].result, respOff) = pcr.response.sliceUint32PrefixedUnchecked(respOff); } - checkLength(pcr.request, reqOff); - checkLength(pcr.response, respOff); + _checkLength(pcr.request, reqOff); + _checkLength(pcr.response, respOff); return ret; }} @@ -346,8 +355,8 @@ library QueryResponseLib { (ret.results[i].result, respOff) = pcr.response.sliceUint32PrefixedUnchecked(respOff); } - checkLength(pcr.request, reqOff); - checkLength(pcr.response, respOff); + _checkLength(pcr.request, reqOff); + _checkLength(pcr.response, respOff); }} function parseEthCallWithFinalityQueryResponse( @@ -383,8 +392,8 @@ library QueryResponseLib { (ret.results[i].result, respOff) = pcr.response.sliceUint32PrefixedUnchecked(respOff); } - checkLength(pcr.request, reqOff); - checkLength(pcr.response, respOff); + _checkLength(pcr.request, reqOff); + _checkLength(pcr.response, respOff); }} function parseSolanaAccountQueryResponse( @@ -425,8 +434,8 @@ library QueryResponseLib { (ret.results[i].data, respOff) = pcr.response.sliceUint32PrefixedUnchecked(respOff); } - checkLength(pcr.request, reqOff); - checkLength(pcr.response, respOff); + _checkLength(pcr.request, reqOff); + _checkLength(pcr.response, respOff); }} function parseSolanaPdaQueryResponse( @@ -476,8 +485,8 @@ library QueryResponseLib { (ret.results[i].data, respOff) = pcr.response.sliceUint32PrefixedUnchecked(respOff); } - checkLength(pcr.request, reqOff); - checkLength(pcr.response, respOff); + _checkLength(pcr.request, reqOff); + _checkLength(pcr.response, respOff); }} function validateBlockTime( @@ -536,20 +545,20 @@ library QueryResponseLib { EthCallRecord memory ecd, address[] memory validContractAddresses, //empty array means accept all bytes4[] memory validFunctionSignatures //empty array means accept all - ) internal pure { unchecked { + ) internal pure { if (validContractAddresses.length > 0) - validateContractAddress(ecd.contractAddress, validContractAddresses); + _validateContractAddress(ecd.contractAddress, validContractAddresses); if (validFunctionSignatures.length > 0) { if (ecd.callData.length < 4) revert InvalidFunctionSignature(); - (bytes4 funcSig, uint offset) = ecd.callData.asBytes4Unchecked(0); - validateFunctionSignature(funcSig, validFunctionSignatures); + (bytes4 funcSig, ) = ecd.callData.asBytes4Unchecked(0); + _validateFunctionSignature(funcSig, validFunctionSignatures); } - }} + } - function validateContractAddress( + function _validateContractAddress( address contractAddress, address[] memory validContractAddresses ) private pure { unchecked { @@ -561,7 +570,7 @@ library QueryResponseLib { revert InvalidContractAddress(); }} - function validateFunctionSignature( + function _validateFunctionSignature( bytes4 functionSignature, bytes4[] memory validFunctionSignatures ) private pure { unchecked { @@ -574,9 +583,8 @@ library QueryResponseLib { }} //we use this over BytesParsing.checkLength to return our custom errors in all error cases - function checkLength(bytes memory encoded, uint256 expected) private pure { + function _checkLength(bytes memory encoded, uint256 expected) private pure { if (encoded.length != expected) revert InvalidPayloadLength(encoded.length, expected); } } - diff --git a/src/testing/helpers/QueryTest.sol b/src/testing/QueryRequestBuilder.sol similarity index 77% rename from src/testing/helpers/QueryTest.sol rename to src/testing/QueryRequestBuilder.sol index 204dc67..8019a93 100644 --- a/src/testing/helpers/QueryTest.sol +++ b/src/testing/QueryRequestBuilder.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.4; //build Cross Chain Query (CCQ) responses for testing purposes. -library QueryTest { +library QueryRequestBuilder { error SolanaTooManyPDAs(); error SolanaTooManySeeds(); error SolanaSeedTooLong(); @@ -23,12 +23,12 @@ library QueryTest { uint32 nonce, uint8 numPerChainQueries, bytes memory perChainQueries - ) internal pure returns (bytes memory){ + ) internal pure returns (bytes memory) { return abi.encodePacked( version, nonce, numPerChainQueries, - perChainQueries // Each created by buildPerChainRequestBytes() + perChainQueries //Each created by buildPerChainRequestBytes() ); } @@ -36,15 +36,15 @@ library QueryTest { uint16 chainId, uint8 queryType, bytes memory queryBytes - ) internal pure returns (bytes memory){ + ) internal pure returns (bytes memory) { return abi.encodePacked(chainId, queryType, uint32(queryBytes.length), queryBytes); } function buildEthCallRequestBytes( bytes memory blockId, uint8 numCallData, - bytes memory callData // Created with buildEthCallRecordBytes() - ) internal pure returns (bytes memory){ + bytes memory callData //Created with buildEthCallRecordBytes() + ) internal pure returns (bytes memory) { return abi.encodePacked(uint32(blockId.length), blockId, numCallData, callData); } @@ -53,8 +53,8 @@ library QueryTest { bytes memory targetBlockHint, bytes memory followingBlockHint, uint8 numCallData, - bytes memory callData // Created with buildEthCallRecordBytes() - ) internal pure returns (bytes memory){ + bytes memory callData //Created with buildEthCallRecordBytes() + ) internal pure returns (bytes memory) { return abi.encodePacked( targetTimeUs, uint32(targetBlockHint.length), @@ -70,8 +70,8 @@ library QueryTest { bytes memory blockId, bytes memory finality, uint8 numCallData, - bytes memory callData // Created with buildEthCallRecordBytes() - ) internal pure returns (bytes memory){ + bytes memory callData //Created with buildEthCallRecordBytes() + ) internal pure returns (bytes memory) { return abi.encodePacked( uint32(blockId.length), blockId, @@ -85,7 +85,7 @@ library QueryTest { function buildEthCallRecordBytes( address contractAddress, bytes memory callData - ) internal pure returns (bytes memory){ + ) internal pure returns (bytes memory) { return abi.encodePacked(contractAddress, uint32(callData.length), callData); } @@ -95,8 +95,8 @@ library QueryTest { uint64 dataSliceOffset, uint64 dataSliceLength, uint8 numAccounts, - bytes memory accounts // Each account is 32 bytes. - ) internal pure returns (bytes memory){ + bytes memory accounts //Each account is 32 bytes. + ) internal pure returns (bytes memory) { return abi.encodePacked( uint32(commitment.length), commitment, @@ -113,8 +113,8 @@ library QueryTest { uint64 minContextSlot, uint64 dataSliceOffset, uint64 dataSliceLength, - bytes[] memory pdas // Created with multiple calls to buildSolanaPdaEntry() - ) internal pure returns (bytes memory){ + bytes[] memory pdas //Created with multiple calls to buildSolanaPdaEntry() + ) internal pure returns (bytes memory) { uint numPdas = pdas.length; if (numPdas > type(uint8).max) revert SolanaTooManyPDAs(); @@ -128,20 +128,17 @@ library QueryTest { uint8(numPdas) ); - for (uint idx; idx < numPdas;) { + for (uint idx; idx < numPdas; ++idx) result = abi.encodePacked(result, pdas[idx]); - unchecked { ++idx; } - } - return result; } function buildSolanaPdaEntry( bytes32 programId, uint8 numSeeds, - bytes memory seeds // Created with buildSolanaPdaSeedBytes() - ) internal pure returns (bytes memory){ + bytes memory seeds //Created with buildSolanaPdaSeedBytes() + ) internal pure returns (bytes memory) { if (numSeeds > SOLANA_MAX_SEEDS) revert SolanaTooManySeeds(); @@ -151,21 +148,18 @@ library QueryTest { //packs the seeds for a PDA entry into an array of bytes. function buildSolanaPdaSeedBytes( bytes[] memory seeds - ) internal pure returns (bytes memory, uint8){ + ) internal pure returns (bytes memory, uint8) { uint numSeeds = seeds.length; if (numSeeds > SOLANA_MAX_SEEDS) revert SolanaTooManySeeds(); bytes memory result; - - for (uint idx; idx < numSeeds;) { + for (uint idx; idx < numSeeds; ++idx) { uint seedLen = seeds[idx].length; if (seedLen > SOLANA_MAX_SEED_LEN) revert SolanaSeedTooLong(); result = abi.encodePacked(result, abi.encodePacked(uint32(seedLen), seeds[idx])); - - unchecked { ++idx; } } return (result, uint8(numSeeds)); @@ -180,7 +174,7 @@ library QueryTest { bytes memory queryRequest, uint8 numPerChainResponses, bytes memory perChainResponses - ) internal pure returns (bytes memory){ + ) internal pure returns (bytes memory) { return abi.encodePacked( version, senderChainId, @@ -188,7 +182,7 @@ library QueryTest { uint32(queryRequest.length), queryRequest, numPerChainResponses, - perChainResponses // Each created by buildPerChainResponseBytes() + perChainResponses //Each created by buildPerChainResponseBytes() ); } @@ -196,7 +190,7 @@ library QueryTest { uint16 chainId, uint8 queryType, bytes memory responseBytes - ) internal pure returns (bytes memory){ + ) internal pure returns (bytes memory) { return abi.encodePacked(chainId, queryType, uint32(responseBytes.length), responseBytes); } @@ -205,8 +199,8 @@ library QueryTest { bytes32 blockHash, uint64 blockTimeUs, uint8 numResults, - bytes memory results // Created with buildEthCallResultBytes() - ) internal pure returns (bytes memory){ + bytes memory results //Created with buildEthCallResultBytes() + ) internal pure returns (bytes memory) { return abi.encodePacked(blockNumber, blockHash, blockTimeUs, numResults, results); } @@ -218,8 +212,8 @@ library QueryTest { bytes32 followingBlockHash, uint64 followingBlockTimeUs, uint8 numResults, - bytes memory results // Created with buildEthCallResultBytes() - ) internal pure returns (bytes memory){ + bytes memory results //Created with buildEthCallResultBytes() + ) internal pure returns (bytes memory) { return abi.encodePacked( targetBlockNumber, targetBlockHash, @@ -238,8 +232,8 @@ library QueryTest { bytes32 blockHash, uint64 blockTimeUs, uint8 numResults, - bytes memory results // Created with buildEthCallResultBytes() - ) internal pure returns (bytes memory){ + bytes memory results //Created with buildEthCallResultBytes() + ) internal pure returns (bytes memory) { return abi.encodePacked(blockNumber, blockHash, blockTimeUs, numResults, results); } @@ -254,8 +248,8 @@ library QueryTest { uint64 blockTimeUs, bytes32 blockHash, uint8 numResults, - bytes memory results // Created with buildEthCallResultBytes() - ) internal pure returns (bytes memory){ + bytes memory results //Created with buildEthCallResultBytes() + ) internal pure returns (bytes memory) { return abi.encodePacked(slotNumber, blockTimeUs, blockHash, numResults, results); } @@ -264,8 +258,8 @@ library QueryTest { uint64 blockTimeUs, bytes32 blockHash, uint8 numResults, - bytes memory results // Created with buildEthCallResultBytes() - ) internal pure returns (bytes memory){ + bytes memory results //Created with buildEthCallResultBytes() + ) internal pure returns (bytes memory) { return abi.encodePacked(slotNumber, blockTimeUs, blockHash, numResults, results); } } diff --git a/src/testing/WormholeOverride.sol b/src/testing/WormholeOverride.sol index 4a5dea8..85b8478 100644 --- a/src/testing/WormholeOverride.sol +++ b/src/testing/WormholeOverride.sol @@ -27,11 +27,12 @@ struct PublishedMessage { //use `using VaaEncoding for IWormhole.VM;` to convert VAAs to bytes via .encode() library VaaEncoding { - function encode(IWormhole.VM memory vaa) internal pure returns (bytes memory) { + function encode(IWormhole.VM memory vaa) internal pure returns (bytes memory) { unchecked { bytes memory sigs; for (uint i = 0; i < vaa.signatures.length; ++i) { - IWormhole.Signature memory sig = vaa.signatures[i]; - sigs = bytes.concat(sigs, abi.encodePacked(sig.guardianIndex, sig.r, sig.s, sig.v)); + IWormhole.Signature memory sig = vaa.signatures[i]; + uint8 v = sig.v - 27; //see https://github.com/wormhole-foundation/wormhole/blob/c35940ae9689f6df9e983d51425763509b74a80f/ethereum/contracts/Messages.sol#L174 + sigs = bytes.concat(sigs, abi.encodePacked(sig.guardianIndex, sig.r, sig.s, v)); } return abi.encodePacked( @@ -47,7 +48,7 @@ library VaaEncoding { vaa.consistencyLevel, vaa.payload ); - } + }} } //simple version of the library - should be sufficient for most use cases @@ -452,11 +453,18 @@ library AdvancedWormholeOverride { return sign(wormhole, pm, getSigningIndices(wormhole)); } + function sign( + IWormhole wormhole, + bytes32 hash + ) internal view returns (IWormhole.Signature[] memory signatures) { + return sign(wormhole, hash, getSigningIndices(wormhole)); + } + function sign( IWormhole wormhole, PublishedMessage memory pm, bytes memory signingGuardianIndices //treated as a packed uint8 array - ) internal view returns (IWormhole.VM memory vaa) { unchecked { + ) internal view returns (IWormhole.VM memory vaa) { vaa.version = WORMHOLE_VAA_VERSION; vaa.timestamp = pm.timestamp; vaa.nonce = pm.nonce; @@ -478,14 +486,21 @@ library AdvancedWormholeOverride { ); vaa.hash = keccak256(abi.encodePacked(keccak256(encodedBody))); - vaa.signatures = new IWormhole.Signature[](signingGuardianIndices.length); + vaa.signatures = sign(wormhole, vaa.hash, signingGuardianIndices); + } + + function sign( + IWormhole wormhole, + bytes32 hash, + bytes memory signingGuardianIndices //treated as a packed uint8 array + ) internal view returns (IWormhole.Signature[] memory signatures) { unchecked { + signatures = new IWormhole.Signature[](signingGuardianIndices.length); uint256[] memory guardianPrivateKeys = getGuardianPrivateKeys(wormhole); for (uint i = 0; i < signingGuardianIndices.length; ++i) { (uint8 gi, ) = signingGuardianIndices.asUint8(i); - (vaa.signatures[i].v, vaa.signatures[i].r, vaa.signatures[i].s) = - vm.sign(guardianPrivateKeys[gi], vaa.hash); - vaa.signatures[i].guardianIndex = gi; - vaa.signatures[i].v -= 27; + (signatures[i].v, signatures[i].r, signatures[i].s) = + vm.sign(guardianPrivateKeys[gi], hash); + signatures[i].guardianIndex = gi; } }} diff --git a/src/testing/WormholeRelayer/MockOffchainRelayer.sol b/src/testing/WormholeRelayer/MockOffchainRelayer.sol index bdf29e8..75c9469 100644 --- a/src/testing/WormholeRelayer/MockOffchainRelayer.sol +++ b/src/testing/WormholeRelayer/MockOffchainRelayer.sol @@ -11,11 +11,11 @@ import {toUniversalAddress, fromUniversalAddress} from "wormhole-sdk/Utils.sol"; import "wormhole-sdk/libraries/BytesParsing.sol"; import {CCTPMessageLib} from "wormhole-sdk/WormholeRelayer/CCTPBase.sol"; -import {VM_ADDRESS} from "../Constants.sol"; -import "../WormholeOverride.sol"; -import "../CctpOverride.sol"; -import "./DeliveryInstructionDecoder.sol"; -import "./ExecutionParameters.sol"; +import {VM_ADDRESS} from "wormhole-sdk/testing/Constants.sol"; +import "wormhole-sdk/testing/WormholeOverride.sol"; +import "wormhole-sdk/testing/CctpOverride.sol"; +import "wormhole-sdk/testing/WormholeRelayer/DeliveryInstructionDecoder.sol"; +import "wormhole-sdk/testing/WormholeRelayer/ExecutionParameters.sol"; using BytesParsing for bytes; diff --git a/src/testing/helpers/WormholeMock.sol b/src/testing/helpers/WormholeMock.sol deleted file mode 100644 index d0d6cc1..0000000 --- a/src/testing/helpers/WormholeMock.sol +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-License-Identifier: Apache 2 - -pragma solidity ^0.8.13; - -import { IWormhole } from '../../interfaces/IWormhole.sol'; - -contract WormholeMock is IWormhole { - constructor() {} - - // INIT_SIGNERS=["0xbeFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe"] - function publishMessage( - uint32 nonce, - bytes memory payload, - uint8 consistencyLevel - ) external payable override returns (uint64 sequence) {} - - function initialize() external override {} - - function parseAndVerifyVM( - bytes calldata encodedVM - ) external view override returns (VM memory vm, bool valid, string memory reason) {} - - function verifyVM( - VM memory vm - ) external view override returns (bool valid, string memory reason) {} - - function verifySignatures( - bytes32 hash, - Signature[] memory signatures, - GuardianSet memory guardianSet - ) external pure override returns (bool valid, string memory reason) { - uint8 lastIndex = 0; - uint256 guardianCount = guardianSet.keys.length; - for (uint i = 0; i < signatures.length; i++) { - Signature memory sig = signatures[i]; - address signatory = ecrecover(hash, sig.v, sig.r, sig.s); - // ecrecover returns 0 for invalid signatures. We explicitly require valid signatures to avoid unexpected - // behaviour due to the default storage slot value also being 0. - require(signatory != address(0), "ecrecover failed with signature"); - - /// Ensure that provided signature indices are ascending only - require(i == 0 || sig.guardianIndex > lastIndex, "signature indices must be ascending"); - lastIndex = sig.guardianIndex; - - /// @dev Ensure that the provided signature index is within the - /// bounds of the guardianSet. This is implicitly checked by the array - /// index operation below, so this check is technically redundant. - /// However, reverting explicitly here ensures that a bug is not - /// introduced accidentally later due to the nontrivial storage - /// semantics of solidity. - require(sig.guardianIndex < guardianCount, "guardian index out of bounds"); - - /// Check to see if the signer of the signature does not match a specific Guardian key at the provided index - if(signatory != guardianSet.keys[sig.guardianIndex]){ - return (false, "VM signature invalid"); - } - } - - /// If we are here, we've validated that the provided signatures are valid for the provided guardianSet - return (true, ""); - } - - function parseVM(bytes memory encodedVM) external pure override returns (VM memory vm) {} - - function quorum( - uint numGuardians - ) external pure override returns (uint numSignaturesRequiredForQuorum) {} - - function getGuardianSet(uint32 index) external pure override returns (GuardianSet memory) { - index = 0; - address[] memory keys = new address[](1); - keys[0] = 0xbeFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe; - - GuardianSet memory gset = GuardianSet({ - keys: keys, - expirationTime: 999999999 - }); - return gset; - } - - function getCurrentGuardianSetIndex() external view override returns (uint32) {} - - function getGuardianSetExpiry() external view override returns (uint32) {} - - function governanceActionIsConsumed(bytes32 hash) external view override returns (bool) {} - - function isInitialized(address impl) external view override returns (bool) {} - - function chainId() external view override returns (uint16) {} - - function isFork() external view override returns (bool) {} - - function governanceChainId() external view override returns (uint16) {} - - function governanceContract() external view override returns (bytes32) {} - - function messageFee() external view override returns (uint256) {} - - function evmChainId() external view override returns (uint256) {} - - function nextSequence(address emitter) external view override returns (uint64) {} - - function parseContractUpgrade( - bytes memory encodedUpgrade - ) external pure override returns (ContractUpgrade memory cu) {} - - function parseGuardianSetUpgrade( - bytes memory encodedUpgrade - ) external pure override returns (GuardianSetUpgrade memory gsu) {} - - function parseSetMessageFee( - bytes memory encodedSetMessageFee - ) external pure override returns (SetMessageFee memory smf) {} - - function parseTransferFees( - bytes memory encodedTransferFees - ) external pure override returns (TransferFees memory tf) {} - - function parseRecoverChainId( - bytes memory encodedRecoverChainId - ) external pure override returns (RecoverChainId memory rci) {} - - function submitContractUpgrade(bytes memory _vm) external override {} - - function submitSetMessageFee(bytes memory _vm) external override {} - - function submitNewGuardianSet(bytes memory _vm) external override {} - - function submitTransferFees(bytes memory _vm) external override {} - - function submitRecoverChainId(bytes memory _vm) external override {} -} diff --git a/test/QueryTest.t.sol b/test/QueryRequest.t.sol similarity index 85% rename from test/QueryTest.t.sol rename to test/QueryRequest.t.sol index 6fa3916..a131669 100644 --- a/test/QueryTest.t.sol +++ b/test/QueryRequest.t.sol @@ -3,11 +3,11 @@ pragma solidity ^0.8.4; import "forge-std/Test.sol"; -import "wormhole-sdk/testing/helpers/QueryTest.sol"; +import "wormhole-sdk/testing/QueryRequestBuilder.sol"; contract QueryRequestTest is Test { function test_buildOffChainQueryRequestBytes() public { - bytes memory req = QueryTest.buildOffChainQueryRequestBytes( + bytes memory req = QueryRequestBuilder.buildOffChainQueryRequestBytes( /* version */ 1, /* nonce */ 1, /* numPerChainQueries */ 1, @@ -17,7 +17,7 @@ contract QueryRequestTest is Test { } function test_buildPerChainRequestBytes() public { - bytes memory pcr = QueryTest.buildPerChainRequestBytes( + bytes memory pcr = QueryRequestBuilder.buildPerChainRequestBytes( /* chainId */ 2, /* queryType */ 1, /* queryBytes */ hex"00000005307837343402ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567" @@ -26,7 +26,7 @@ contract QueryRequestTest is Test { } function test_buildEthCallRequestBytes() public { - bytes memory ecr = QueryTest.buildEthCallRequestBytes( + bytes memory ecr = QueryRequestBuilder.buildEthCallRequestBytes( /* blockId */ "0x744", /* numCallData */ 2, /* callData */ hex"ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567" @@ -35,7 +35,7 @@ contract QueryRequestTest is Test { } function test_buildEthCallByTimestampRequestBytes() public { - bytes memory ecr = QueryTest.buildEthCallByTimestampRequestBytes( + bytes memory ecr = QueryRequestBuilder.buildEthCallByTimestampRequestBytes( /* targetTimeUs */ 0x10642ac0, /* targetBlockHint */ "0x15d", /* followingBlockHint */ "0x15e", @@ -46,7 +46,7 @@ contract QueryRequestTest is Test { } function test_buildEthCallWithFinalityRequestBytes() public { - bytes memory ecr = QueryTest.buildEthCallWithFinalityRequestBytes( + bytes memory ecr = QueryRequestBuilder.buildEthCallWithFinalityRequestBytes( /* blockId */ "0x1f8", /* finality */ "finalized", /* numCallData */ 2, @@ -56,12 +56,12 @@ contract QueryRequestTest is Test { } function test_buildEthCallRecordBytes() public { - bytes memory ecd1 = QueryTest.buildEthCallRecordBytes( + bytes memory ecd1 = QueryRequestBuilder.buildEthCallRecordBytes( /* contractAddress */ 0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E, /* callData */ hex"06fdde03" ); assertEq(ecd1, hex"ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03"); - bytes memory ecd2 = QueryTest.buildEthCallRecordBytes( + bytes memory ecd2 = QueryRequestBuilder.buildEthCallRecordBytes( /* contractAddress */ 0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E, /* callData */ hex"313ce567" ); @@ -69,7 +69,7 @@ contract QueryRequestTest is Test { } function test_buildSolanaAccountRequestBytes() public { - bytes memory ecr = QueryTest.buildSolanaAccountRequestBytes( + bytes memory ecr = QueryRequestBuilder.buildSolanaAccountRequestBytes( /* commitment */ "finalized", /* minContextSlot */ 8069, /* dataSliceOffset */ 10, @@ -87,10 +87,10 @@ contract QueryRequestTest is Test { bytes[] memory seeds = new bytes[](2); seeds[0] = hex"477561726469616e536574"; seeds[1] = hex"00000000"; - (bytes memory seedBytes, uint8 numSeeds) = QueryTest.buildSolanaPdaSeedBytes(seeds); + (bytes memory seedBytes, uint8 numSeeds) = QueryRequestBuilder.buildSolanaPdaSeedBytes(seeds); assertEq(seedBytes, hex"0000000b477561726469616e5365740000000400000000"); - pdas[0] = QueryTest.buildSolanaPdaEntry( + pdas[0] = QueryRequestBuilder.buildSolanaPdaEntry( programId, numSeeds, seedBytes @@ -101,10 +101,10 @@ contract QueryRequestTest is Test { bytes[] memory seeds2 = new bytes[](2); seeds2[0] = hex"477561726469616e536574"; seeds2[1] = hex"00000001"; - (bytes memory seedBytes2, uint8 numSeeds2) = QueryTest.buildSolanaPdaSeedBytes(seeds2); + (bytes memory seedBytes2, uint8 numSeeds2) = QueryRequestBuilder.buildSolanaPdaSeedBytes(seeds2); assertEq(seedBytes2, hex"0000000b477561726469616e5365740000000400000001"); - pdas[1] = QueryTest.buildSolanaPdaEntry( + pdas[1] = QueryRequestBuilder.buildSolanaPdaEntry( programId, numSeeds2, seedBytes2 @@ -112,7 +112,7 @@ contract QueryRequestTest is Test { assertEq(pdas[1], hex"02c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa020000000b477561726469616e5365740000000400000001"); assertEq(numSeeds2, uint8(seeds2.length)); - bytes memory ecr = QueryTest.buildSolanaPdaRequestBytes( + bytes memory ecr = QueryRequestBuilder.buildSolanaPdaRequestBytes( /* commitment */ "finalized", /* minContextSlot */ 2303, /* dataSliceOffset */ 12, @@ -127,23 +127,21 @@ contract QueryRequestTest is Test { bytes[] memory pdas = new bytes[](256); uint numPDAs = pdas.length; - for (uint idx; idx < numPDAs;) { + for (uint idx; idx < numPDAs; ++idx) { bytes[] memory seeds = new bytes[](2); seeds[0] = hex"477561726469616e536574"; seeds[1] = hex"00000000"; - (bytes memory seedBytes, uint8 numSeeds) = QueryTest.buildSolanaPdaSeedBytes(seeds); + (bytes memory seedBytes, uint8 numSeeds) = QueryRequestBuilder.buildSolanaPdaSeedBytes(seeds); - pdas[idx] = QueryTest.buildSolanaPdaEntry( + pdas[idx] = QueryRequestBuilder.buildSolanaPdaEntry( programId, numSeeds, seedBytes ); - - unchecked { ++idx; } } - vm.expectRevert(QueryTest.SolanaTooManyPDAs.selector); - QueryTest.buildSolanaPdaRequestBytes( + vm.expectRevert(QueryRequestBuilder.SolanaTooManyPDAs.selector); + QueryRequestBuilder.buildSolanaPdaRequestBytes( /* commitment */ "finalized", /* minContextSlot */ 2303, /* dataSliceOffset */ 12, @@ -156,29 +154,27 @@ contract QueryRequestTest is Test { bytes[] memory seeds = new bytes[](2); seeds[0] = hex"477561726469616e536574"; seeds[1] = hex"00000000"; - (bytes memory seedBytes,) = QueryTest.buildSolanaPdaSeedBytes(seeds); + (bytes memory seedBytes,) = QueryRequestBuilder.buildSolanaPdaSeedBytes(seeds); assertEq(seedBytes, hex"0000000b477561726469616e5365740000000400000000"); bytes32 programId = hex"02c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa"; - vm.expectRevert(QueryTest.SolanaTooManySeeds.selector); - QueryTest.buildSolanaPdaEntry( + vm.expectRevert(QueryRequestBuilder.SolanaTooManySeeds.selector); + QueryRequestBuilder.buildSolanaPdaEntry( programId, - uint8(QueryTest.SOLANA_MAX_SEEDS + 1), + uint8(QueryRequestBuilder.SOLANA_MAX_SEEDS + 1), seedBytes ); } function test_buildSolanaPdaSeedBytesTooManySeeds() public { - bytes[] memory seeds = new bytes[](QueryTest.SOLANA_MAX_SEEDS + 1); + bytes[] memory seeds = new bytes[](QueryRequestBuilder.SOLANA_MAX_SEEDS + 1); uint numSeeds = seeds.length; - for (uint idx; idx < numSeeds;) { + for (uint idx; idx < numSeeds; ++idx) seeds[idx] = "junk"; - unchecked { ++idx; } - } - vm.expectRevert(QueryTest.SolanaTooManySeeds.selector); - QueryTest.buildSolanaPdaSeedBytes(seeds); + vm.expectRevert(QueryRequestBuilder.SolanaTooManySeeds.selector); + QueryRequestBuilder.buildSolanaPdaSeedBytes(seeds); } function test_buildSolanaPdaSeedBytesSeedTooLong() public { @@ -186,14 +182,14 @@ contract QueryRequestTest is Test { seeds[0] = "junk"; seeds[1] = "This seed is too long!!!!!!!!!!!!"; - vm.expectRevert(QueryTest.SolanaSeedTooLong.selector); - QueryTest.buildSolanaPdaSeedBytes(seeds); + vm.expectRevert(QueryRequestBuilder.SolanaSeedTooLong.selector); + QueryRequestBuilder.buildSolanaPdaSeedBytes(seeds); } } contract QueryResponseTest is Test { function test_buildQueryResponseBytes() public { - bytes memory resp = QueryTest.buildQueryResponseBytes( + bytes memory resp = QueryRequestBuilder.buildQueryResponseBytes( /* version */ 1, /* senderChainId */ 0, /* signature */ hex"11b03bdbbe15a8f12b803d2193de5ddff72d92eaabd2763553ec3c3133182d1443719a05e2b65c87b923c6bd8aeff49f34937f90f3ab7cd33449388c60fa30a301", @@ -205,7 +201,7 @@ contract QueryResponseTest is Test { } function test_buildPerChainResponseBytes() public { - bytes memory pcr = QueryTest.buildPerChainResponseBytes( + bytes memory pcr = QueryRequestBuilder.buildPerChainResponseBytes( /* chainId */ 2, /* queryType */ 1, /* responseBytes */ hex"00000000000007446a0b819aee8945e659e37537a0bdbe03c06275be23e499819138d1eee8337e9b000000006ab13b8002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012" @@ -214,7 +210,7 @@ contract QueryResponseTest is Test { } function test_buildEthCallResponseBytes() public { - bytes memory ecr = QueryTest.buildEthCallResponseBytes( + bytes memory ecr = QueryRequestBuilder.buildEthCallResponseBytes( /* blockNumber */ 1860, /* blockHash */ hex"6a0b819aee8945e659e37537a0bdbe03c06275be23e499819138d1eee8337e9b", /* blockTimeUs */ 0x6ab13b80, @@ -225,7 +221,7 @@ contract QueryResponseTest is Test { } function test_buildEthCallByTimestampResponseBytes() public { - bytes memory ecr = QueryTest.buildEthCallByTimestampResponseBytes( + bytes memory ecr = QueryRequestBuilder.buildEthCallByTimestampResponseBytes( /* targetBlockNumber */ 349, /* targetBlockHash */ hex"966cd846f812be43c4ee2d310f962bc592ba944c66de878e53584b8e75c6051f", /* targetBlockTimeUs */ 0x10642ac0, @@ -239,7 +235,7 @@ contract QueryResponseTest is Test { } function test_buildEthCallWithFinalityResponseBytes() public { - bytes memory ecr = QueryTest.buildEthCallWithFinalityResponseBytes( + bytes memory ecr = QueryRequestBuilder.buildEthCallWithFinalityResponseBytes( /* blockNumber */ 1860, /* blockHash */ hex"6a0b819aee8945e659e37537a0bdbe03c06275be23e499819138d1eee8337e9b", /* blockTimeUs */ 0x6ab13b80, @@ -250,18 +246,18 @@ contract QueryResponseTest is Test { } function test_buildEthCallResultBytes() public { - bytes memory ecr1 = QueryTest.buildEthCallResultBytes( + bytes memory ecr1 = QueryRequestBuilder.buildEthCallResultBytes( /* result */ hex"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000" ); assertEq(ecr1, hex"000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000"); - bytes memory ecr2 = QueryTest.buildEthCallResultBytes( + bytes memory ecr2 = QueryRequestBuilder.buildEthCallResultBytes( /* result */ hex"0000000000000000000000000000000000000000000000000000000000000012" ); assertEq(ecr2, hex"000000200000000000000000000000000000000000000000000000000000000000000012"); } function test_buildSolanaAccountResponseBytes() public { - bytes memory ecr = QueryTest.buildSolanaAccountResponseBytes( + bytes memory ecr = QueryRequestBuilder.buildSolanaAccountResponseBytes( /* slotNumber */ 5603, /* blockTimeUs */ 0x610cdf2510500, /* blockHash */ hex"e0eca895a92c0347e30538cd07c50777440de58e896dd13ff86ef0dae3e12552", @@ -272,7 +268,7 @@ contract QueryResponseTest is Test { } function test_buildSolanaPdaResponseBytes() public { - bytes memory ecr = QueryTest.buildSolanaPdaResponseBytes( + bytes memory ecr = QueryRequestBuilder.buildSolanaPdaResponseBytes( /* slotNumber */ 2303, /* blockTimeUs */ 0x6115e3f6d7540, /* blockHash */ hex"e05035785e15056a8559815e71343ce31db2abf23f65b19c982b68aee7bf207b", diff --git a/test/QueryResponse.t.sol b/test/QueryResponse.t.sol index 247efc2..c1f909f 100644 --- a/test/QueryResponse.t.sol +++ b/test/QueryResponse.t.sol @@ -1,108 +1,18 @@ // SPDX-License-Identifier: Apache 2 -// forge test --match-contract QueryResponse - pragma solidity ^0.8.4; -import "../src/QueryResponse.sol"; import "forge-std/Test.sol"; -import "../src/testing/helpers/QueryTest.sol"; -import {WormholeMock} from "../src/testing/helpers/WormholeMock.sol"; - -//wrap library to allow testing via vm.expectRevert -contract QueryResponseLibWrapper { - function calcPrefixedResponseHash(bytes memory response) external pure returns (bytes32) { - return QueryResponseLib.calcPrefixedResponseHash(response); - } - - function parseAndVerifyQueryResponse( - address wormhole, - bytes memory response, - IWormhole.Signature[] memory signatures - ) external view returns (QueryResponse memory ret) { - return QueryResponseLib.parseAndVerifyQueryResponse(wormhole, response, signatures); - } - - function verifyQueryResponse( - address wormhole, - bytes memory response, - IWormhole.Signature[] memory signatures - ) external view { - return QueryResponseLib.verifyQueryResponse(wormhole, response, signatures); - } - - function parseQueryResponse( - bytes memory response - ) external pure returns (QueryResponse memory ret) { - return QueryResponseLib.parseQueryResponse(response); - } - - function parseEthCallQueryResponse( - PerChainQueryResponse memory pcr - ) external pure returns (EthCallQueryResponse memory ret) { - return QueryResponseLib.parseEthCallQueryResponse(pcr); - } - - function parseEthCallByTimestampQueryResponse( - PerChainQueryResponse memory pcr - ) external pure returns (EthCallByTimestampQueryResponse memory ret) { - return QueryResponseLib.parseEthCallByTimestampQueryResponse(pcr); - } - - function parseEthCallWithFinalityQueryResponse( - PerChainQueryResponse memory pcr - ) external pure returns (EthCallWithFinalityQueryResponse memory ret) { - return QueryResponseLib.parseEthCallWithFinalityQueryResponse(pcr); - } - - function parseSolanaAccountQueryResponse( - PerChainQueryResponse memory pcr - ) external pure returns (SolanaAccountQueryResponse memory ret) { - return QueryResponseLib.parseSolanaAccountQueryResponse(pcr); - } - - function parseSolanaPdaQueryResponse( - PerChainQueryResponse memory pcr - ) external pure returns (SolanaPdaQueryResponse memory ret) { - return QueryResponseLib.parseSolanaPdaQueryResponse(pcr); - } - function validateBlockTime( - uint64 blockTimeInMicroSeconds, - uint256 minBlockTimeInSeconds - ) external pure { - QueryResponseLib.validateBlockTime(blockTimeInMicroSeconds, minBlockTimeInSeconds); - } - - function validateBlockNum(uint64 blockNum, uint256 minBlockNum) external pure { - QueryResponseLib.validateBlockNum(blockNum, minBlockNum); - } - - function validateChainId( - uint16 chainId, - uint16[] memory validChainIds - ) external pure { - QueryResponseLib.validateChainId(chainId, validChainIds); - } - function validateEthCallRecord( - EthCallRecord[] memory ecds, - address[] memory validContractAddresses, - bytes4[] memory validFunctionSignatures - ) external pure { - QueryResponseLib.validateEthCallRecord(ecds, validContractAddresses, validFunctionSignatures); - } +import "wormhole-sdk/libraries/QueryResponse.sol"; +import "wormhole-sdk/testing/QueryRequestBuilder.sol"; +import "wormhole-sdk/testing/WormholeOverride.sol"; +import "./generated/QueryResponseTestWrapper.sol"; - function validateEthCallRecord( - EthCallRecord memory ecd, - address[] memory validContractAddresses, //empty array means accept all - bytes4[] memory validFunctionSignatures //empty array means accept all - ) external pure { - QueryResponseLib.validateEthCallRecord(ecd, validContractAddresses, validFunctionSignatures); - } -} +contract QueryResponseTest is Test { + using AdvancedWormholeOverride for IWormhole; -contract TestQueryResponse is Test { // Some happy case defaults uint8 version = 0x01; uint16 senderChainId = 0x0000; @@ -139,29 +49,20 @@ contract TestQueryResponse is Test { bytes solanaPdaPerChainResponses = hex"0001050000009b00000000000008ff0006115e3f6d7540e05035785e15056a8559815e71343ce31db2abf23f65b19c982b68aee7bf207b014fa9188b339cfd573a0778c5deaeeee94d4bcfb12b345bf8e417e5119dae773efd0000000000116ac000000000000000000002c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa0000001457cd18b7f8a4d91a2da9ab4af05d0fbece2dcd65"; bytes solanaPdaPerChainResponsesInner = hex"00000000000008ff0006115e3f6d7540e05035785e15056a8559815e71343ce31db2abf23f65b19c982b68aee7bf207b014fa9188b339cfd573a0778c5deaeeee94d4bcfb12b345bf8e417e5119dae773efd0000000000116ac000000000000000000002c806312cbe5b79ef8aa6c17e3f423d8fdfe1d46909fb1f6cdf65ee8e2e6faa0000001457cd18b7f8a4d91a2da9ab4af05d0fbece2dcd65"; - uint8 constant SIG_GUARDIAN_INDEX = 0; - address wormhole; - QueryResponseLibWrapper wrapper; + QueryResponseLibTestWrapper wrapper; function setUp() public { - wormhole = address(new WormholeMock()); - wrapper = new QueryResponseLibWrapper(); + vm.createSelectFork(vm.envString("TEST_RPC_URL")); + wormhole = vm.envAddress("TEST_WORMHOLE_ADDRESS"); + IWormhole(wormhole).setUpOverride(); + wrapper = new QueryResponseLibTestWrapper(); } - uint16 constant TEST_CHAIN_ID = 2; - address constant DEVNET_GUARDIAN = 0xbeFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe; - uint256 constant DEVNET_GUARDIAN_PRIVATE_KEY = 0xcfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0; - uint16 constant GOVERNANCE_CHAIN_ID = 1; - bytes32 constant GOVERNANCE_CONTRACT = 0x0000000000000000000000000000000000000000000000000000000000000004; - - function getSignature( + function sign( bytes memory response - ) internal pure returns (IWormhole.Signature[] memory signatures) { - bytes32 responseDigest = QueryResponseLib.calcPrefixedResponseHash(response); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(DEVNET_GUARDIAN_PRIVATE_KEY, responseDigest); - signatures = new IWormhole.Signature[](1); - signatures[0] = IWormhole.Signature(r, s, v, SIG_GUARDIAN_INDEX); + ) internal view returns (IWormhole.Signature[] memory signatures) { + return IWormhole(wormhole).sign(QueryResponseLib.calcPrefixedResponseHash(response)); } function concatenateQueryResponseBytesOffChain( @@ -175,13 +76,13 @@ contract TestQueryResponse is Test { uint8 _numPerChainResponses, bytes memory _perChainResponses ) internal pure returns (bytes memory){ - bytes memory queryRequest = QueryTest.buildOffChainQueryRequestBytes( + bytes memory queryRequest = QueryRequestBuilder.buildOffChainQueryRequestBytes( _queryRequestVersion, _queryRequestNonce, _numPerChainQueries, _perChainQueries ); - return QueryTest.buildQueryResponseBytes( + return QueryRequestBuilder.buildQueryResponseBytes( _version, _senderChainId, _signature, @@ -200,13 +101,13 @@ contract TestQueryResponse is Test { function test_verifyQueryResponse() public view { bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); - wrapper.verifyQueryResponse(wormhole, resp, getSignature(resp)); + wrapper.verifyQueryResponse(wormhole, resp, sign(resp)); // TODO: There are no assertions for this test } function test_parseAndVerifyQueryResponse() public { bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); - QueryResponse memory r = wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); + QueryResponse memory r = wrapper.parseAndVerifyQueryResponse(wormhole, resp, sign(resp)); assertEq(r.version, 1); assertEq(r.senderChainId, 0); assertEq(r.requestId, hex"ff0c222dc9e3655ec38e212e9792bf1860356d1277462b6bf747db865caca6fc08e6317b64ee3245264e371146b1d315d38c867fe1f69614368dc4430bb560f200"); @@ -368,7 +269,7 @@ contract TestQueryResponse is Test { function test_verifyQueryResponseForSolana() public view { bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, solanaAccountSignature, solanaAccountQueryRequestVersion, solanaAccountQueryRequestNonce, solanaAccountNumPerChainQueries, solanaAccountPerChainQueries, solanaAccountNumPerChainResponses, solanaAccountPerChainResponses); - wrapper.verifyQueryResponse(wormhole, resp, getSignature(resp)); + wrapper.verifyQueryResponse(wormhole, resp, sign(resp)); // TODO: There are no assertions for this test } @@ -555,75 +456,98 @@ contract TestQueryResponse is Test { *********** FUZZ TESTS ************* ***********************************/ - function testFuzz_parseAndVerifyQueryResponse_fuzzVersion(uint8 _version) public { + function testFuzz_parseAndVerifyQueryResponse_version( + uint8 _version + ) public { vm.assume(_version != 1); bytes memory resp = concatenateQueryResponseBytesOffChain(_version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); vm.expectRevert(InvalidResponseVersion.selector); - wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); + wrapper.parseAndVerifyQueryResponse(wormhole, resp, sign(resp)); } - function testFuzz_parseAndVerifyQueryResponse_fuzzSenderChainId(uint16 _senderChainId) public { + function testFuzz_parseAndVerifyQueryResponse_senderChainId( + uint16 _senderChainId + ) public { vm.assume(_senderChainId != 0); bytes memory resp = concatenateQueryResponseBytesOffChain(version, _senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); // This could revert for multiple reasons. But the checkLength to ensure all the bytes are consumed is the backstop. vm.expectRevert(); - wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); + wrapper.parseAndVerifyQueryResponse(wormhole, resp, sign(resp)); } - function testFuzz_parseAndVerifyQueryResponse_fuzzSignatureHappyCase(bytes memory _signature) public { + function testFuzz_parseAndVerifyQueryResponse_signatureHappyCase( + bytes memory _signature + ) public { // This signature isn't validated in the QueryResponse library, therefore it could be an 65 byte hex string vm.assume(_signature.length == 65); bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, _signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); - QueryResponse memory r = wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); + QueryResponse memory r = wrapper.parseAndVerifyQueryResponse(wormhole, resp, sign(resp)); assertEq(r.requestId, _signature); } - function testFuzz_parseAndVerifyQueryResponse_fuzzSignatureUnhappyCase(bytes memory _signature) public { + function testFuzz_parseAndVerifyQueryResponse_signatureUnhappyCase( + bytes memory _signature + ) public { // A signature that isn't 65 bytes long will always lead to a revert. The type of revert is unknown since it could be one of many. vm.assume(_signature.length != 65); bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, _signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); vm.expectRevert(); - wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); + wrapper.parseAndVerifyQueryResponse(wormhole, resp, sign(resp)); } - function testFuzz_parseAndVerifyQueryResponse_fuzzQueryRequestLen(uint32 _queryRequestLen, bytes calldata _perChainQueries) public { + function testFuzz_parseAndVerifyQueryResponse_fuzzQueryRequestLen( + uint32 _queryRequestLen, + bytes calldata _perChainQueries + ) public { // We add 6 to account for version + nonce + numPerChainQueries vm.assume(_queryRequestLen != _perChainQueries.length + 6); bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, _perChainQueries, numPerChainResponses, perChainResponses); vm.expectRevert(); - wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); + wrapper.parseAndVerifyQueryResponse(wormhole, resp, sign(resp)); } - function testFuzz_parseAndVerifyQueryResponse_fuzzQueryRequestVersion(uint8 _version, uint8 _queryRequestVersion) public { + function testFuzz_parseAndVerifyQueryResponse_queryRequestVersion( + uint8 _version, + uint8 _queryRequestVersion + ) public { vm.assume(_version != _queryRequestVersion); bytes memory resp = concatenateQueryResponseBytesOffChain(_version, senderChainId, signature, _queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); vm.expectRevert(); - wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); + wrapper.parseAndVerifyQueryResponse(wormhole, resp, sign(resp)); } - function testFuzz_parseAndVerifyQueryResponse_fuzzQueryRequestNonce(uint32 _queryRequestNonce) public { + function testFuzz_parseAndVerifyQueryResponse_queryRequestNonce( + uint32 _queryRequestNonce + ) public { bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, _queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); - QueryResponse memory r = wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); + QueryResponse memory r = wrapper.parseAndVerifyQueryResponse(wormhole, resp, sign(resp)); assertEq(r.nonce, _queryRequestNonce); } - function testFuzz_parseAndVerifyQueryResponse_fuzzNumPerChainQueriesAndResponses(uint8 _numPerChainQueries, uint8 _numPerChainResponses) public { + function testFuzz_parseAndVerifyQueryResponse_numPerChainQueriesAndResponses( + uint8 _numPerChainQueries, + uint8 _numPerChainResponses + ) public { vm.assume(_numPerChainQueries != _numPerChainResponses); bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, _numPerChainQueries, perChainQueries, _numPerChainResponses, perChainResponses); vm.expectRevert(); - wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); + wrapper.parseAndVerifyQueryResponse(wormhole, resp, sign(resp)); } - function testFuzz_parseAndVerifyQueryResponse_fuzzChainIds(uint16 _requestChainId, uint16 _responseChainId, uint256 _requestQueryType) public { + function testFuzz_parseAndVerifyQueryResponse_chainIds( + uint16 _requestChainId, + uint16 _responseChainId, + uint256 _requestQueryType + ) public { vm.assume(_requestChainId != _responseChainId); _requestQueryType = bound(_requestQueryType, QueryType.min(), QueryType.max()); @@ -631,10 +555,13 @@ contract TestQueryResponse is Test { bytes memory packedPerChainResponses = abi.encodePacked(_responseChainId, uint8(_requestQueryType), uint32(perChainResponsesInner.length), perChainResponsesInner); bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, packedPerChainQueries, numPerChainResponses, packedPerChainResponses); vm.expectRevert(ChainIdMismatch.selector); - wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); + wrapper.parseAndVerifyQueryResponse(wormhole, resp, sign(resp)); } - function testFuzz_parseAndVerifyQueryResponse_fuzzMistmatchedRequestType(uint256 _requestQueryType, uint256 _responseQueryType) public { + function testFuzz_parseAndVerifyQueryResponse_mistmatchedRequestType( + uint256 _requestQueryType, + uint256 _responseQueryType + ) public { _requestQueryType = bound(_requestQueryType, QueryType.min(), QueryType.max()); _responseQueryType = bound(_responseQueryType, QueryType.min(), QueryType.max()); vm.assume(_requestQueryType != _responseQueryType); @@ -643,43 +570,48 @@ contract TestQueryResponse is Test { bytes memory packedPerChainResponses = abi.encodePacked(uint16(0x0005), uint8(_responseQueryType), uint32(perChainResponsesInner.length), perChainResponsesInner); bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, packedPerChainQueries, numPerChainResponses, packedPerChainResponses); vm.expectRevert(RequestTypeMismatch.selector); - wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); + wrapper.parseAndVerifyQueryResponse(wormhole, resp, sign(resp)); } - function testFuzz_parseAndVerifyQueryResponse_fuzzUnsupportedRequestType(uint8 _requestQueryType) public { + function testFuzz_parseAndVerifyQueryResponse_unsupportedRequestType( + uint8 _requestQueryType + ) public { vm.assume(!QueryType.isValid(_requestQueryType)); bytes memory packedPerChainQueries = abi.encodePacked(uint16(0x0005), uint8(_requestQueryType), uint32(perChainQueriesInner.length), perChainQueriesInner); bytes memory packedPerChainResponses = abi.encodePacked(uint16(0x0005), uint8(_requestQueryType), uint32(perChainResponsesInner.length), perChainResponsesInner); bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, packedPerChainQueries, numPerChainResponses, packedPerChainResponses); vm.expectRevert(abi.encodeWithSelector(UnsupportedQueryType.selector, _requestQueryType)); - wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); + wrapper.parseAndVerifyQueryResponse(wormhole, resp, sign(resp)); } - function testFuzz_parseAndVerifyQueryResponse_fuzzQueryBytesLength(uint32 _queryLength) public { + function testFuzz_parseAndVerifyQueryResponse_queryBytesLength( + uint32 _queryLength + ) public { vm.assume(_queryLength != uint32(perChainQueriesInner.length)); bytes memory packedPerChainQueries = abi.encodePacked(uint16(0x0005), uint8(0x01), _queryLength, perChainQueriesInner); bytes memory packedPerChainResponses = abi.encodePacked(uint16(0x0005), uint8(0x01), uint32(perChainResponsesInner.length), perChainResponsesInner); bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, packedPerChainQueries, numPerChainResponses, packedPerChainResponses); vm.expectRevert(); - wrapper.parseAndVerifyQueryResponse(wormhole, resp, getSignature(resp)); + wrapper.parseAndVerifyQueryResponse(wormhole, resp, sign(resp)); } function testFuzz_verifyQueryResponse_validSignature(bytes calldata resp) public view { // This should pass with a valid signature of any payload - wrapper.verifyQueryResponse(wormhole, resp, getSignature(resp)); - } - - function testFuzz_verifyQueryResponse_invalidSignature(bytes calldata resp, uint256 privateKey) public { - vm.assume(privateKey != DEVNET_GUARDIAN_PRIVATE_KEY); - // Less than secp256k1 curve - vm.assume(privateKey < 115792089237316195423570985008687907852837564279074904382605163141518161494337); - vm.assume(privateKey != 0); - - (uint8 sigV, bytes32 sigR, bytes32 sigS) = vm.sign(privateKey, wrapper.calcPrefixedResponseHash(resp)); - IWormhole.Signature[] memory signatures = new IWormhole.Signature[](1); - signatures[0] = IWormhole.Signature(sigR, sigS, sigV, SIG_GUARDIAN_INDEX); + wrapper.verifyQueryResponse(wormhole, resp, sign(resp)); + } + + function testFuzz_verifyQueryResponse_invalidSignature( + bytes calldata resp, + uint8 sigV, + bytes32 sigR, + bytes32 sigS, + uint8 sigIndex + ) public { + IWormhole.Signature[] memory signatures = sign(resp); + uint sigI = bound(sigIndex, 0, signatures.length-1); + signatures[sigI] = IWormhole.Signature(sigR, sigS, sigV, signatures[sigI].guardianIndex); vm.expectRevert(VerificationFailed.selector); wrapper.verifyQueryResponse(wormhole, resp, signatures); } @@ -690,17 +622,32 @@ contract TestQueryResponse is Test { bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); bytes32 responseDigest = keccak256(abi.encodePacked(responsePrefix, keccak256(resp))); - (uint8 sigV, bytes32 sigR, bytes32 sigS) = vm.sign(DEVNET_GUARDIAN_PRIVATE_KEY, responseDigest); - IWormhole.Signature[] memory signatures = new IWormhole.Signature[](1); - signatures[0] = IWormhole.Signature(sigR, sigS, sigV, SIG_GUARDIAN_INDEX); + IWormhole.Signature[] memory signatures = IWormhole(wormhole).sign(responseDigest); vm.expectRevert(VerificationFailed.selector); wrapper.verifyQueryResponse(wormhole, resp, signatures); } + function testFuzz_verifyQueryResponse_noQuorum( + bytes calldata resp, + uint8 sigCount + ) public { + IWormhole.Signature[] memory signatures = sign(resp); + uint sigC = bound(sigCount, 0, signatures.length-1); + IWormhole.Signature[] memory signaturesToUse = new IWormhole.Signature[](sigC); + for (uint i = 0; i < sigC; ++i) + signaturesToUse[i] = signatures[i]; + + vm.expectRevert(VerificationFailed.selector); + wrapper.verifyQueryResponse(wormhole, resp, signaturesToUse); + } + uint64 constant private MICROSECONDS_PER_SECOND = QueryResponseLib.MICROSECONDS_PER_SECOND; uint64 constant private MAX_SECONDS = type(uint64).max/MICROSECONDS_PER_SECOND; - function testFuzz_validateBlockTime_success(uint64 _blockTime, uint64 _minBlockTime) public view { + function testFuzz_validateBlockTime_success( + uint64 _blockTime, + uint64 _minBlockTime + ) public view { //assure: blockTime >= minBlockTime _minBlockTime = uint64(bound(_minBlockTime, 0, MAX_SECONDS)); _blockTime = uint64(bound(_blockTime, _minBlockTime, MAX_SECONDS)); @@ -708,7 +655,10 @@ contract TestQueryResponse is Test { wrapper.validateBlockTime(_blockTime * MICROSECONDS_PER_SECOND, _minBlockTime); } - function testFuzz_validateBlockTime_fail(uint64 _blockTime, uint256 _minBlockTime) public { + function testFuzz_validateBlockTime_fail( + uint64 _blockTime, + uint256 _minBlockTime + ) public { //assure: blockTime < minBlockTime vm.assume(_minBlockTime > 0); uint upperBound = _minBlockTime <= MAX_SECONDS ? _minBlockTime-1 : MAX_SECONDS; @@ -718,14 +668,20 @@ contract TestQueryResponse is Test { wrapper.validateBlockTime(_blockTime * MICROSECONDS_PER_SECOND, _minBlockTime); } - function testFuzz_validateBlockNum_success(uint64 _blockNum, uint64 _minBlockNum) public view { + function testFuzz_validateBlockNum_success( + uint64 _blockNum, + uint64 _minBlockNum + ) public view { //assure: blockNum >= minBlockNum _blockNum = uint64(bound(_blockNum, _minBlockNum, type(uint64).max)); wrapper.validateBlockNum(_blockNum, _minBlockNum); } - function testFuzz_validateBlockNum_fail(uint256 _blockNum, uint256 _minBlockNum) public { + function testFuzz_validateBlockNum_fail( + uint64 _blockNum, + uint64 _minBlockNum + ) public { //assure: blockNum < minBlockNum vm.assume(_minBlockNum > 0); _blockNum = uint64(bound(_blockNum, 0, _minBlockNum-1)); @@ -734,23 +690,34 @@ contract TestQueryResponse is Test { wrapper.validateBlockNum(uint64(_blockNum), _minBlockNum); } - function testFuzz_validateChainId_success(uint256 _validChainIndex, uint16[] memory _validChainIds) public view { + function testFuzz_validateChainId_success( + uint256 _validChainIndex, + uint16[] memory _validChainIds + ) public view { vm.assume(_validChainIds.length > 0); _validChainIndex %= _validChainIds.length; wrapper.validateChainId(_validChainIds[_validChainIndex], _validChainIds); } - function testFuzz_validateChainId_fail(uint16 _chainId, uint16[] memory _validChainIds) public { - for (uint16 i = 0; i < _validChainIds.length; ++i) { + function testFuzz_validateChainId_fail( + uint16 _chainId, + uint16[] memory _validChainIds + ) public { + for (uint16 i = 0; i < _validChainIds.length; ++i) vm.assume(_chainId != _validChainIds[i]); - } vm.expectRevert(InvalidChainId.selector); wrapper.validateChainId(_chainId, _validChainIds); } - function testFuzz_validateEthCallRecord_success(bytes memory randomBytes, uint256 _contractAddressIndex, uint256 _functionSignatureIndex, address[] memory _validContractAddresses, bytes4[] memory _validFunctionSignatures) public view { + function testFuzz_validateEthCallRecord_success( + bytes memory randomBytes, + uint256 _contractAddressIndex, + uint256 _functionSignatureIndex, + address[] memory _validContractAddresses, + bytes4[] memory _validFunctionSignatures + ) public view { vm.assume(randomBytes.length >= 4); vm.assume(_validContractAddresses.length > 0); _contractAddressIndex %= _validContractAddresses.length; @@ -766,7 +733,12 @@ contract TestQueryResponse is Test { wrapper.validateEthCallRecord(callData, _validContractAddresses, _validFunctionSignatures); } - function testFuzz_validateEthCallRecord_successZeroSignatures(bytes4 randomSignature, bytes memory randomBytes, uint256 _contractAddressIndex, address[] memory _validContractAddresses) public view { + function testFuzz_validateEthCallRecord_successZeroSignatures( + bytes4 randomSignature, + bytes memory randomBytes, + uint256 _contractAddressIndex, + address[] memory _validContractAddresses + ) public view { vm.assume(_validContractAddresses.length > 0); _contractAddressIndex %= _validContractAddresses.length; @@ -781,7 +753,12 @@ contract TestQueryResponse is Test { wrapper.validateEthCallRecord(callData, _validContractAddresses, validSignatures); } - function testFuzz_validateEthCallRecord_successZeroAddresses(address randomAddress, bytes memory randomBytes, uint256 _functionSignatureIndex, bytes4[] memory _validFunctionSignatures) public view { + function testFuzz_validateEthCallRecord_successZeroAddresses( + address randomAddress, + bytes memory randomBytes, + uint256 _functionSignatureIndex, + bytes4[] memory _validFunctionSignatures + ) public view { vm.assume(randomBytes.length >= 4); vm.assume(_validFunctionSignatures.length > 0); _functionSignatureIndex %= _validFunctionSignatures.length; @@ -797,15 +774,19 @@ contract TestQueryResponse is Test { wrapper.validateEthCallRecord(callData, validAddresses, _validFunctionSignatures); } - function testFuzz_validateEthCallRecord_failSignature(bytes memory randomBytes, uint256 _contractAddressIndex, address[] memory _validContractAddresses, bytes4[] memory _validFunctionSignatures) public { + function testFuzz_validateEthCallRecord_failSignature( + bytes memory randomBytes, + uint256 _contractAddressIndex, + address[] memory _validContractAddresses, + bytes4[] memory _validFunctionSignatures + ) public { vm.assume(randomBytes.length >= 4); vm.assume(_validContractAddresses.length > 0); _contractAddressIndex %= _validContractAddresses.length; vm.assume(_validFunctionSignatures.length > 0); - for (uint256 i = 0; i < _validFunctionSignatures.length; ++i) { + for (uint i = 0; i < _validFunctionSignatures.length; ++i) vm.assume(bytes4(randomBytes) != _validFunctionSignatures[i]); - } EthCallRecord memory callData = EthCallRecord({ contractAddress: _validContractAddresses[_contractAddressIndex], @@ -817,14 +798,19 @@ contract TestQueryResponse is Test { wrapper.validateEthCallRecord(callData, _validContractAddresses, _validFunctionSignatures); } - function testFuzz_validateEthCallRecord_failAddress(bytes memory randomBytes, address randomAddress, uint256 _functionSignatureIndex, address[] memory _validContractAddresses, bytes4[] memory _validFunctionSignatures) public { + function testFuzz_validateEthCallRecord_failAddress( + bytes memory randomBytes, + address randomAddress, + uint256 _functionSignatureIndex, + address[] memory _validContractAddresses, + bytes4[] memory _validFunctionSignatures + ) public { vm.assume(_validFunctionSignatures.length > 0); _functionSignatureIndex %= _validFunctionSignatures.length; vm.assume(_validContractAddresses.length > 0); - for (uint256 i = 0; i < _validContractAddresses.length; ++i) { + for (uint i = 0; i < _validContractAddresses.length; ++i) vm.assume(randomAddress != _validContractAddresses[i]); - } EthCallRecord memory callData = EthCallRecord({ contractAddress: randomAddress, @@ -836,7 +822,14 @@ contract TestQueryResponse is Test { wrapper.validateEthCallRecord(callData, _validContractAddresses, _validFunctionSignatures); } - function testFuzz_validateMultipleEthCallRecord_success(uint8 numInputs, bytes memory randomBytes, uint256 _contractAddressIndex, uint256 _functionSignatureIndex, address[] memory _validContractAddresses, bytes4[] memory _validFunctionSignatures) public view { + function testFuzz_validateMultipleEthCallRecord_success( + uint8 numInputs, + bytes memory randomBytes, + uint256 _contractAddressIndex, + uint256 _functionSignatureIndex, + address[] memory _validContractAddresses, + bytes4[] memory _validFunctionSignatures + ) public view { vm.assume(_validContractAddresses.length > 0); _contractAddressIndex %= _validContractAddresses.length; vm.assume(_validFunctionSignatures.length > 0); @@ -844,13 +837,12 @@ contract TestQueryResponse is Test { EthCallRecord[] memory callDatas = new EthCallRecord[](numInputs); - for (uint256 i = 0; i < numInputs; ++i) { + for (uint i = 0; i < numInputs; ++i) callDatas[i] = EthCallRecord({ contractAddress: _validContractAddresses[_contractAddressIndex], callData: bytes.concat(_validFunctionSignatures[_functionSignatureIndex], randomBytes), result: randomBytes - }); - } + }); wrapper.validateEthCallRecord(callDatas, _validContractAddresses, _validFunctionSignatures); } diff --git a/test/generated/BytesParsingTestWrapper.sol b/test/generated/BytesParsingTestWrapper.sol index 177bfad..4df3d88 100644 --- a/test/generated/BytesParsingTestWrapper.sol +++ b/test/generated/BytesParsingTestWrapper.sol @@ -6,7 +6,7 @@ import "wormhole-sdk/libraries/BytesParsing.sol"; // This file was auto-generated by wormhole-solidity-sdk gen/libraryTestWrapper.ts contract BytesParsingTestWrapper { - function checkBound(uint offset, uint length) external pure { + function checkBound(uint offset, uint length) external pure { BytesParsing.checkBound(offset, length); } @@ -14,7 +14,7 @@ contract BytesParsingTestWrapper { bytes calldata encoded, uint offset, uint length - ) external pure returns (bytes memory ret, uint nextOffset) { + ) external pure returns (bytes memory ret, uint nextOffset) { return BytesParsing.sliceCdUnchecked(encoded, offset, length); } @@ -22,11 +22,11 @@ contract BytesParsingTestWrapper { bytes calldata encoded, uint offset, uint length - ) external pure returns (bytes memory ret, uint nextOffset) { + ) external pure returns (bytes memory ret, uint nextOffset) { return BytesParsing.sliceUnchecked(encoded, offset, length); } - function checkLengthCd(bytes calldata encoded, uint256 expected) external pure { + function checkLengthCd(bytes calldata encoded, uint256 expected) external pure { BytesParsing.checkLengthCd(encoded, expected); } @@ -34,11 +34,11 @@ contract BytesParsingTestWrapper { bytes calldata encoded, uint offset, uint length - ) external pure returns (bytes memory ret, uint nextOffset) { + ) external pure returns (bytes memory ret, uint nextOffset) { return BytesParsing.sliceCd(encoded, offset, length); } - function checkLength(bytes calldata encoded, uint256 expected) external pure { + function checkLength(bytes calldata encoded, uint256 expected) external pure { BytesParsing.checkLength(encoded, expected); } @@ -46,1111 +46,1111 @@ contract BytesParsingTestWrapper { bytes calldata encoded, uint offset, uint length - ) external pure returns (bytes memory ret, uint nextOffset) { + ) external pure returns (bytes memory ret, uint nextOffset) { return BytesParsing.slice(encoded, offset, length); } - function sliceUint8PrefixedCdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes memory ret, uint nextOffset) { + function sliceUint8PrefixedCdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes memory ret, uint nextOffset) { return BytesParsing.sliceUint8PrefixedCdUnchecked(encoded, offset); } - function sliceUint8PrefixedCd(bytes calldata encoded, uint offset) external pure returns (bytes memory ret, uint nextOffset) { + function sliceUint8PrefixedCd(bytes calldata encoded, uint offset) external pure returns (bytes memory ret, uint nextOffset) { return BytesParsing.sliceUint8PrefixedCd(encoded, offset); } - function sliceUint8PrefixedUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes memory ret, uint nextOffset) { + function sliceUint8PrefixedUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes memory ret, uint nextOffset) { return BytesParsing.sliceUint8PrefixedUnchecked(encoded, offset); } - function sliceUint8Prefixed(bytes calldata encoded, uint offset) external pure returns (bytes memory ret, uint nextOffset) { + function sliceUint8Prefixed(bytes calldata encoded, uint offset) external pure returns (bytes memory ret, uint nextOffset) { return BytesParsing.sliceUint8Prefixed(encoded, offset); } - function sliceUint16PrefixedCdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes memory ret, uint nextOffset) { + function sliceUint16PrefixedCdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes memory ret, uint nextOffset) { return BytesParsing.sliceUint16PrefixedCdUnchecked(encoded, offset); } - function sliceUint16PrefixedCd(bytes calldata encoded, uint offset) external pure returns (bytes memory ret, uint nextOffset) { + function sliceUint16PrefixedCd(bytes calldata encoded, uint offset) external pure returns (bytes memory ret, uint nextOffset) { return BytesParsing.sliceUint16PrefixedCd(encoded, offset); } - function sliceUint16PrefixedUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes memory ret, uint nextOffset) { + function sliceUint16PrefixedUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes memory ret, uint nextOffset) { return BytesParsing.sliceUint16PrefixedUnchecked(encoded, offset); } - function sliceUint16Prefixed(bytes calldata encoded, uint offset) external pure returns (bytes memory ret, uint nextOffset) { + function sliceUint16Prefixed(bytes calldata encoded, uint offset) external pure returns (bytes memory ret, uint nextOffset) { return BytesParsing.sliceUint16Prefixed(encoded, offset); } - function sliceUint32PrefixedCdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes memory ret, uint nextOffset) { + function sliceUint32PrefixedCdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes memory ret, uint nextOffset) { return BytesParsing.sliceUint32PrefixedCdUnchecked(encoded, offset); } - function sliceUint32PrefixedCd(bytes calldata encoded, uint offset) external pure returns (bytes memory ret, uint nextOffset) { + function sliceUint32PrefixedCd(bytes calldata encoded, uint offset) external pure returns (bytes memory ret, uint nextOffset) { return BytesParsing.sliceUint32PrefixedCd(encoded, offset); } - function sliceUint32PrefixedUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes memory ret, uint nextOffset) { + function sliceUint32PrefixedUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes memory ret, uint nextOffset) { return BytesParsing.sliceUint32PrefixedUnchecked(encoded, offset); } - function sliceUint32Prefixed(bytes calldata encoded, uint offset) external pure returns (bytes memory ret, uint nextOffset) { + function sliceUint32Prefixed(bytes calldata encoded, uint offset) external pure returns (bytes memory ret, uint nextOffset) { return BytesParsing.sliceUint32Prefixed(encoded, offset); } - function asAddressCdUnchecked(bytes calldata encoded, uint offset) external pure returns (address ret, uint nextOffset) { + function asAddressCdUnchecked(bytes calldata encoded, uint offset) external pure returns (address ret, uint nextOffset) { return BytesParsing.asAddressCdUnchecked(encoded, offset); } - function asAddressCd(bytes calldata encoded, uint offset) external pure returns (address ret, uint nextOffset) { + function asAddressCd(bytes calldata encoded, uint offset) external pure returns (address ret, uint nextOffset) { return BytesParsing.asAddressCd(encoded, offset); } - function asAddressUnchecked(bytes calldata encoded, uint offset) external pure returns (address ret, uint nextOffset) { + function asAddressUnchecked(bytes calldata encoded, uint offset) external pure returns (address ret, uint nextOffset) { return BytesParsing.asAddressUnchecked(encoded, offset); } - function asAddress(bytes calldata encoded, uint offset) external pure returns (address ret, uint nextOffset) { + function asAddress(bytes calldata encoded, uint offset) external pure returns (address ret, uint nextOffset) { return BytesParsing.asAddress(encoded, offset); } - function asBoolCdUnchecked(bytes calldata encoded, uint offset) external pure returns (bool ret, uint nextOffset) { + function asBoolCdUnchecked(bytes calldata encoded, uint offset) external pure returns (bool ret, uint nextOffset) { return BytesParsing.asBoolCdUnchecked(encoded, offset); } - function asBoolCd(bytes calldata encoded, uint offset) external pure returns (bool ret, uint nextOffset) { + function asBoolCd(bytes calldata encoded, uint offset) external pure returns (bool ret, uint nextOffset) { return BytesParsing.asBoolCd(encoded, offset); } - function asBoolUnchecked(bytes calldata encoded, uint offset) external pure returns (bool ret, uint nextOffset) { + function asBoolUnchecked(bytes calldata encoded, uint offset) external pure returns (bool ret, uint nextOffset) { return BytesParsing.asBoolUnchecked(encoded, offset); } - function asBool(bytes calldata encoded, uint offset) external pure returns (bool ret, uint nextOffset) { + function asBool(bytes calldata encoded, uint offset) external pure returns (bool ret, uint nextOffset) { return BytesParsing.asBool(encoded, offset); } - function asUint8CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint8 ret, uint nextOffset) { + function asUint8CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint8 ret, uint nextOffset) { return BytesParsing.asUint8CdUnchecked(encoded, offset); } - function asUint8Cd(bytes calldata encoded, uint offset) external pure returns (uint8 ret, uint nextOffset) { + function asUint8Cd(bytes calldata encoded, uint offset) external pure returns (uint8 ret, uint nextOffset) { return BytesParsing.asUint8Cd(encoded, offset); } - function asUint8Unchecked(bytes calldata encoded, uint offset) external pure returns (uint8 ret, uint nextOffset) { + function asUint8Unchecked(bytes calldata encoded, uint offset) external pure returns (uint8 ret, uint nextOffset) { return BytesParsing.asUint8Unchecked(encoded, offset); } - function asUint8(bytes calldata encoded, uint offset) external pure returns (uint8 ret, uint nextOffset) { + function asUint8(bytes calldata encoded, uint offset) external pure returns (uint8 ret, uint nextOffset) { return BytesParsing.asUint8(encoded, offset); } - function asUint16CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint16 ret, uint nextOffset) { + function asUint16CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint16 ret, uint nextOffset) { return BytesParsing.asUint16CdUnchecked(encoded, offset); } - function asUint16Cd(bytes calldata encoded, uint offset) external pure returns (uint16 ret, uint nextOffset) { + function asUint16Cd(bytes calldata encoded, uint offset) external pure returns (uint16 ret, uint nextOffset) { return BytesParsing.asUint16Cd(encoded, offset); } - function asUint16Unchecked(bytes calldata encoded, uint offset) external pure returns (uint16 ret, uint nextOffset) { + function asUint16Unchecked(bytes calldata encoded, uint offset) external pure returns (uint16 ret, uint nextOffset) { return BytesParsing.asUint16Unchecked(encoded, offset); } - function asUint16(bytes calldata encoded, uint offset) external pure returns (uint16 ret, uint nextOffset) { + function asUint16(bytes calldata encoded, uint offset) external pure returns (uint16 ret, uint nextOffset) { return BytesParsing.asUint16(encoded, offset); } - function asUint24CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint24 ret, uint nextOffset) { + function asUint24CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint24 ret, uint nextOffset) { return BytesParsing.asUint24CdUnchecked(encoded, offset); } - function asUint24Cd(bytes calldata encoded, uint offset) external pure returns (uint24 ret, uint nextOffset) { + function asUint24Cd(bytes calldata encoded, uint offset) external pure returns (uint24 ret, uint nextOffset) { return BytesParsing.asUint24Cd(encoded, offset); } - function asUint24Unchecked(bytes calldata encoded, uint offset) external pure returns (uint24 ret, uint nextOffset) { + function asUint24Unchecked(bytes calldata encoded, uint offset) external pure returns (uint24 ret, uint nextOffset) { return BytesParsing.asUint24Unchecked(encoded, offset); } - function asUint24(bytes calldata encoded, uint offset) external pure returns (uint24 ret, uint nextOffset) { + function asUint24(bytes calldata encoded, uint offset) external pure returns (uint24 ret, uint nextOffset) { return BytesParsing.asUint24(encoded, offset); } - function asUint32CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint32 ret, uint nextOffset) { + function asUint32CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint32 ret, uint nextOffset) { return BytesParsing.asUint32CdUnchecked(encoded, offset); } - function asUint32Cd(bytes calldata encoded, uint offset) external pure returns (uint32 ret, uint nextOffset) { + function asUint32Cd(bytes calldata encoded, uint offset) external pure returns (uint32 ret, uint nextOffset) { return BytesParsing.asUint32Cd(encoded, offset); } - function asUint32Unchecked(bytes calldata encoded, uint offset) external pure returns (uint32 ret, uint nextOffset) { + function asUint32Unchecked(bytes calldata encoded, uint offset) external pure returns (uint32 ret, uint nextOffset) { return BytesParsing.asUint32Unchecked(encoded, offset); } - function asUint32(bytes calldata encoded, uint offset) external pure returns (uint32 ret, uint nextOffset) { + function asUint32(bytes calldata encoded, uint offset) external pure returns (uint32 ret, uint nextOffset) { return BytesParsing.asUint32(encoded, offset); } - function asUint40CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint40 ret, uint nextOffset) { + function asUint40CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint40 ret, uint nextOffset) { return BytesParsing.asUint40CdUnchecked(encoded, offset); } - function asUint40Cd(bytes calldata encoded, uint offset) external pure returns (uint40 ret, uint nextOffset) { + function asUint40Cd(bytes calldata encoded, uint offset) external pure returns (uint40 ret, uint nextOffset) { return BytesParsing.asUint40Cd(encoded, offset); } - function asUint40Unchecked(bytes calldata encoded, uint offset) external pure returns (uint40 ret, uint nextOffset) { + function asUint40Unchecked(bytes calldata encoded, uint offset) external pure returns (uint40 ret, uint nextOffset) { return BytesParsing.asUint40Unchecked(encoded, offset); } - function asUint40(bytes calldata encoded, uint offset) external pure returns (uint40 ret, uint nextOffset) { + function asUint40(bytes calldata encoded, uint offset) external pure returns (uint40 ret, uint nextOffset) { return BytesParsing.asUint40(encoded, offset); } - function asUint48CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint48 ret, uint nextOffset) { + function asUint48CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint48 ret, uint nextOffset) { return BytesParsing.asUint48CdUnchecked(encoded, offset); } - function asUint48Cd(bytes calldata encoded, uint offset) external pure returns (uint48 ret, uint nextOffset) { + function asUint48Cd(bytes calldata encoded, uint offset) external pure returns (uint48 ret, uint nextOffset) { return BytesParsing.asUint48Cd(encoded, offset); } - function asUint48Unchecked(bytes calldata encoded, uint offset) external pure returns (uint48 ret, uint nextOffset) { + function asUint48Unchecked(bytes calldata encoded, uint offset) external pure returns (uint48 ret, uint nextOffset) { return BytesParsing.asUint48Unchecked(encoded, offset); } - function asUint48(bytes calldata encoded, uint offset) external pure returns (uint48 ret, uint nextOffset) { + function asUint48(bytes calldata encoded, uint offset) external pure returns (uint48 ret, uint nextOffset) { return BytesParsing.asUint48(encoded, offset); } - function asUint56CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint56 ret, uint nextOffset) { + function asUint56CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint56 ret, uint nextOffset) { return BytesParsing.asUint56CdUnchecked(encoded, offset); } - function asUint56Cd(bytes calldata encoded, uint offset) external pure returns (uint56 ret, uint nextOffset) { + function asUint56Cd(bytes calldata encoded, uint offset) external pure returns (uint56 ret, uint nextOffset) { return BytesParsing.asUint56Cd(encoded, offset); } - function asUint56Unchecked(bytes calldata encoded, uint offset) external pure returns (uint56 ret, uint nextOffset) { + function asUint56Unchecked(bytes calldata encoded, uint offset) external pure returns (uint56 ret, uint nextOffset) { return BytesParsing.asUint56Unchecked(encoded, offset); } - function asUint56(bytes calldata encoded, uint offset) external pure returns (uint56 ret, uint nextOffset) { + function asUint56(bytes calldata encoded, uint offset) external pure returns (uint56 ret, uint nextOffset) { return BytesParsing.asUint56(encoded, offset); } - function asUint64CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint64 ret, uint nextOffset) { + function asUint64CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint64 ret, uint nextOffset) { return BytesParsing.asUint64CdUnchecked(encoded, offset); } - function asUint64Cd(bytes calldata encoded, uint offset) external pure returns (uint64 ret, uint nextOffset) { + function asUint64Cd(bytes calldata encoded, uint offset) external pure returns (uint64 ret, uint nextOffset) { return BytesParsing.asUint64Cd(encoded, offset); } - function asUint64Unchecked(bytes calldata encoded, uint offset) external pure returns (uint64 ret, uint nextOffset) { + function asUint64Unchecked(bytes calldata encoded, uint offset) external pure returns (uint64 ret, uint nextOffset) { return BytesParsing.asUint64Unchecked(encoded, offset); } - function asUint64(bytes calldata encoded, uint offset) external pure returns (uint64 ret, uint nextOffset) { + function asUint64(bytes calldata encoded, uint offset) external pure returns (uint64 ret, uint nextOffset) { return BytesParsing.asUint64(encoded, offset); } - function asUint72CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint72 ret, uint nextOffset) { + function asUint72CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint72 ret, uint nextOffset) { return BytesParsing.asUint72CdUnchecked(encoded, offset); } - function asUint72Cd(bytes calldata encoded, uint offset) external pure returns (uint72 ret, uint nextOffset) { + function asUint72Cd(bytes calldata encoded, uint offset) external pure returns (uint72 ret, uint nextOffset) { return BytesParsing.asUint72Cd(encoded, offset); } - function asUint72Unchecked(bytes calldata encoded, uint offset) external pure returns (uint72 ret, uint nextOffset) { + function asUint72Unchecked(bytes calldata encoded, uint offset) external pure returns (uint72 ret, uint nextOffset) { return BytesParsing.asUint72Unchecked(encoded, offset); } - function asUint72(bytes calldata encoded, uint offset) external pure returns (uint72 ret, uint nextOffset) { + function asUint72(bytes calldata encoded, uint offset) external pure returns (uint72 ret, uint nextOffset) { return BytesParsing.asUint72(encoded, offset); } - function asUint80CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint80 ret, uint nextOffset) { + function asUint80CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint80 ret, uint nextOffset) { return BytesParsing.asUint80CdUnchecked(encoded, offset); } - function asUint80Cd(bytes calldata encoded, uint offset) external pure returns (uint80 ret, uint nextOffset) { + function asUint80Cd(bytes calldata encoded, uint offset) external pure returns (uint80 ret, uint nextOffset) { return BytesParsing.asUint80Cd(encoded, offset); } - function asUint80Unchecked(bytes calldata encoded, uint offset) external pure returns (uint80 ret, uint nextOffset) { + function asUint80Unchecked(bytes calldata encoded, uint offset) external pure returns (uint80 ret, uint nextOffset) { return BytesParsing.asUint80Unchecked(encoded, offset); } - function asUint80(bytes calldata encoded, uint offset) external pure returns (uint80 ret, uint nextOffset) { + function asUint80(bytes calldata encoded, uint offset) external pure returns (uint80 ret, uint nextOffset) { return BytesParsing.asUint80(encoded, offset); } - function asUint88CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint88 ret, uint nextOffset) { + function asUint88CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint88 ret, uint nextOffset) { return BytesParsing.asUint88CdUnchecked(encoded, offset); } - function asUint88Cd(bytes calldata encoded, uint offset) external pure returns (uint88 ret, uint nextOffset) { + function asUint88Cd(bytes calldata encoded, uint offset) external pure returns (uint88 ret, uint nextOffset) { return BytesParsing.asUint88Cd(encoded, offset); } - function asUint88Unchecked(bytes calldata encoded, uint offset) external pure returns (uint88 ret, uint nextOffset) { + function asUint88Unchecked(bytes calldata encoded, uint offset) external pure returns (uint88 ret, uint nextOffset) { return BytesParsing.asUint88Unchecked(encoded, offset); } - function asUint88(bytes calldata encoded, uint offset) external pure returns (uint88 ret, uint nextOffset) { + function asUint88(bytes calldata encoded, uint offset) external pure returns (uint88 ret, uint nextOffset) { return BytesParsing.asUint88(encoded, offset); } - function asUint96CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint96 ret, uint nextOffset) { + function asUint96CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint96 ret, uint nextOffset) { return BytesParsing.asUint96CdUnchecked(encoded, offset); } - function asUint96Cd(bytes calldata encoded, uint offset) external pure returns (uint96 ret, uint nextOffset) { + function asUint96Cd(bytes calldata encoded, uint offset) external pure returns (uint96 ret, uint nextOffset) { return BytesParsing.asUint96Cd(encoded, offset); } - function asUint96Unchecked(bytes calldata encoded, uint offset) external pure returns (uint96 ret, uint nextOffset) { + function asUint96Unchecked(bytes calldata encoded, uint offset) external pure returns (uint96 ret, uint nextOffset) { return BytesParsing.asUint96Unchecked(encoded, offset); } - function asUint96(bytes calldata encoded, uint offset) external pure returns (uint96 ret, uint nextOffset) { + function asUint96(bytes calldata encoded, uint offset) external pure returns (uint96 ret, uint nextOffset) { return BytesParsing.asUint96(encoded, offset); } - function asUint104CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint104 ret, uint nextOffset) { + function asUint104CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint104 ret, uint nextOffset) { return BytesParsing.asUint104CdUnchecked(encoded, offset); } - function asUint104Cd(bytes calldata encoded, uint offset) external pure returns (uint104 ret, uint nextOffset) { + function asUint104Cd(bytes calldata encoded, uint offset) external pure returns (uint104 ret, uint nextOffset) { return BytesParsing.asUint104Cd(encoded, offset); } - function asUint104Unchecked(bytes calldata encoded, uint offset) external pure returns (uint104 ret, uint nextOffset) { + function asUint104Unchecked(bytes calldata encoded, uint offset) external pure returns (uint104 ret, uint nextOffset) { return BytesParsing.asUint104Unchecked(encoded, offset); } - function asUint104(bytes calldata encoded, uint offset) external pure returns (uint104 ret, uint nextOffset) { + function asUint104(bytes calldata encoded, uint offset) external pure returns (uint104 ret, uint nextOffset) { return BytesParsing.asUint104(encoded, offset); } - function asUint112CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint112 ret, uint nextOffset) { + function asUint112CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint112 ret, uint nextOffset) { return BytesParsing.asUint112CdUnchecked(encoded, offset); } - function asUint112Cd(bytes calldata encoded, uint offset) external pure returns (uint112 ret, uint nextOffset) { + function asUint112Cd(bytes calldata encoded, uint offset) external pure returns (uint112 ret, uint nextOffset) { return BytesParsing.asUint112Cd(encoded, offset); } - function asUint112Unchecked(bytes calldata encoded, uint offset) external pure returns (uint112 ret, uint nextOffset) { + function asUint112Unchecked(bytes calldata encoded, uint offset) external pure returns (uint112 ret, uint nextOffset) { return BytesParsing.asUint112Unchecked(encoded, offset); } - function asUint112(bytes calldata encoded, uint offset) external pure returns (uint112 ret, uint nextOffset) { + function asUint112(bytes calldata encoded, uint offset) external pure returns (uint112 ret, uint nextOffset) { return BytesParsing.asUint112(encoded, offset); } - function asUint120CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint120 ret, uint nextOffset) { + function asUint120CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint120 ret, uint nextOffset) { return BytesParsing.asUint120CdUnchecked(encoded, offset); } - function asUint120Cd(bytes calldata encoded, uint offset) external pure returns (uint120 ret, uint nextOffset) { + function asUint120Cd(bytes calldata encoded, uint offset) external pure returns (uint120 ret, uint nextOffset) { return BytesParsing.asUint120Cd(encoded, offset); } - function asUint120Unchecked(bytes calldata encoded, uint offset) external pure returns (uint120 ret, uint nextOffset) { + function asUint120Unchecked(bytes calldata encoded, uint offset) external pure returns (uint120 ret, uint nextOffset) { return BytesParsing.asUint120Unchecked(encoded, offset); } - function asUint120(bytes calldata encoded, uint offset) external pure returns (uint120 ret, uint nextOffset) { + function asUint120(bytes calldata encoded, uint offset) external pure returns (uint120 ret, uint nextOffset) { return BytesParsing.asUint120(encoded, offset); } - function asUint128CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint128 ret, uint nextOffset) { + function asUint128CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint128 ret, uint nextOffset) { return BytesParsing.asUint128CdUnchecked(encoded, offset); } - function asUint128Cd(bytes calldata encoded, uint offset) external pure returns (uint128 ret, uint nextOffset) { + function asUint128Cd(bytes calldata encoded, uint offset) external pure returns (uint128 ret, uint nextOffset) { return BytesParsing.asUint128Cd(encoded, offset); } - function asUint128Unchecked(bytes calldata encoded, uint offset) external pure returns (uint128 ret, uint nextOffset) { + function asUint128Unchecked(bytes calldata encoded, uint offset) external pure returns (uint128 ret, uint nextOffset) { return BytesParsing.asUint128Unchecked(encoded, offset); } - function asUint128(bytes calldata encoded, uint offset) external pure returns (uint128 ret, uint nextOffset) { + function asUint128(bytes calldata encoded, uint offset) external pure returns (uint128 ret, uint nextOffset) { return BytesParsing.asUint128(encoded, offset); } - function asUint136CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint136 ret, uint nextOffset) { + function asUint136CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint136 ret, uint nextOffset) { return BytesParsing.asUint136CdUnchecked(encoded, offset); } - function asUint136Cd(bytes calldata encoded, uint offset) external pure returns (uint136 ret, uint nextOffset) { + function asUint136Cd(bytes calldata encoded, uint offset) external pure returns (uint136 ret, uint nextOffset) { return BytesParsing.asUint136Cd(encoded, offset); } - function asUint136Unchecked(bytes calldata encoded, uint offset) external pure returns (uint136 ret, uint nextOffset) { + function asUint136Unchecked(bytes calldata encoded, uint offset) external pure returns (uint136 ret, uint nextOffset) { return BytesParsing.asUint136Unchecked(encoded, offset); } - function asUint136(bytes calldata encoded, uint offset) external pure returns (uint136 ret, uint nextOffset) { + function asUint136(bytes calldata encoded, uint offset) external pure returns (uint136 ret, uint nextOffset) { return BytesParsing.asUint136(encoded, offset); } - function asUint144CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint144 ret, uint nextOffset) { + function asUint144CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint144 ret, uint nextOffset) { return BytesParsing.asUint144CdUnchecked(encoded, offset); } - function asUint144Cd(bytes calldata encoded, uint offset) external pure returns (uint144 ret, uint nextOffset) { + function asUint144Cd(bytes calldata encoded, uint offset) external pure returns (uint144 ret, uint nextOffset) { return BytesParsing.asUint144Cd(encoded, offset); } - function asUint144Unchecked(bytes calldata encoded, uint offset) external pure returns (uint144 ret, uint nextOffset) { + function asUint144Unchecked(bytes calldata encoded, uint offset) external pure returns (uint144 ret, uint nextOffset) { return BytesParsing.asUint144Unchecked(encoded, offset); } - function asUint144(bytes calldata encoded, uint offset) external pure returns (uint144 ret, uint nextOffset) { + function asUint144(bytes calldata encoded, uint offset) external pure returns (uint144 ret, uint nextOffset) { return BytesParsing.asUint144(encoded, offset); } - function asUint152CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint152 ret, uint nextOffset) { + function asUint152CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint152 ret, uint nextOffset) { return BytesParsing.asUint152CdUnchecked(encoded, offset); } - function asUint152Cd(bytes calldata encoded, uint offset) external pure returns (uint152 ret, uint nextOffset) { + function asUint152Cd(bytes calldata encoded, uint offset) external pure returns (uint152 ret, uint nextOffset) { return BytesParsing.asUint152Cd(encoded, offset); } - function asUint152Unchecked(bytes calldata encoded, uint offset) external pure returns (uint152 ret, uint nextOffset) { + function asUint152Unchecked(bytes calldata encoded, uint offset) external pure returns (uint152 ret, uint nextOffset) { return BytesParsing.asUint152Unchecked(encoded, offset); } - function asUint152(bytes calldata encoded, uint offset) external pure returns (uint152 ret, uint nextOffset) { + function asUint152(bytes calldata encoded, uint offset) external pure returns (uint152 ret, uint nextOffset) { return BytesParsing.asUint152(encoded, offset); } - function asUint160CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint160 ret, uint nextOffset) { + function asUint160CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint160 ret, uint nextOffset) { return BytesParsing.asUint160CdUnchecked(encoded, offset); } - function asUint160Cd(bytes calldata encoded, uint offset) external pure returns (uint160 ret, uint nextOffset) { + function asUint160Cd(bytes calldata encoded, uint offset) external pure returns (uint160 ret, uint nextOffset) { return BytesParsing.asUint160Cd(encoded, offset); } - function asUint160Unchecked(bytes calldata encoded, uint offset) external pure returns (uint160 ret, uint nextOffset) { + function asUint160Unchecked(bytes calldata encoded, uint offset) external pure returns (uint160 ret, uint nextOffset) { return BytesParsing.asUint160Unchecked(encoded, offset); } - function asUint160(bytes calldata encoded, uint offset) external pure returns (uint160 ret, uint nextOffset) { + function asUint160(bytes calldata encoded, uint offset) external pure returns (uint160 ret, uint nextOffset) { return BytesParsing.asUint160(encoded, offset); } - function asUint168CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint168 ret, uint nextOffset) { + function asUint168CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint168 ret, uint nextOffset) { return BytesParsing.asUint168CdUnchecked(encoded, offset); } - function asUint168Cd(bytes calldata encoded, uint offset) external pure returns (uint168 ret, uint nextOffset) { + function asUint168Cd(bytes calldata encoded, uint offset) external pure returns (uint168 ret, uint nextOffset) { return BytesParsing.asUint168Cd(encoded, offset); } - function asUint168Unchecked(bytes calldata encoded, uint offset) external pure returns (uint168 ret, uint nextOffset) { + function asUint168Unchecked(bytes calldata encoded, uint offset) external pure returns (uint168 ret, uint nextOffset) { return BytesParsing.asUint168Unchecked(encoded, offset); } - function asUint168(bytes calldata encoded, uint offset) external pure returns (uint168 ret, uint nextOffset) { + function asUint168(bytes calldata encoded, uint offset) external pure returns (uint168 ret, uint nextOffset) { return BytesParsing.asUint168(encoded, offset); } - function asUint176CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint176 ret, uint nextOffset) { + function asUint176CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint176 ret, uint nextOffset) { return BytesParsing.asUint176CdUnchecked(encoded, offset); } - function asUint176Cd(bytes calldata encoded, uint offset) external pure returns (uint176 ret, uint nextOffset) { + function asUint176Cd(bytes calldata encoded, uint offset) external pure returns (uint176 ret, uint nextOffset) { return BytesParsing.asUint176Cd(encoded, offset); } - function asUint176Unchecked(bytes calldata encoded, uint offset) external pure returns (uint176 ret, uint nextOffset) { + function asUint176Unchecked(bytes calldata encoded, uint offset) external pure returns (uint176 ret, uint nextOffset) { return BytesParsing.asUint176Unchecked(encoded, offset); } - function asUint176(bytes calldata encoded, uint offset) external pure returns (uint176 ret, uint nextOffset) { + function asUint176(bytes calldata encoded, uint offset) external pure returns (uint176 ret, uint nextOffset) { return BytesParsing.asUint176(encoded, offset); } - function asUint184CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint184 ret, uint nextOffset) { + function asUint184CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint184 ret, uint nextOffset) { return BytesParsing.asUint184CdUnchecked(encoded, offset); } - function asUint184Cd(bytes calldata encoded, uint offset) external pure returns (uint184 ret, uint nextOffset) { + function asUint184Cd(bytes calldata encoded, uint offset) external pure returns (uint184 ret, uint nextOffset) { return BytesParsing.asUint184Cd(encoded, offset); } - function asUint184Unchecked(bytes calldata encoded, uint offset) external pure returns (uint184 ret, uint nextOffset) { + function asUint184Unchecked(bytes calldata encoded, uint offset) external pure returns (uint184 ret, uint nextOffset) { return BytesParsing.asUint184Unchecked(encoded, offset); } - function asUint184(bytes calldata encoded, uint offset) external pure returns (uint184 ret, uint nextOffset) { + function asUint184(bytes calldata encoded, uint offset) external pure returns (uint184 ret, uint nextOffset) { return BytesParsing.asUint184(encoded, offset); } - function asUint192CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint192 ret, uint nextOffset) { + function asUint192CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint192 ret, uint nextOffset) { return BytesParsing.asUint192CdUnchecked(encoded, offset); } - function asUint192Cd(bytes calldata encoded, uint offset) external pure returns (uint192 ret, uint nextOffset) { + function asUint192Cd(bytes calldata encoded, uint offset) external pure returns (uint192 ret, uint nextOffset) { return BytesParsing.asUint192Cd(encoded, offset); } - function asUint192Unchecked(bytes calldata encoded, uint offset) external pure returns (uint192 ret, uint nextOffset) { + function asUint192Unchecked(bytes calldata encoded, uint offset) external pure returns (uint192 ret, uint nextOffset) { return BytesParsing.asUint192Unchecked(encoded, offset); } - function asUint192(bytes calldata encoded, uint offset) external pure returns (uint192 ret, uint nextOffset) { + function asUint192(bytes calldata encoded, uint offset) external pure returns (uint192 ret, uint nextOffset) { return BytesParsing.asUint192(encoded, offset); } - function asUint200CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint200 ret, uint nextOffset) { + function asUint200CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint200 ret, uint nextOffset) { return BytesParsing.asUint200CdUnchecked(encoded, offset); } - function asUint200Cd(bytes calldata encoded, uint offset) external pure returns (uint200 ret, uint nextOffset) { + function asUint200Cd(bytes calldata encoded, uint offset) external pure returns (uint200 ret, uint nextOffset) { return BytesParsing.asUint200Cd(encoded, offset); } - function asUint200Unchecked(bytes calldata encoded, uint offset) external pure returns (uint200 ret, uint nextOffset) { + function asUint200Unchecked(bytes calldata encoded, uint offset) external pure returns (uint200 ret, uint nextOffset) { return BytesParsing.asUint200Unchecked(encoded, offset); } - function asUint200(bytes calldata encoded, uint offset) external pure returns (uint200 ret, uint nextOffset) { + function asUint200(bytes calldata encoded, uint offset) external pure returns (uint200 ret, uint nextOffset) { return BytesParsing.asUint200(encoded, offset); } - function asUint208CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint208 ret, uint nextOffset) { + function asUint208CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint208 ret, uint nextOffset) { return BytesParsing.asUint208CdUnchecked(encoded, offset); } - function asUint208Cd(bytes calldata encoded, uint offset) external pure returns (uint208 ret, uint nextOffset) { + function asUint208Cd(bytes calldata encoded, uint offset) external pure returns (uint208 ret, uint nextOffset) { return BytesParsing.asUint208Cd(encoded, offset); } - function asUint208Unchecked(bytes calldata encoded, uint offset) external pure returns (uint208 ret, uint nextOffset) { + function asUint208Unchecked(bytes calldata encoded, uint offset) external pure returns (uint208 ret, uint nextOffset) { return BytesParsing.asUint208Unchecked(encoded, offset); } - function asUint208(bytes calldata encoded, uint offset) external pure returns (uint208 ret, uint nextOffset) { + function asUint208(bytes calldata encoded, uint offset) external pure returns (uint208 ret, uint nextOffset) { return BytesParsing.asUint208(encoded, offset); } - function asUint216CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint216 ret, uint nextOffset) { + function asUint216CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint216 ret, uint nextOffset) { return BytesParsing.asUint216CdUnchecked(encoded, offset); } - function asUint216Cd(bytes calldata encoded, uint offset) external pure returns (uint216 ret, uint nextOffset) { + function asUint216Cd(bytes calldata encoded, uint offset) external pure returns (uint216 ret, uint nextOffset) { return BytesParsing.asUint216Cd(encoded, offset); } - function asUint216Unchecked(bytes calldata encoded, uint offset) external pure returns (uint216 ret, uint nextOffset) { + function asUint216Unchecked(bytes calldata encoded, uint offset) external pure returns (uint216 ret, uint nextOffset) { return BytesParsing.asUint216Unchecked(encoded, offset); } - function asUint216(bytes calldata encoded, uint offset) external pure returns (uint216 ret, uint nextOffset) { + function asUint216(bytes calldata encoded, uint offset) external pure returns (uint216 ret, uint nextOffset) { return BytesParsing.asUint216(encoded, offset); } - function asUint224CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint224 ret, uint nextOffset) { + function asUint224CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint224 ret, uint nextOffset) { return BytesParsing.asUint224CdUnchecked(encoded, offset); } - function asUint224Cd(bytes calldata encoded, uint offset) external pure returns (uint224 ret, uint nextOffset) { + function asUint224Cd(bytes calldata encoded, uint offset) external pure returns (uint224 ret, uint nextOffset) { return BytesParsing.asUint224Cd(encoded, offset); } - function asUint224Unchecked(bytes calldata encoded, uint offset) external pure returns (uint224 ret, uint nextOffset) { + function asUint224Unchecked(bytes calldata encoded, uint offset) external pure returns (uint224 ret, uint nextOffset) { return BytesParsing.asUint224Unchecked(encoded, offset); } - function asUint224(bytes calldata encoded, uint offset) external pure returns (uint224 ret, uint nextOffset) { + function asUint224(bytes calldata encoded, uint offset) external pure returns (uint224 ret, uint nextOffset) { return BytesParsing.asUint224(encoded, offset); } - function asUint232CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint232 ret, uint nextOffset) { + function asUint232CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint232 ret, uint nextOffset) { return BytesParsing.asUint232CdUnchecked(encoded, offset); } - function asUint232Cd(bytes calldata encoded, uint offset) external pure returns (uint232 ret, uint nextOffset) { + function asUint232Cd(bytes calldata encoded, uint offset) external pure returns (uint232 ret, uint nextOffset) { return BytesParsing.asUint232Cd(encoded, offset); } - function asUint232Unchecked(bytes calldata encoded, uint offset) external pure returns (uint232 ret, uint nextOffset) { + function asUint232Unchecked(bytes calldata encoded, uint offset) external pure returns (uint232 ret, uint nextOffset) { return BytesParsing.asUint232Unchecked(encoded, offset); } - function asUint232(bytes calldata encoded, uint offset) external pure returns (uint232 ret, uint nextOffset) { + function asUint232(bytes calldata encoded, uint offset) external pure returns (uint232 ret, uint nextOffset) { return BytesParsing.asUint232(encoded, offset); } - function asUint240CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint240 ret, uint nextOffset) { + function asUint240CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint240 ret, uint nextOffset) { return BytesParsing.asUint240CdUnchecked(encoded, offset); } - function asUint240Cd(bytes calldata encoded, uint offset) external pure returns (uint240 ret, uint nextOffset) { + function asUint240Cd(bytes calldata encoded, uint offset) external pure returns (uint240 ret, uint nextOffset) { return BytesParsing.asUint240Cd(encoded, offset); } - function asUint240Unchecked(bytes calldata encoded, uint offset) external pure returns (uint240 ret, uint nextOffset) { + function asUint240Unchecked(bytes calldata encoded, uint offset) external pure returns (uint240 ret, uint nextOffset) { return BytesParsing.asUint240Unchecked(encoded, offset); } - function asUint240(bytes calldata encoded, uint offset) external pure returns (uint240 ret, uint nextOffset) { + function asUint240(bytes calldata encoded, uint offset) external pure returns (uint240 ret, uint nextOffset) { return BytesParsing.asUint240(encoded, offset); } - function asUint248CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint248 ret, uint nextOffset) { + function asUint248CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint248 ret, uint nextOffset) { return BytesParsing.asUint248CdUnchecked(encoded, offset); } - function asUint248Cd(bytes calldata encoded, uint offset) external pure returns (uint248 ret, uint nextOffset) { + function asUint248Cd(bytes calldata encoded, uint offset) external pure returns (uint248 ret, uint nextOffset) { return BytesParsing.asUint248Cd(encoded, offset); } - function asUint248Unchecked(bytes calldata encoded, uint offset) external pure returns (uint248 ret, uint nextOffset) { + function asUint248Unchecked(bytes calldata encoded, uint offset) external pure returns (uint248 ret, uint nextOffset) { return BytesParsing.asUint248Unchecked(encoded, offset); } - function asUint248(bytes calldata encoded, uint offset) external pure returns (uint248 ret, uint nextOffset) { + function asUint248(bytes calldata encoded, uint offset) external pure returns (uint248 ret, uint nextOffset) { return BytesParsing.asUint248(encoded, offset); } - function asUint256CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint256 ret, uint nextOffset) { + function asUint256CdUnchecked(bytes calldata encoded, uint offset) external pure returns (uint256 ret, uint nextOffset) { return BytesParsing.asUint256CdUnchecked(encoded, offset); } - function asUint256Cd(bytes calldata encoded, uint offset) external pure returns (uint256 ret, uint nextOffset) { + function asUint256Cd(bytes calldata encoded, uint offset) external pure returns (uint256 ret, uint nextOffset) { return BytesParsing.asUint256Cd(encoded, offset); } - function asUint256Unchecked(bytes calldata encoded, uint offset) external pure returns (uint256 ret, uint nextOffset) { + function asUint256Unchecked(bytes calldata encoded, uint offset) external pure returns (uint256 ret, uint nextOffset) { return BytesParsing.asUint256Unchecked(encoded, offset); } - function asUint256(bytes calldata encoded, uint offset) external pure returns (uint256 ret, uint nextOffset) { + function asUint256(bytes calldata encoded, uint offset) external pure returns (uint256 ret, uint nextOffset) { return BytesParsing.asUint256(encoded, offset); } - function asBytes1CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes1 ret, uint nextOffset) { + function asBytes1CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes1 ret, uint nextOffset) { return BytesParsing.asBytes1CdUnchecked(encoded, offset); } - function asBytes1Cd(bytes calldata encoded, uint offset) external pure returns (bytes1 ret, uint nextOffset) { + function asBytes1Cd(bytes calldata encoded, uint offset) external pure returns (bytes1 ret, uint nextOffset) { return BytesParsing.asBytes1Cd(encoded, offset); } - function asBytes1Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes1 ret, uint nextOffset) { + function asBytes1Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes1 ret, uint nextOffset) { return BytesParsing.asBytes1Unchecked(encoded, offset); } - function asBytes1(bytes calldata encoded, uint offset) external pure returns (bytes1 ret, uint nextOffset) { + function asBytes1(bytes calldata encoded, uint offset) external pure returns (bytes1 ret, uint nextOffset) { return BytesParsing.asBytes1(encoded, offset); } - function asBytes2CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes2 ret, uint nextOffset) { + function asBytes2CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes2 ret, uint nextOffset) { return BytesParsing.asBytes2CdUnchecked(encoded, offset); } - function asBytes2Cd(bytes calldata encoded, uint offset) external pure returns (bytes2 ret, uint nextOffset) { + function asBytes2Cd(bytes calldata encoded, uint offset) external pure returns (bytes2 ret, uint nextOffset) { return BytesParsing.asBytes2Cd(encoded, offset); } - function asBytes2Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes2 ret, uint nextOffset) { + function asBytes2Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes2 ret, uint nextOffset) { return BytesParsing.asBytes2Unchecked(encoded, offset); } - function asBytes2(bytes calldata encoded, uint offset) external pure returns (bytes2 ret, uint nextOffset) { + function asBytes2(bytes calldata encoded, uint offset) external pure returns (bytes2 ret, uint nextOffset) { return BytesParsing.asBytes2(encoded, offset); } - function asBytes3CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes3 ret, uint nextOffset) { + function asBytes3CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes3 ret, uint nextOffset) { return BytesParsing.asBytes3CdUnchecked(encoded, offset); } - function asBytes3Cd(bytes calldata encoded, uint offset) external pure returns (bytes3 ret, uint nextOffset) { + function asBytes3Cd(bytes calldata encoded, uint offset) external pure returns (bytes3 ret, uint nextOffset) { return BytesParsing.asBytes3Cd(encoded, offset); } - function asBytes3Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes3 ret, uint nextOffset) { + function asBytes3Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes3 ret, uint nextOffset) { return BytesParsing.asBytes3Unchecked(encoded, offset); } - function asBytes3(bytes calldata encoded, uint offset) external pure returns (bytes3 ret, uint nextOffset) { + function asBytes3(bytes calldata encoded, uint offset) external pure returns (bytes3 ret, uint nextOffset) { return BytesParsing.asBytes3(encoded, offset); } - function asBytes4CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes4 ret, uint nextOffset) { + function asBytes4CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes4 ret, uint nextOffset) { return BytesParsing.asBytes4CdUnchecked(encoded, offset); } - function asBytes4Cd(bytes calldata encoded, uint offset) external pure returns (bytes4 ret, uint nextOffset) { + function asBytes4Cd(bytes calldata encoded, uint offset) external pure returns (bytes4 ret, uint nextOffset) { return BytesParsing.asBytes4Cd(encoded, offset); } - function asBytes4Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes4 ret, uint nextOffset) { + function asBytes4Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes4 ret, uint nextOffset) { return BytesParsing.asBytes4Unchecked(encoded, offset); } - function asBytes4(bytes calldata encoded, uint offset) external pure returns (bytes4 ret, uint nextOffset) { + function asBytes4(bytes calldata encoded, uint offset) external pure returns (bytes4 ret, uint nextOffset) { return BytesParsing.asBytes4(encoded, offset); } - function asBytes5CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes5 ret, uint nextOffset) { + function asBytes5CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes5 ret, uint nextOffset) { return BytesParsing.asBytes5CdUnchecked(encoded, offset); } - function asBytes5Cd(bytes calldata encoded, uint offset) external pure returns (bytes5 ret, uint nextOffset) { + function asBytes5Cd(bytes calldata encoded, uint offset) external pure returns (bytes5 ret, uint nextOffset) { return BytesParsing.asBytes5Cd(encoded, offset); } - function asBytes5Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes5 ret, uint nextOffset) { + function asBytes5Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes5 ret, uint nextOffset) { return BytesParsing.asBytes5Unchecked(encoded, offset); } - function asBytes5(bytes calldata encoded, uint offset) external pure returns (bytes5 ret, uint nextOffset) { + function asBytes5(bytes calldata encoded, uint offset) external pure returns (bytes5 ret, uint nextOffset) { return BytesParsing.asBytes5(encoded, offset); } - function asBytes6CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes6 ret, uint nextOffset) { + function asBytes6CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes6 ret, uint nextOffset) { return BytesParsing.asBytes6CdUnchecked(encoded, offset); } - function asBytes6Cd(bytes calldata encoded, uint offset) external pure returns (bytes6 ret, uint nextOffset) { + function asBytes6Cd(bytes calldata encoded, uint offset) external pure returns (bytes6 ret, uint nextOffset) { return BytesParsing.asBytes6Cd(encoded, offset); } - function asBytes6Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes6 ret, uint nextOffset) { + function asBytes6Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes6 ret, uint nextOffset) { return BytesParsing.asBytes6Unchecked(encoded, offset); } - function asBytes6(bytes calldata encoded, uint offset) external pure returns (bytes6 ret, uint nextOffset) { + function asBytes6(bytes calldata encoded, uint offset) external pure returns (bytes6 ret, uint nextOffset) { return BytesParsing.asBytes6(encoded, offset); } - function asBytes7CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes7 ret, uint nextOffset) { + function asBytes7CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes7 ret, uint nextOffset) { return BytesParsing.asBytes7CdUnchecked(encoded, offset); } - function asBytes7Cd(bytes calldata encoded, uint offset) external pure returns (bytes7 ret, uint nextOffset) { + function asBytes7Cd(bytes calldata encoded, uint offset) external pure returns (bytes7 ret, uint nextOffset) { return BytesParsing.asBytes7Cd(encoded, offset); } - function asBytes7Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes7 ret, uint nextOffset) { + function asBytes7Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes7 ret, uint nextOffset) { return BytesParsing.asBytes7Unchecked(encoded, offset); } - function asBytes7(bytes calldata encoded, uint offset) external pure returns (bytes7 ret, uint nextOffset) { + function asBytes7(bytes calldata encoded, uint offset) external pure returns (bytes7 ret, uint nextOffset) { return BytesParsing.asBytes7(encoded, offset); } - function asBytes8CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes8 ret, uint nextOffset) { + function asBytes8CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes8 ret, uint nextOffset) { return BytesParsing.asBytes8CdUnchecked(encoded, offset); } - function asBytes8Cd(bytes calldata encoded, uint offset) external pure returns (bytes8 ret, uint nextOffset) { + function asBytes8Cd(bytes calldata encoded, uint offset) external pure returns (bytes8 ret, uint nextOffset) { return BytesParsing.asBytes8Cd(encoded, offset); } - function asBytes8Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes8 ret, uint nextOffset) { + function asBytes8Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes8 ret, uint nextOffset) { return BytesParsing.asBytes8Unchecked(encoded, offset); } - function asBytes8(bytes calldata encoded, uint offset) external pure returns (bytes8 ret, uint nextOffset) { + function asBytes8(bytes calldata encoded, uint offset) external pure returns (bytes8 ret, uint nextOffset) { return BytesParsing.asBytes8(encoded, offset); } - function asBytes9CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes9 ret, uint nextOffset) { + function asBytes9CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes9 ret, uint nextOffset) { return BytesParsing.asBytes9CdUnchecked(encoded, offset); } - function asBytes9Cd(bytes calldata encoded, uint offset) external pure returns (bytes9 ret, uint nextOffset) { + function asBytes9Cd(bytes calldata encoded, uint offset) external pure returns (bytes9 ret, uint nextOffset) { return BytesParsing.asBytes9Cd(encoded, offset); } - function asBytes9Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes9 ret, uint nextOffset) { + function asBytes9Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes9 ret, uint nextOffset) { return BytesParsing.asBytes9Unchecked(encoded, offset); } - function asBytes9(bytes calldata encoded, uint offset) external pure returns (bytes9 ret, uint nextOffset) { + function asBytes9(bytes calldata encoded, uint offset) external pure returns (bytes9 ret, uint nextOffset) { return BytesParsing.asBytes9(encoded, offset); } - function asBytes10CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes10 ret, uint nextOffset) { + function asBytes10CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes10 ret, uint nextOffset) { return BytesParsing.asBytes10CdUnchecked(encoded, offset); } - function asBytes10Cd(bytes calldata encoded, uint offset) external pure returns (bytes10 ret, uint nextOffset) { + function asBytes10Cd(bytes calldata encoded, uint offset) external pure returns (bytes10 ret, uint nextOffset) { return BytesParsing.asBytes10Cd(encoded, offset); } - function asBytes10Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes10 ret, uint nextOffset) { + function asBytes10Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes10 ret, uint nextOffset) { return BytesParsing.asBytes10Unchecked(encoded, offset); } - function asBytes10(bytes calldata encoded, uint offset) external pure returns (bytes10 ret, uint nextOffset) { + function asBytes10(bytes calldata encoded, uint offset) external pure returns (bytes10 ret, uint nextOffset) { return BytesParsing.asBytes10(encoded, offset); } - function asBytes11CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes11 ret, uint nextOffset) { + function asBytes11CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes11 ret, uint nextOffset) { return BytesParsing.asBytes11CdUnchecked(encoded, offset); } - function asBytes11Cd(bytes calldata encoded, uint offset) external pure returns (bytes11 ret, uint nextOffset) { + function asBytes11Cd(bytes calldata encoded, uint offset) external pure returns (bytes11 ret, uint nextOffset) { return BytesParsing.asBytes11Cd(encoded, offset); } - function asBytes11Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes11 ret, uint nextOffset) { + function asBytes11Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes11 ret, uint nextOffset) { return BytesParsing.asBytes11Unchecked(encoded, offset); } - function asBytes11(bytes calldata encoded, uint offset) external pure returns (bytes11 ret, uint nextOffset) { + function asBytes11(bytes calldata encoded, uint offset) external pure returns (bytes11 ret, uint nextOffset) { return BytesParsing.asBytes11(encoded, offset); } - function asBytes12CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes12 ret, uint nextOffset) { + function asBytes12CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes12 ret, uint nextOffset) { return BytesParsing.asBytes12CdUnchecked(encoded, offset); } - function asBytes12Cd(bytes calldata encoded, uint offset) external pure returns (bytes12 ret, uint nextOffset) { + function asBytes12Cd(bytes calldata encoded, uint offset) external pure returns (bytes12 ret, uint nextOffset) { return BytesParsing.asBytes12Cd(encoded, offset); } - function asBytes12Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes12 ret, uint nextOffset) { + function asBytes12Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes12 ret, uint nextOffset) { return BytesParsing.asBytes12Unchecked(encoded, offset); } - function asBytes12(bytes calldata encoded, uint offset) external pure returns (bytes12 ret, uint nextOffset) { + function asBytes12(bytes calldata encoded, uint offset) external pure returns (bytes12 ret, uint nextOffset) { return BytesParsing.asBytes12(encoded, offset); } - function asBytes13CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes13 ret, uint nextOffset) { + function asBytes13CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes13 ret, uint nextOffset) { return BytesParsing.asBytes13CdUnchecked(encoded, offset); } - function asBytes13Cd(bytes calldata encoded, uint offset) external pure returns (bytes13 ret, uint nextOffset) { + function asBytes13Cd(bytes calldata encoded, uint offset) external pure returns (bytes13 ret, uint nextOffset) { return BytesParsing.asBytes13Cd(encoded, offset); } - function asBytes13Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes13 ret, uint nextOffset) { + function asBytes13Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes13 ret, uint nextOffset) { return BytesParsing.asBytes13Unchecked(encoded, offset); } - function asBytes13(bytes calldata encoded, uint offset) external pure returns (bytes13 ret, uint nextOffset) { + function asBytes13(bytes calldata encoded, uint offset) external pure returns (bytes13 ret, uint nextOffset) { return BytesParsing.asBytes13(encoded, offset); } - function asBytes14CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes14 ret, uint nextOffset) { + function asBytes14CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes14 ret, uint nextOffset) { return BytesParsing.asBytes14CdUnchecked(encoded, offset); } - function asBytes14Cd(bytes calldata encoded, uint offset) external pure returns (bytes14 ret, uint nextOffset) { + function asBytes14Cd(bytes calldata encoded, uint offset) external pure returns (bytes14 ret, uint nextOffset) { return BytesParsing.asBytes14Cd(encoded, offset); } - function asBytes14Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes14 ret, uint nextOffset) { + function asBytes14Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes14 ret, uint nextOffset) { return BytesParsing.asBytes14Unchecked(encoded, offset); } - function asBytes14(bytes calldata encoded, uint offset) external pure returns (bytes14 ret, uint nextOffset) { + function asBytes14(bytes calldata encoded, uint offset) external pure returns (bytes14 ret, uint nextOffset) { return BytesParsing.asBytes14(encoded, offset); } - function asBytes15CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes15 ret, uint nextOffset) { + function asBytes15CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes15 ret, uint nextOffset) { return BytesParsing.asBytes15CdUnchecked(encoded, offset); } - function asBytes15Cd(bytes calldata encoded, uint offset) external pure returns (bytes15 ret, uint nextOffset) { + function asBytes15Cd(bytes calldata encoded, uint offset) external pure returns (bytes15 ret, uint nextOffset) { return BytesParsing.asBytes15Cd(encoded, offset); } - function asBytes15Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes15 ret, uint nextOffset) { + function asBytes15Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes15 ret, uint nextOffset) { return BytesParsing.asBytes15Unchecked(encoded, offset); } - function asBytes15(bytes calldata encoded, uint offset) external pure returns (bytes15 ret, uint nextOffset) { + function asBytes15(bytes calldata encoded, uint offset) external pure returns (bytes15 ret, uint nextOffset) { return BytesParsing.asBytes15(encoded, offset); } - function asBytes16CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes16 ret, uint nextOffset) { + function asBytes16CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes16 ret, uint nextOffset) { return BytesParsing.asBytes16CdUnchecked(encoded, offset); } - function asBytes16Cd(bytes calldata encoded, uint offset) external pure returns (bytes16 ret, uint nextOffset) { + function asBytes16Cd(bytes calldata encoded, uint offset) external pure returns (bytes16 ret, uint nextOffset) { return BytesParsing.asBytes16Cd(encoded, offset); } - function asBytes16Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes16 ret, uint nextOffset) { + function asBytes16Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes16 ret, uint nextOffset) { return BytesParsing.asBytes16Unchecked(encoded, offset); } - function asBytes16(bytes calldata encoded, uint offset) external pure returns (bytes16 ret, uint nextOffset) { + function asBytes16(bytes calldata encoded, uint offset) external pure returns (bytes16 ret, uint nextOffset) { return BytesParsing.asBytes16(encoded, offset); } - function asBytes17CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes17 ret, uint nextOffset) { + function asBytes17CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes17 ret, uint nextOffset) { return BytesParsing.asBytes17CdUnchecked(encoded, offset); } - function asBytes17Cd(bytes calldata encoded, uint offset) external pure returns (bytes17 ret, uint nextOffset) { + function asBytes17Cd(bytes calldata encoded, uint offset) external pure returns (bytes17 ret, uint nextOffset) { return BytesParsing.asBytes17Cd(encoded, offset); } - function asBytes17Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes17 ret, uint nextOffset) { + function asBytes17Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes17 ret, uint nextOffset) { return BytesParsing.asBytes17Unchecked(encoded, offset); } - function asBytes17(bytes calldata encoded, uint offset) external pure returns (bytes17 ret, uint nextOffset) { + function asBytes17(bytes calldata encoded, uint offset) external pure returns (bytes17 ret, uint nextOffset) { return BytesParsing.asBytes17(encoded, offset); } - function asBytes18CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes18 ret, uint nextOffset) { + function asBytes18CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes18 ret, uint nextOffset) { return BytesParsing.asBytes18CdUnchecked(encoded, offset); } - function asBytes18Cd(bytes calldata encoded, uint offset) external pure returns (bytes18 ret, uint nextOffset) { + function asBytes18Cd(bytes calldata encoded, uint offset) external pure returns (bytes18 ret, uint nextOffset) { return BytesParsing.asBytes18Cd(encoded, offset); } - function asBytes18Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes18 ret, uint nextOffset) { + function asBytes18Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes18 ret, uint nextOffset) { return BytesParsing.asBytes18Unchecked(encoded, offset); } - function asBytes18(bytes calldata encoded, uint offset) external pure returns (bytes18 ret, uint nextOffset) { + function asBytes18(bytes calldata encoded, uint offset) external pure returns (bytes18 ret, uint nextOffset) { return BytesParsing.asBytes18(encoded, offset); } - function asBytes19CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes19 ret, uint nextOffset) { + function asBytes19CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes19 ret, uint nextOffset) { return BytesParsing.asBytes19CdUnchecked(encoded, offset); } - function asBytes19Cd(bytes calldata encoded, uint offset) external pure returns (bytes19 ret, uint nextOffset) { + function asBytes19Cd(bytes calldata encoded, uint offset) external pure returns (bytes19 ret, uint nextOffset) { return BytesParsing.asBytes19Cd(encoded, offset); } - function asBytes19Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes19 ret, uint nextOffset) { + function asBytes19Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes19 ret, uint nextOffset) { return BytesParsing.asBytes19Unchecked(encoded, offset); } - function asBytes19(bytes calldata encoded, uint offset) external pure returns (bytes19 ret, uint nextOffset) { + function asBytes19(bytes calldata encoded, uint offset) external pure returns (bytes19 ret, uint nextOffset) { return BytesParsing.asBytes19(encoded, offset); } - function asBytes20CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes20 ret, uint nextOffset) { + function asBytes20CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes20 ret, uint nextOffset) { return BytesParsing.asBytes20CdUnchecked(encoded, offset); } - function asBytes20Cd(bytes calldata encoded, uint offset) external pure returns (bytes20 ret, uint nextOffset) { + function asBytes20Cd(bytes calldata encoded, uint offset) external pure returns (bytes20 ret, uint nextOffset) { return BytesParsing.asBytes20Cd(encoded, offset); } - function asBytes20Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes20 ret, uint nextOffset) { + function asBytes20Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes20 ret, uint nextOffset) { return BytesParsing.asBytes20Unchecked(encoded, offset); } - function asBytes20(bytes calldata encoded, uint offset) external pure returns (bytes20 ret, uint nextOffset) { + function asBytes20(bytes calldata encoded, uint offset) external pure returns (bytes20 ret, uint nextOffset) { return BytesParsing.asBytes20(encoded, offset); } - function asBytes21CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes21 ret, uint nextOffset) { + function asBytes21CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes21 ret, uint nextOffset) { return BytesParsing.asBytes21CdUnchecked(encoded, offset); } - function asBytes21Cd(bytes calldata encoded, uint offset) external pure returns (bytes21 ret, uint nextOffset) { + function asBytes21Cd(bytes calldata encoded, uint offset) external pure returns (bytes21 ret, uint nextOffset) { return BytesParsing.asBytes21Cd(encoded, offset); } - function asBytes21Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes21 ret, uint nextOffset) { + function asBytes21Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes21 ret, uint nextOffset) { return BytesParsing.asBytes21Unchecked(encoded, offset); } - function asBytes21(bytes calldata encoded, uint offset) external pure returns (bytes21 ret, uint nextOffset) { + function asBytes21(bytes calldata encoded, uint offset) external pure returns (bytes21 ret, uint nextOffset) { return BytesParsing.asBytes21(encoded, offset); } - function asBytes22CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes22 ret, uint nextOffset) { + function asBytes22CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes22 ret, uint nextOffset) { return BytesParsing.asBytes22CdUnchecked(encoded, offset); } - function asBytes22Cd(bytes calldata encoded, uint offset) external pure returns (bytes22 ret, uint nextOffset) { + function asBytes22Cd(bytes calldata encoded, uint offset) external pure returns (bytes22 ret, uint nextOffset) { return BytesParsing.asBytes22Cd(encoded, offset); } - function asBytes22Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes22 ret, uint nextOffset) { + function asBytes22Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes22 ret, uint nextOffset) { return BytesParsing.asBytes22Unchecked(encoded, offset); } - function asBytes22(bytes calldata encoded, uint offset) external pure returns (bytes22 ret, uint nextOffset) { + function asBytes22(bytes calldata encoded, uint offset) external pure returns (bytes22 ret, uint nextOffset) { return BytesParsing.asBytes22(encoded, offset); } - function asBytes23CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes23 ret, uint nextOffset) { + function asBytes23CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes23 ret, uint nextOffset) { return BytesParsing.asBytes23CdUnchecked(encoded, offset); } - function asBytes23Cd(bytes calldata encoded, uint offset) external pure returns (bytes23 ret, uint nextOffset) { + function asBytes23Cd(bytes calldata encoded, uint offset) external pure returns (bytes23 ret, uint nextOffset) { return BytesParsing.asBytes23Cd(encoded, offset); } - function asBytes23Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes23 ret, uint nextOffset) { + function asBytes23Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes23 ret, uint nextOffset) { return BytesParsing.asBytes23Unchecked(encoded, offset); } - function asBytes23(bytes calldata encoded, uint offset) external pure returns (bytes23 ret, uint nextOffset) { + function asBytes23(bytes calldata encoded, uint offset) external pure returns (bytes23 ret, uint nextOffset) { return BytesParsing.asBytes23(encoded, offset); } - function asBytes24CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes24 ret, uint nextOffset) { + function asBytes24CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes24 ret, uint nextOffset) { return BytesParsing.asBytes24CdUnchecked(encoded, offset); } - function asBytes24Cd(bytes calldata encoded, uint offset) external pure returns (bytes24 ret, uint nextOffset) { + function asBytes24Cd(bytes calldata encoded, uint offset) external pure returns (bytes24 ret, uint nextOffset) { return BytesParsing.asBytes24Cd(encoded, offset); } - function asBytes24Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes24 ret, uint nextOffset) { + function asBytes24Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes24 ret, uint nextOffset) { return BytesParsing.asBytes24Unchecked(encoded, offset); } - function asBytes24(bytes calldata encoded, uint offset) external pure returns (bytes24 ret, uint nextOffset) { + function asBytes24(bytes calldata encoded, uint offset) external pure returns (bytes24 ret, uint nextOffset) { return BytesParsing.asBytes24(encoded, offset); } - function asBytes25CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes25 ret, uint nextOffset) { + function asBytes25CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes25 ret, uint nextOffset) { return BytesParsing.asBytes25CdUnchecked(encoded, offset); } - function asBytes25Cd(bytes calldata encoded, uint offset) external pure returns (bytes25 ret, uint nextOffset) { + function asBytes25Cd(bytes calldata encoded, uint offset) external pure returns (bytes25 ret, uint nextOffset) { return BytesParsing.asBytes25Cd(encoded, offset); } - function asBytes25Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes25 ret, uint nextOffset) { + function asBytes25Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes25 ret, uint nextOffset) { return BytesParsing.asBytes25Unchecked(encoded, offset); } - function asBytes25(bytes calldata encoded, uint offset) external pure returns (bytes25 ret, uint nextOffset) { + function asBytes25(bytes calldata encoded, uint offset) external pure returns (bytes25 ret, uint nextOffset) { return BytesParsing.asBytes25(encoded, offset); } - function asBytes26CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes26 ret, uint nextOffset) { + function asBytes26CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes26 ret, uint nextOffset) { return BytesParsing.asBytes26CdUnchecked(encoded, offset); } - function asBytes26Cd(bytes calldata encoded, uint offset) external pure returns (bytes26 ret, uint nextOffset) { + function asBytes26Cd(bytes calldata encoded, uint offset) external pure returns (bytes26 ret, uint nextOffset) { return BytesParsing.asBytes26Cd(encoded, offset); } - function asBytes26Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes26 ret, uint nextOffset) { + function asBytes26Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes26 ret, uint nextOffset) { return BytesParsing.asBytes26Unchecked(encoded, offset); } - function asBytes26(bytes calldata encoded, uint offset) external pure returns (bytes26 ret, uint nextOffset) { + function asBytes26(bytes calldata encoded, uint offset) external pure returns (bytes26 ret, uint nextOffset) { return BytesParsing.asBytes26(encoded, offset); } - function asBytes27CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes27 ret, uint nextOffset) { + function asBytes27CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes27 ret, uint nextOffset) { return BytesParsing.asBytes27CdUnchecked(encoded, offset); } - function asBytes27Cd(bytes calldata encoded, uint offset) external pure returns (bytes27 ret, uint nextOffset) { + function asBytes27Cd(bytes calldata encoded, uint offset) external pure returns (bytes27 ret, uint nextOffset) { return BytesParsing.asBytes27Cd(encoded, offset); } - function asBytes27Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes27 ret, uint nextOffset) { + function asBytes27Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes27 ret, uint nextOffset) { return BytesParsing.asBytes27Unchecked(encoded, offset); } - function asBytes27(bytes calldata encoded, uint offset) external pure returns (bytes27 ret, uint nextOffset) { + function asBytes27(bytes calldata encoded, uint offset) external pure returns (bytes27 ret, uint nextOffset) { return BytesParsing.asBytes27(encoded, offset); } - function asBytes28CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes28 ret, uint nextOffset) { + function asBytes28CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes28 ret, uint nextOffset) { return BytesParsing.asBytes28CdUnchecked(encoded, offset); } - function asBytes28Cd(bytes calldata encoded, uint offset) external pure returns (bytes28 ret, uint nextOffset) { + function asBytes28Cd(bytes calldata encoded, uint offset) external pure returns (bytes28 ret, uint nextOffset) { return BytesParsing.asBytes28Cd(encoded, offset); } - function asBytes28Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes28 ret, uint nextOffset) { + function asBytes28Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes28 ret, uint nextOffset) { return BytesParsing.asBytes28Unchecked(encoded, offset); } - function asBytes28(bytes calldata encoded, uint offset) external pure returns (bytes28 ret, uint nextOffset) { + function asBytes28(bytes calldata encoded, uint offset) external pure returns (bytes28 ret, uint nextOffset) { return BytesParsing.asBytes28(encoded, offset); } - function asBytes29CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes29 ret, uint nextOffset) { + function asBytes29CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes29 ret, uint nextOffset) { return BytesParsing.asBytes29CdUnchecked(encoded, offset); } - function asBytes29Cd(bytes calldata encoded, uint offset) external pure returns (bytes29 ret, uint nextOffset) { + function asBytes29Cd(bytes calldata encoded, uint offset) external pure returns (bytes29 ret, uint nextOffset) { return BytesParsing.asBytes29Cd(encoded, offset); } - function asBytes29Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes29 ret, uint nextOffset) { + function asBytes29Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes29 ret, uint nextOffset) { return BytesParsing.asBytes29Unchecked(encoded, offset); } - function asBytes29(bytes calldata encoded, uint offset) external pure returns (bytes29 ret, uint nextOffset) { + function asBytes29(bytes calldata encoded, uint offset) external pure returns (bytes29 ret, uint nextOffset) { return BytesParsing.asBytes29(encoded, offset); } - function asBytes30CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes30 ret, uint nextOffset) { + function asBytes30CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes30 ret, uint nextOffset) { return BytesParsing.asBytes30CdUnchecked(encoded, offset); } - function asBytes30Cd(bytes calldata encoded, uint offset) external pure returns (bytes30 ret, uint nextOffset) { + function asBytes30Cd(bytes calldata encoded, uint offset) external pure returns (bytes30 ret, uint nextOffset) { return BytesParsing.asBytes30Cd(encoded, offset); } - function asBytes30Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes30 ret, uint nextOffset) { + function asBytes30Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes30 ret, uint nextOffset) { return BytesParsing.asBytes30Unchecked(encoded, offset); } - function asBytes30(bytes calldata encoded, uint offset) external pure returns (bytes30 ret, uint nextOffset) { + function asBytes30(bytes calldata encoded, uint offset) external pure returns (bytes30 ret, uint nextOffset) { return BytesParsing.asBytes30(encoded, offset); } - function asBytes31CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes31 ret, uint nextOffset) { + function asBytes31CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes31 ret, uint nextOffset) { return BytesParsing.asBytes31CdUnchecked(encoded, offset); } - function asBytes31Cd(bytes calldata encoded, uint offset) external pure returns (bytes31 ret, uint nextOffset) { + function asBytes31Cd(bytes calldata encoded, uint offset) external pure returns (bytes31 ret, uint nextOffset) { return BytesParsing.asBytes31Cd(encoded, offset); } - function asBytes31Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes31 ret, uint nextOffset) { + function asBytes31Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes31 ret, uint nextOffset) { return BytesParsing.asBytes31Unchecked(encoded, offset); } - function asBytes31(bytes calldata encoded, uint offset) external pure returns (bytes31 ret, uint nextOffset) { + function asBytes31(bytes calldata encoded, uint offset) external pure returns (bytes31 ret, uint nextOffset) { return BytesParsing.asBytes31(encoded, offset); } - function asBytes32CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes32 ret, uint nextOffset) { + function asBytes32CdUnchecked(bytes calldata encoded, uint offset) external pure returns (bytes32 ret, uint nextOffset) { return BytesParsing.asBytes32CdUnchecked(encoded, offset); } - function asBytes32Cd(bytes calldata encoded, uint offset) external pure returns (bytes32 ret, uint nextOffset) { + function asBytes32Cd(bytes calldata encoded, uint offset) external pure returns (bytes32 ret, uint nextOffset) { return BytesParsing.asBytes32Cd(encoded, offset); } - function asBytes32Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes32 ret, uint nextOffset) { + function asBytes32Unchecked(bytes calldata encoded, uint offset) external pure returns (bytes32 ret, uint nextOffset) { return BytesParsing.asBytes32Unchecked(encoded, offset); } - function asBytes32(bytes calldata encoded, uint offset) external pure returns (bytes32 ret, uint nextOffset) { + function asBytes32(bytes calldata encoded, uint offset) external pure returns (bytes32 ret, uint nextOffset) { return BytesParsing.asBytes32(encoded, offset); } } diff --git a/test/generated/QueryResponseTestWrapper.sol b/test/generated/QueryResponseTestWrapper.sol new file mode 100644 index 0000000..e6f140a --- /dev/null +++ b/test/generated/QueryResponseTestWrapper.sol @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: Apache 2 +pragma solidity ^0.8.24; + +import "wormhole-sdk/libraries/QueryResponse.sol"; + +// This file was auto-generated by wormhole-solidity-sdk gen/libraryTestWrapper.ts + +contract QueryTypeTestWrapper { + function min() external pure returns (uint8) { + return QueryType.min(); + } + + function max() external pure returns (uint8) { + return QueryType.max(); + } + + function checkValid(uint8 queryType) external pure { + QueryType.checkValid(queryType); + } + + function isValid(uint8 queryType) external pure returns (bool) { + return QueryType.isValid(queryType); + } +} + +contract QueryResponseLibTestWrapper { + function calcPrefixedResponseHash(bytes calldata response) external pure returns (bytes32) { + return QueryResponseLib.calcPrefixedResponseHash(response); + } + + function parseAndVerifyQueryResponse( + address wormhole, + bytes calldata response, + IWormhole.Signature[] calldata signatures + ) external view returns (QueryResponse memory ret) { + return QueryResponseLib.parseAndVerifyQueryResponse(wormhole, response, signatures); + } + + function verifyQueryResponse( + address wormhole, + bytes calldata response, + IWormhole.Signature[] calldata signatures + ) external view { + QueryResponseLib.verifyQueryResponse(wormhole, response, signatures); + } + + function verifyQueryResponse( + address wormhole, + bytes32 prefixedResponseHash, + IWormhole.Signature[] calldata signatures + ) external view { + QueryResponseLib.verifyQueryResponse(wormhole, prefixedResponseHash, signatures); + } + + function parseQueryResponse(bytes calldata response) external pure returns (QueryResponse memory ret) { + return QueryResponseLib.parseQueryResponse(response); + } + + function parseEthCallQueryResponse(PerChainQueryResponse calldata pcr) external pure returns (EthCallQueryResponse memory ret) { + return QueryResponseLib.parseEthCallQueryResponse(pcr); + } + + function parseEthCallByTimestampQueryResponse(PerChainQueryResponse calldata pcr) external pure returns (EthCallByTimestampQueryResponse memory ret) { + return QueryResponseLib.parseEthCallByTimestampQueryResponse(pcr); + } + + function parseEthCallWithFinalityQueryResponse(PerChainQueryResponse calldata pcr) external pure returns (EthCallWithFinalityQueryResponse memory ret) { + return QueryResponseLib.parseEthCallWithFinalityQueryResponse(pcr); + } + + function parseSolanaAccountQueryResponse(PerChainQueryResponse calldata pcr) external pure returns (SolanaAccountQueryResponse memory ret) { + return QueryResponseLib.parseSolanaAccountQueryResponse(pcr); + } + + function parseSolanaPdaQueryResponse(PerChainQueryResponse calldata pcr) external pure returns (SolanaPdaQueryResponse memory ret) { + return QueryResponseLib.parseSolanaPdaQueryResponse(pcr); + } + + function validateBlockTime(uint64 blockTimeInMicroSeconds, uint256 minBlockTimeInSeconds) external pure { + QueryResponseLib.validateBlockTime(blockTimeInMicroSeconds, minBlockTimeInSeconds); + } + + function validateBlockNum(uint64 blockNum, uint256 minBlockNum) external pure { + QueryResponseLib.validateBlockNum(blockNum, minBlockNum); + } + + function validateChainId(uint16 chainId, uint16[] calldata validChainIds) external pure { + QueryResponseLib.validateChainId(chainId, validChainIds); + } + + function validateEthCallRecord( + EthCallRecord[] calldata ecrs, + address[] calldata validContractAddresses, + bytes4[] calldata validFunctionSignatures + ) external pure { + QueryResponseLib.validateEthCallRecord(ecrs, validContractAddresses, validFunctionSignatures); + } + + function validateEthCallRecord( + EthCallRecord calldata ecd, + address[] calldata validContractAddresses, + bytes4[] calldata validFunctionSignatures + ) external pure { + QueryResponseLib.validateEthCallRecord(ecd, validContractAddresses, validFunctionSignatures); + } +} From 2babcf412cc48b50ab196476226de86c2beb4324 Mon Sep 17 00:00:00 2001 From: nonergodic Date: Tue, 29 Oct 2024 17:31:31 -0700 Subject: [PATCH 5/6] remove package-lock to always pull latest version of TS SDK --- gen/Makefile | 4 +- gen/package-lock.json | 500 ------------------------------------------ 2 files changed, 2 insertions(+), 502 deletions(-) delete mode 100644 gen/package-lock.json diff --git a/gen/Makefile b/gen/Makefile index c6d694e..8430e31 100644 --- a/gen/Makefile +++ b/gen/Makefile @@ -21,8 +21,8 @@ clean: FORCE: -node_modules: package-lock.json - npm ci +node_modules: + npm i define ruleGenerator $(call fnGeneratorTarget,$(1)): node_modules $(1).ts diff --git a/gen/package-lock.json b/gen/package-lock.json deleted file mode 100644 index f1874cb..0000000 --- a/gen/package-lock.json +++ /dev/null @@ -1,500 +0,0 @@ -{ - "name": "solidity-generators", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "solidity-generators", - "version": "1.0.0", - "license": "Apache-2.0", - "dependencies": { - "@types/node": "^20.3.1", - "@wormhole-foundation/sdk-base": "latest", - "@wormhole-foundation/sdk-definitions": "latest", - "@wormhole-foundation/sdk-evm": "latest", - "ts-node": "^10.9.2", - "typescript": "^5.5.4" - } - }, - "node_modules/@adraffy/ens-normalize": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", - "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", - "license": "MIT" - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@noble/curves": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.6.0.tgz", - "integrity": "sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ==", - "license": "MIT", - "dependencies": { - "@noble/hashes": "1.5.0" - }, - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@noble/hashes": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.5.0.tgz", - "integrity": "sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==", - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/base": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", - "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", - "license": "MIT", - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "license": "MIT" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "license": "MIT" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "license": "MIT" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "20.17.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.2.tgz", - "integrity": "sha512-OOHK4sjXqkL7yQ7VEEHcf6+0jSvKjWqwnaCtY7AKD/VLEvRHMsxxu7eI8ErnjxHS8VwmekD4PeVCpu4qZEZSxg==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.19.2" - } - }, - "node_modules/@wormhole-foundation/sdk-base": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@wormhole-foundation/sdk-base/-/sdk-base-0.15.0.tgz", - "integrity": "sha512-Akc17+YuI+m0VWVIxI3F7AkeU5FR9l/OP30UT+VuYQCPxqMI/d/crMu5ZbXmYJWAzrsFpbu9/ohOS0WsDwj0EQ==", - "license": "Apache-2.0", - "dependencies": { - "@scure/base": "^1.1.3" - } - }, - "node_modules/@wormhole-foundation/sdk-connect": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@wormhole-foundation/sdk-connect/-/sdk-connect-0.15.0.tgz", - "integrity": "sha512-lXK5o4O0QOED7Q3nDKM7z2BwN7NZp+vYwW4xI4gs/v61jkx8Hfa0RTKu+RNxB2VTNl+XCA6rQbz5QGWSI0BP7g==", - "license": "Apache-2.0", - "dependencies": { - "@wormhole-foundation/sdk-base": "0.15.0", - "@wormhole-foundation/sdk-definitions": "0.15.0", - "axios": "^1.4.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@wormhole-foundation/sdk-definitions": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@wormhole-foundation/sdk-definitions/-/sdk-definitions-0.15.0.tgz", - "integrity": "sha512-RfEXMQga4IXlPbKhldcWcMHVGdKfmIbTCbH2cf/TwSVTZp3dPrktLbqkfuB7+/JRML8Bg9b4xwb/YZ5gxNDMsA==", - "dependencies": { - "@noble/curves": "^1.4.0", - "@noble/hashes": "^1.3.1", - "@wormhole-foundation/sdk-base": "0.15.0" - } - }, - "node_modules/@wormhole-foundation/sdk-evm": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@wormhole-foundation/sdk-evm/-/sdk-evm-0.15.0.tgz", - "integrity": "sha512-m7H9S4DQcOUAfcrdj9dZAXhdSipHr4vn9j9YQgltbrZiY0/tG5MShcZbZzBtLg7/JfEgp29Z6lje7Q2zFJUmCw==", - "license": "Apache-2.0", - "dependencies": { - "@wormhole-foundation/sdk-connect": "0.15.0", - "ethers": "^6.5.1" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/aes-js": { - "version": "4.0.0-beta.5", - "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", - "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", - "license": "MIT" - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "license": "MIT" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/axios": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", - "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "license": "MIT" - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/ethers": { - "version": "6.13.4", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.13.4.tgz", - "integrity": "sha512-21YtnZVg4/zKkCQPjrDj38B1r4nQvTZLopUGMLQ1ePU2zV/joCfDC3t3iKQjWRzjjjbzR+mdAIoikeBRNkdllA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/ethers-io/" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@adraffy/ens-normalize": "1.10.1", - "@noble/curves": "1.2.0", - "@noble/hashes": "1.3.2", - "@types/node": "22.7.5", - "aes-js": "4.0.0-beta.5", - "tslib": "2.7.0", - "ws": "8.17.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/ethers/node_modules/@noble/curves": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", - "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", - "license": "MIT", - "dependencies": { - "@noble/hashes": "1.3.2" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/ethers/node_modules/@noble/hashes": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", - "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", - "license": "MIT", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/ethers/node_modules/@types/node": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", - "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.19.2" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "license": "ISC" - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "license": "MIT", - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", - "license": "0BSD" - }, - "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "license": "MIT" - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "license": "MIT" - }, - "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "license": "MIT", - "engines": { - "node": ">=6" - } - } - } -} From f54a1a0ff1ac184d2f9a5ce41b1bcfbcc06b418e Mon Sep 17 00:00:00 2001 From: nonergodic Date: Tue, 29 Oct 2024 17:33:12 -0700 Subject: [PATCH 6/6] use Makefile for CI --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b3bc773..1571ef5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,10 +29,10 @@ jobs: - name: Run Forge build run: | forge --version - forge build --sizes + make id: build - name: Run Forge tests run: | - forge test -vvv + make test id: test