CCQ/EVM: Testing library for building responses (#3768)

* CCQ/EVM: Testing library for building responses

* Add request building stuff

* Convert to a library

* Don't pass in unnecessary lengths
This commit is contained in:
bruce-riley 2024-03-04 12:48:03 -05:00 committed by GitHub
parent c0b402cd1f
commit 0a49c6ee28
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 443 additions and 25 deletions

View File

@ -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);

View File

@ -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
);
}
}

View File

@ -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");
}
}