CCQ/EVM: sol_pda support (#3790)
* CCQ/EVM: sol_pda support * Code review rework * Code review rework
This commit is contained in:
parent
405bd58580
commit
65c4dc4177
|
@ -24,7 +24,7 @@ struct ParsedPerChainQueryResponse {
|
|||
bytes response;
|
||||
}
|
||||
|
||||
// @dev EthCallQueryResponse describes an ETH call per-chain query.
|
||||
// @dev EthCallQueryResponse describes the response to an ETH call per-chain query.
|
||||
struct EthCallQueryResponse {
|
||||
bytes requestBlockId;
|
||||
uint64 blockNum;
|
||||
|
@ -33,7 +33,7 @@ struct EthCallQueryResponse {
|
|||
EthCallData [] result;
|
||||
}
|
||||
|
||||
// @dev EthCallByTimestampQueryResponse describes an ETH call by timestamp per-chain query.
|
||||
// @dev EthCallByTimestampQueryResponse describes the response to an ETH call by timestamp per-chain query.
|
||||
struct EthCallByTimestampQueryResponse {
|
||||
bytes requestTargetBlockIdHint;
|
||||
bytes requestFollowingBlockIdHint;
|
||||
|
@ -47,7 +47,7 @@ struct EthCallByTimestampQueryResponse {
|
|||
EthCallData [] result;
|
||||
}
|
||||
|
||||
// @dev EthCallWithFinalityQueryResponse describes an ETH call with finality per-chain query.
|
||||
// @dev EthCallWithFinalityQueryResponse describes the response to an ETH call with finality per-chain query.
|
||||
struct EthCallWithFinalityQueryResponse {
|
||||
bytes requestBlockId;
|
||||
bytes requestFinality;
|
||||
|
@ -64,7 +64,7 @@ struct EthCallData {
|
|||
bytes result;
|
||||
}
|
||||
|
||||
// @dev SolanaAccountQueryResponse describes a Solana Account query per-chain query.
|
||||
// @dev SolanaAccountQueryResponse describes the response to a Solana Account query per-chain query.
|
||||
struct SolanaAccountQueryResponse {
|
||||
bytes requestCommitment;
|
||||
uint64 requestMinContextSlot;
|
||||
|
@ -86,6 +86,31 @@ struct SolanaAccountResult {
|
|||
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;
|
||||
}
|
||||
|
||||
// @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;
|
||||
}
|
||||
|
||||
// Custom errors
|
||||
error EmptyWormholeAddress();
|
||||
error InvalidResponseVersion();
|
||||
|
@ -94,7 +119,8 @@ error ZeroQueries();
|
|||
error NumberOfResponsesMismatch();
|
||||
error ChainIdMismatch();
|
||||
error RequestTypeMismatch();
|
||||
error UnsupportedQueryType();
|
||||
error UnsupportedQueryType(uint8 received);
|
||||
error WrongQueryType(uint8 received, uint8 expected);
|
||||
error UnexpectedNumberOfResults();
|
||||
error InvalidPayloadLength(uint256 received, uint256 expected);
|
||||
error InvalidContractAddress();
|
||||
|
@ -104,6 +130,8 @@ 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;
|
||||
|
||||
|
@ -111,11 +139,14 @@ abstract contract QueryResponse {
|
|||
|
||||
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_MAX = 5; // Keep this last
|
||||
uint8 public constant QT_SOL_PDA = 5;
|
||||
uint8 public constant QT_MAX = 6; // Keep this last
|
||||
|
||||
constructor(address _wormhole) {
|
||||
if (_wormhole == address(0)) {
|
||||
|
@ -207,7 +238,7 @@ abstract contract QueryResponse {
|
|||
}
|
||||
|
||||
if (r.responses[idx].queryType < QT_ETH_CALL || r.responses[idx].queryType >= QT_MAX) {
|
||||
revert UnsupportedQueryType();
|
||||
revert UnsupportedQueryType(r.responses[idx].queryType);
|
||||
}
|
||||
|
||||
(len, reqIdx) = response.asUint32Unchecked(reqIdx);
|
||||
|
@ -231,7 +262,7 @@ abstract contract QueryResponse {
|
|||
/// @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 UnsupportedQueryType();
|
||||
revert WrongQueryType(pcr.queryType, QT_ETH_CALL);
|
||||
}
|
||||
|
||||
uint reqIdx;
|
||||
|
@ -280,7 +311,7 @@ abstract contract QueryResponse {
|
|||
/// @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 UnsupportedQueryType();
|
||||
revert WrongQueryType(pcr.queryType, QT_ETH_CALL_BY_TIMESTAMP);
|
||||
}
|
||||
|
||||
uint reqIdx;
|
||||
|
@ -334,7 +365,7 @@ abstract contract QueryResponse {
|
|||
/// @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 UnsupportedQueryType();
|
||||
revert WrongQueryType(pcr.queryType, QT_ETH_CALL_WITH_FINALITY);
|
||||
}
|
||||
|
||||
uint reqIdx;
|
||||
|
@ -384,7 +415,7 @@ abstract contract QueryResponse {
|
|||
/// @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 UnsupportedQueryType();
|
||||
revert WrongQueryType(pcr.queryType, QT_SOL_ACCOUNT);
|
||||
}
|
||||
|
||||
uint reqIdx;
|
||||
|
@ -434,6 +465,71 @@ abstract contract QueryResponse {
|
|||
checkLength(pcr.response, respIdx);
|
||||
}
|
||||
|
||||
/// @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
|
||||
|
||||
(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 != numPdas) {
|
||||
revert UnexpectedNumberOfResults();
|
||||
}
|
||||
|
||||
r.results = new SolanaPdaResult[](numPdas);
|
||||
|
||||
// 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 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; }
|
||||
}
|
||||
|
||||
(r.results[idx].account, respIdx) = pcr.response.asBytes32Unchecked(respIdx); // Response account
|
||||
(r.results[idx].bump, respIdx) = pcr.response.asUint8Unchecked(respIdx); // Response bump
|
||||
|
||||
(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.asBoolUnckecked(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);
|
||||
}
|
||||
|
||||
/// @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
|
||||
|
|
|
@ -31,16 +31,27 @@ contract TestQueryResponse is Test {
|
|||
bytes perChainResponses = hex"000501000000b90000000002a61ac4c1adff9f6e180309e7d0d94c063338ddc61c1c4474cd6957c960efe659534d040005ff312e4f90c002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d57726170706564204d6174696300000000000000000000000000000000000000000000200000000000000000000000000000000000000000007ae5649beabeddf889364a";
|
||||
bytes perChainResponsesInner = hex"00000009307832613631616334020d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000406fdde030d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000418160ddd";
|
||||
|
||||
bytes solanaSignature = hex"acb1d93cdfe60f9776e3e05d7fafaf9d83a1d14db70317230f6b0b6f3a60708a1a64dddac02d3843f4c516f2509b89454a2e73c360fea47beee1c1a091ff9f3201";
|
||||
uint32 solanaQueryRequestLen = 0x00000073;
|
||||
uint8 solanaQueryRequestVersion = 0x01;
|
||||
uint32 solanaQueryRequestNonce = 0x0000002a;
|
||||
uint8 solanaNumPerChainQueries = 0x01;
|
||||
bytes solanaPerChainQueries = hex"000104000000660000000966696e616c697a656400000000000000000000000000000000000000000000000002165809739240a0ac03b98440fe8985548e3aa683cd0d4d9df5b5659669faa3019c006c48c8cbf33849cb07a3f936159cc523f9591cb1999abd45890ec5fee9b7";
|
||||
bytes solanaPerChainQueriesInner = hex"0000000966696e616c697a656400000000000000000000000000000000000000000000000002165809739240a0ac03b98440fe8985548e3aa683cd0d4d9df5b5659669faa3019c006c48c8cbf33849cb07a3f936159cc523f9591cb1999abd45890ec5fee9b7";
|
||||
uint8 solanaNumPerChainResponses = 0x01;
|
||||
bytes solanaPerChainResponses = hex"010001040000013f000000000000d85f00060f3e9915ddc03a8de2b1de609020bb0a0dcee594a8c06801619cf9ea2a498b9d910f9a25772b020000000000164d6000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90000005201000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d0000e8890423c78a09010000000000000000000000000000000000000000000000000000000000000000000000000000000000164d6000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90000005201000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d01000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000";
|
||||
bytes solanaPerChainResponsesInner = hex"000000000000d85f00060f3e9915ddc03a8de2b1de609020bb0a0dcee594a8c06801619cf9ea2a498b9d910f9a25772b020000000000164d6000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90000005201000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d0000e8890423c78a09010000000000000000000000000000000000000000000000000000000000000000000000000000000000164d6000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a90000005201000000574108aed69daf7e625a361864b1f74d13702f2ca56de9660e566d1d8691848d01000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000";
|
||||
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;
|
||||
|
||||
|
@ -191,7 +202,7 @@ contract TestQueryResponse is Test {
|
|||
response: hex"0000000002a61ac4c1adff9f6e180309e7d0d94c063338ddc61c1c4474cd6957c960efe659534d040005ff312e4f90c002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d57726170706564204d6174696300000000000000000000000000000000000000000000200000000000000000000000000000000000000000007ae5649beabeddf889364a"
|
||||
});
|
||||
|
||||
vm.expectRevert(UnsupportedQueryType.selector);
|
||||
vm.expectRevert(abi.encodeWithSelector(WrongQueryType.selector, 2, queryResponse.QT_ETH_CALL()));
|
||||
queryResponse.parseEthCallQueryResponse(r);
|
||||
}
|
||||
|
||||
|
@ -262,7 +273,7 @@ contract TestQueryResponse is Test {
|
|||
response: hex"0000000000004271ec70d2f70cf1933770ae760050a75334ce650aa091665ee43a6ed488cd154b0800000003f4810cc000000000000042720b1608c2cddfd9d7fb4ec94f79ec1389e2410e611a2c2bbde94e9ad37519ebbb00000003f4904f0002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000"
|
||||
});
|
||||
|
||||
vm.expectRevert(UnsupportedQueryType.selector);
|
||||
vm.expectRevert(abi.encodeWithSelector(WrongQueryType.selector, 1, queryResponse.QT_ETH_CALL_BY_TIMESTAMP()));
|
||||
queryResponse.parseEthCallByTimestampQueryResponse(r);
|
||||
}
|
||||
|
||||
|
@ -301,14 +312,14 @@ contract TestQueryResponse is Test {
|
|||
response: hex"00000000000060299eb9c56ffdae81214867ed217f5ab37e295c196b4f04b23a795d3e4aea6ff3d700000005bb1bd58002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d5772617070656420457468657200000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000"
|
||||
});
|
||||
|
||||
vm.expectRevert(UnsupportedQueryType.selector);
|
||||
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, solanaSignature, solanaQueryRequestVersion, solanaQueryRequestNonce, solanaNumPerChainQueries, solanaPerChainQueries, solanaNumPerChainResponses, solanaPerChainResponses);
|
||||
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});
|
||||
|
@ -321,8 +332,8 @@ contract TestQueryResponse is Test {
|
|||
ParsedPerChainQueryResponse memory r = ParsedPerChainQueryResponse({
|
||||
chainId: 1,
|
||||
queryType: 4,
|
||||
request: solanaPerChainQueriesInner,
|
||||
response: solanaPerChainResponsesInner
|
||||
request: solanaAccountPerChainQueriesInner,
|
||||
response: solanaAccountPerChainResponsesInner
|
||||
});
|
||||
|
||||
SolanaAccountQueryResponse memory sar = queryResponse.parseSolanaAccountQueryResponse(r);
|
||||
|
@ -356,11 +367,11 @@ contract TestQueryResponse is Test {
|
|||
ParsedPerChainQueryResponse memory r = ParsedPerChainQueryResponse({
|
||||
chainId: 2,
|
||||
queryType: 1,
|
||||
request: solanaPerChainQueriesInner,
|
||||
response: solanaPerChainResponsesInner
|
||||
request: solanaAccountPerChainQueriesInner,
|
||||
response: solanaAccountPerChainResponsesInner
|
||||
});
|
||||
|
||||
vm.expectRevert(UnsupportedQueryType.selector);
|
||||
vm.expectRevert(abi.encodeWithSelector(WrongQueryType.selector, 1, queryResponse.QT_SOL_ACCOUNT()));
|
||||
queryResponse.parseSolanaAccountQueryResponse(r);
|
||||
}
|
||||
|
||||
|
@ -371,7 +382,7 @@ contract TestQueryResponse is Test {
|
|||
chainId: 1,
|
||||
queryType: 4,
|
||||
request: requestWithOnlyOneAccount,
|
||||
response: solanaPerChainResponsesInner
|
||||
response: solanaAccountPerChainResponsesInner
|
||||
});
|
||||
|
||||
vm.expectRevert(UnexpectedNumberOfResults.selector);
|
||||
|
@ -385,7 +396,7 @@ contract TestQueryResponse is Test {
|
|||
chainId: 1,
|
||||
queryType: 4,
|
||||
request: requestWithExtraBytes,
|
||||
response: solanaPerChainResponsesInner
|
||||
response: solanaAccountPerChainResponsesInner
|
||||
});
|
||||
|
||||
vm.expectRevert(abi.encodeWithSelector(InvalidPayloadLength.selector, 106, 102));
|
||||
|
@ -398,7 +409,7 @@ contract TestQueryResponse is Test {
|
|||
ParsedPerChainQueryResponse memory r = ParsedPerChainQueryResponse({
|
||||
chainId: 1,
|
||||
queryType: 4,
|
||||
request: solanaPerChainQueriesInner,
|
||||
request: solanaAccountPerChainQueriesInner,
|
||||
response: responseWithExtraBytes
|
||||
});
|
||||
|
||||
|
@ -406,6 +417,95 @@ contract TestQueryResponse is Test {
|
|||
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");
|
||||
}
|
||||
|
||||
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 *************
|
||||
***********************************/
|
||||
|
@ -542,7 +642,7 @@ contract TestQueryResponse is Test {
|
|||
(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(UnsupportedQueryType.selector);
|
||||
vm.expectRevert(abi.encodeWithSelector(UnsupportedQueryType.selector, _requestQueryType));
|
||||
queryResponse.parseAndVerifyQueryResponse(resp, signatures);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,11 @@ pragma solidity ^0.8.4;
|
|||
|
||||
// @dev QueryTest is a library to build Cross Chain Query (CCQ) responses for testing purposes.
|
||||
library QueryTest {
|
||||
// Custom errors
|
||||
error SolanaTooManyPDAs();
|
||||
error SolanaTooManySeeds();
|
||||
error SolanaSeedTooLong();
|
||||
|
||||
//
|
||||
// Query Request stuff
|
||||
//
|
||||
|
@ -101,7 +106,7 @@ library QueryTest {
|
|||
);
|
||||
}
|
||||
|
||||
/// @dev buildSolanaAccountRequestBytes builds an sol_account query request from the specified fields.
|
||||
/// @dev buildSolanaAccountRequestBytes builds a sol_account query request from the specified fields.
|
||||
function buildSolanaAccountRequestBytes(
|
||||
bytes memory _commitment,
|
||||
uint64 _minContextSlot,
|
||||
|
@ -120,6 +125,93 @@ library QueryTest {
|
|||
_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;
|
||||
}
|
||||
|
||||
/// @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
|
||||
);
|
||||
}
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
//
|
||||
// Query Response stuff
|
||||
|
@ -242,4 +334,21 @@ library QueryTest {
|
|||
_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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,6 +86,116 @@ contract TestQueryTest is Test {
|
|||
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; }
|
||||
}
|
||||
|
||||
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; }
|
||||
}
|
||||
|
||||
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
|
||||
//
|
||||
|
@ -168,4 +278,15 @@ contract TestQueryTest is Test {
|
|||
);
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue