diff --git a/ethereum/forge-test/query/QueryResponse.t.sol b/ethereum/forge-test/query/QueryResponse.t.sol index 8240f4bb36..cc431258ea 100644 --- a/ethereum/forge-test/query/QueryResponse.t.sol +++ b/ethereum/forge-test/query/QueryResponse.t.sol @@ -9,6 +9,7 @@ import "../../contracts/Implementation.sol"; import "../../contracts/Setup.sol"; import "../../contracts/Wormhole.sol"; import "forge-std/Test.sol"; +import "./QueryTest.sol"; // @dev A non-abstract QueryResponse contract contract QueryResponseContract is QueryResponse { @@ -66,7 +67,6 @@ contract TestQueryResponse is Test { uint8 _version, uint16 _senderChainId, bytes memory _signature, - uint32 _queryRequestLen, uint8 _queryRequestVersion, uint32 _queryRequestNonce, uint8 _numPerChainQueries, @@ -74,15 +74,17 @@ contract TestQueryResponse is Test { uint8 _numPerChainResponses, bytes memory _perChainResponses ) internal pure returns (bytes memory){ - return abi.encodePacked( - _version, - _senderChainId, - _signature, - _queryRequestLen, + bytes memory queryRequest = QueryTest.buildOffChainQueryRequestBytes( _queryRequestVersion, _queryRequestNonce, _numPerChainQueries, - _perChainQueries, + _perChainQueries + ); + return QueryTest.buildQueryResponseBytes( + _version, + _senderChainId, + _signature, + queryRequest, _numPerChainResponses, _perChainResponses ); @@ -116,21 +118,21 @@ contract TestQueryResponse is Test { } function test_getResponseHash() public { - bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestLen, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); + 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, queryRequestLen, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); + 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, queryRequestLen, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); + 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}); @@ -139,7 +141,7 @@ contract TestQueryResponse is Test { } function test_parseAndVerifyQueryResponse() public { - bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestLen, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); + 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}); @@ -306,7 +308,7 @@ contract TestQueryResponse is Test { // Start of Solana Stuff /////////////////////////////////////////////////////////////////////////// function test_verifyQueryResponseSignaturesForSolana() public view { - bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, solanaSignature, solanaQueryRequestLen, solanaQueryRequestVersion, solanaQueryRequestNonce, solanaNumPerChainQueries, solanaPerChainQueries, solanaNumPerChainResponses, solanaPerChainResponses); + bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, solanaSignature, solanaQueryRequestVersion, solanaQueryRequestNonce, solanaNumPerChainQueries, solanaPerChainQueries, solanaNumPerChainResponses, solanaPerChainResponses); (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}); @@ -413,7 +415,7 @@ contract TestQueryResponse is Test { function testFuzz_parseAndVerifyQueryResponse_fuzzVersion(uint8 _version) public { vm.assume(_version != 1); - bytes memory resp = concatenateQueryResponseBytesOffChain(_version, senderChainId, signature, queryRequestLen, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); + 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}); @@ -424,7 +426,7 @@ contract TestQueryResponse is Test { function testFuzz_parseAndVerifyQueryResponse_fuzzSenderChainId(uint16 _senderChainId) public { vm.assume(_senderChainId != 0); - bytes memory resp = concatenateQueryResponseBytesOffChain(version, _senderChainId, signature, queryRequestLen, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); + 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}); @@ -437,7 +439,7 @@ contract TestQueryResponse is Test { // 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, queryRequestLen, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); + 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}); @@ -450,7 +452,7 @@ contract TestQueryResponse is Test { // 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, queryRequestLen, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); + 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}); @@ -462,7 +464,7 @@ contract TestQueryResponse is Test { // We add 6 to account for version + nonce + numPerChainQueries vm.assume(_queryRequestLen != _perChainQueries.length + 6); - bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, _queryRequestLen, queryRequestVersion, queryRequestNonce, numPerChainQueries, _perChainQueries, numPerChainResponses, perChainResponses); + 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}); @@ -473,7 +475,7 @@ contract TestQueryResponse is Test { function testFuzz_parseAndVerifyQueryResponse_fuzzQueryRequestVersion(uint8 _version, uint8 _queryRequestVersion) public { vm.assume(_version != _queryRequestVersion); - bytes memory resp = concatenateQueryResponseBytesOffChain(_version, senderChainId, signature, queryRequestLen, _queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); + 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}); @@ -482,7 +484,7 @@ contract TestQueryResponse is Test { } function testFuzz_parseAndVerifyQueryResponse_fuzzQueryRequestNonce(uint32 _queryRequestNonce) public { - bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestLen, queryRequestVersion, _queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); + 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}); @@ -494,7 +496,7 @@ contract TestQueryResponse is Test { function testFuzz_parseAndVerifyQueryResponse_fuzzNumPerChainQueriesAndResponses(uint8 _numPerChainQueries, uint8 _numPerChainResponses) public { vm.assume(_numPerChainQueries != _numPerChainResponses); - bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestLen, queryRequestVersion, queryRequestNonce, _numPerChainQueries, perChainQueries, _numPerChainResponses, perChainResponses); + 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}); @@ -508,7 +510,7 @@ contract TestQueryResponse is Test { 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, queryRequestLen, queryRequestVersion, queryRequestNonce, numPerChainQueries, packedPerChainQueries, numPerChainResponses, packedPerChainResponses); + 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}); @@ -523,7 +525,7 @@ contract TestQueryResponse is Test { 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, queryRequestLen, queryRequestVersion, queryRequestNonce, numPerChainQueries, packedPerChainQueries, numPerChainResponses, packedPerChainResponses); + 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}); @@ -536,7 +538,7 @@ contract TestQueryResponse is Test { 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, queryRequestLen, queryRequestVersion, queryRequestNonce, numPerChainQueries, packedPerChainQueries, numPerChainResponses, packedPerChainResponses); + 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}); @@ -549,7 +551,7 @@ contract TestQueryResponse is Test { 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, queryRequestLen, queryRequestVersion, queryRequestNonce, numPerChainQueries, packedPerChainQueries, numPerChainResponses, packedPerChainResponses); + 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}); @@ -581,7 +583,7 @@ contract TestQueryResponse is Test { function testFuzz_verifyQueryResponseSignatures_validSignatureWrongPrefix(bytes calldata responsePrefix) public { vm.assume(keccak256(responsePrefix) != keccak256(queryResponse.responsePrefix())); - bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestLen, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses); + 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); diff --git a/ethereum/forge-test/query/QueryTest.sol b/ethereum/forge-test/query/QueryTest.sol new file mode 100644 index 0000000000..0f5a9675a6 --- /dev/null +++ b/ethereum/forge-test/query/QueryTest.sol @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: Apache 2 + +// forge test --match-contract QueryTest + +pragma solidity ^0.8.4; + +// @dev QueryTest is a library to build Cross Chain Query (CCQ) responses for testing purposes. +library QueryTest { + // + // 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() + ); + } + + /// @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 + ); + } + + /// @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 + ); + } + + /// @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 + ); + } + + /// @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 + ); + } + + /// @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 an 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 + ); + } + + // + // 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() + ); + } + + /// @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 + ); + } + + /// @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 + ); + } + + /// @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 + ); + } + + /// @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 + ); + } + + /// @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 + ); + } +} diff --git a/ethereum/forge-test/query/QueryTest.t.sol b/ethereum/forge-test/query/QueryTest.t.sol new file mode 100644 index 0000000000..70ac80f079 --- /dev/null +++ b/ethereum/forge-test/query/QueryTest.t.sol @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: Apache 2 + +// forge test --match-contract QueryTest + +pragma solidity ^0.8.4; + +import "forge-std/Test.sol"; +import "./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"); + } + + function test_buildEthCallWithFinalityRequestBytes() public { + bytes memory ecr = QueryTest.buildEthCallWithFinalityRequestBytes( + /* blockId */ "0x1f8", + /* finality */ "finalized", + /* numCallData */ 2, + /* callData */ hex"ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567" + ); + assertEq(ecr, hex"0000000530783166380000000966696e616c697a656402ddb64fe46a91d46ee29420539fc25fd07c5fea3e0000000406fdde03ddb64fe46a91d46ee29420539fc25fd07c5fea3e00000004313ce567"); + } + + 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"); + } + + // + // 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"); + } +}