Skip to content

Commit

Permalink
bugfix incorrect vaa hash calculation and implement tests
Browse files Browse the repository at this point in the history
  • Loading branch information
nonergodic committed Jan 16, 2025
1 parent e19013d commit 4718928
Show file tree
Hide file tree
Showing 9 changed files with 1,492 additions and 16 deletions.
2 changes: 1 addition & 1 deletion gen/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ cctpDomains_TARGET = constants/CCTPDomains.sol
fnGeneratorTarget = ../src/$($(1)_TARGET)
GENERATOR_TARGETS = $(foreach generator,$(GENERATORS),$(call fnGeneratorTarget,$(generator)))

TEST_WRAPPERS = BytesParsing QueryResponse
TEST_WRAPPERS = BytesParsing QueryResponse VaaLib TokenBridgeMessages CctpMessages

fnTestWrapperTarget = ../test/generated/$(1)TestWrapper.sol
TEST_WRAPPER_TARGETS =\
Expand Down
19 changes: 19 additions & 0 deletions src/constants/CctpDomains.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;

// This file is auto-generated by gen/cctpDomains.ts.

uint32 constant CCTP_DOMAIN_ETHEREUM = 0;
uint32 constant CCTP_DOMAIN_AVALANCHE = 1;
uint32 constant CCTP_DOMAIN_OPTIMISM = 2;
uint32 constant CCTP_DOMAIN_ARBITRUM = 3;
uint32 constant CCTP_DOMAIN_SOLANA = 5;
uint32 constant CCTP_DOMAIN_BASE = 6;
uint32 constant CCTP_DOMAIN_POLYGON = 7;
uint32 constant CCTP_DOMAIN_SUI = 8;

// Additional Testnet mappings:
uint32 constant CCTP_DOMAIN_SEPOLIA = 0;
uint32 constant CCTP_DOMAIN_OPTIMISM_SEPOLIA = 2;
uint32 constant CCTP_DOMAIN_ARBITRUM_SEPOLIA = 3;
uint32 constant CCTP_DOMAIN_BASE_SEPOLIA = 6;
53 changes: 43 additions & 10 deletions src/libraries/VaaLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,23 @@ library VaaLib {
(vm, ) = decodeVmStructMemUnchecked(encodedVaa, 0, encodedVaa.length);
}

function decodeVaaStructCd(
bytes calldata encodedVaa
) internal pure returns (Vaa memory vaa) {
uint envelopeOffset;
(vaa.header, envelopeOffset) = decodeVaaHeaderStructCdUnchecked(encodedVaa);

uint payloadOffset;
(vaa.envelope, payloadOffset) = decodeVaaEnvelopeStructCdUnchecked(encodedVaa, envelopeOffset);
vaa.payload = decodeVaaPayloadCd(encodedVaa, payloadOffset);
}

function decodeVaaStructMem(
bytes memory encodedVaa
) internal pure returns (Vaa memory vaa) {
(vaa, ) = decodeVaaStructMemUnchecked(encodedVaa, 0, encodedVaa.length);
}

function decodeVaaEssentialsCd(
bytes calldata encodedVaa
) internal pure returns (
Expand Down Expand Up @@ -488,9 +505,10 @@ library VaaLib {
bytes memory encoded,
uint envelopeOffset,
uint vaaLength
) internal pure returns (bytes32) {
return keccak256SliceUnchecked(encoded, envelopeOffset, vaaLength);
}
) internal pure returns (bytes32) { unchecked {
envelopeOffset.checkBound(vaaLength);
return keccak256SliceUnchecked(encoded, envelopeOffset, vaaLength - envelopeOffset);
}}

//see WARNING box at the top
function calcSingleHash(Vaa memory vaa) internal pure returns (bytes32) {
Expand Down Expand Up @@ -553,6 +571,21 @@ library VaaLib {
) = decodeVaaBodyMemUnchecked(encoded, envelopeOffset, vaaLength);
}

function decodeVaaStructMemUnchecked(
bytes memory encoded,
uint headerOffset,
uint vaaLength
) internal pure returns (Vaa memory vaa, uint newOffset) {
uint envelopeOffset;
(vaa.header.guardianSetIndex, vaa.header.signatures, envelopeOffset) =
decodeVaaHeaderMemUnchecked(encoded, headerOffset);

uint payloadOffset;
(vaa.envelope, payloadOffset) = decodeVaaEnvelopeStructMemUnchecked(encoded, envelopeOffset);

(vaa.payload, newOffset) = decodeVaaPayloadMemUnchecked(encoded, payloadOffset, vaaLength);
}

function decodeVaaBodyCd(
bytes calldata encodedVaa,
uint envelopeOffset
Expand Down Expand Up @@ -643,7 +676,7 @@ library VaaLib {
payloadOffset = offset;
}

function decodeVaaEnvelopeStructCd(
function decodeVaaEnvelopeStructCdUnchecked(
bytes calldata encodedVaa,
uint envelopeOffset
) internal pure returns (VaaEnvelope memory envelope, uint payloadOffset) {
Expand Down Expand Up @@ -679,7 +712,7 @@ library VaaLib {
payloadOffset = offset;
}

function decodeVaaEnvelopeStructMem(
function decodeVaaEnvelopeStructMemUnchecked(
bytes memory encoded,
uint envelopeOffset
) internal pure returns (VaaEnvelope memory envelope, uint payloadOffset) {
Expand Down Expand Up @@ -808,19 +841,19 @@ library VaaLib {

function decodeVaaPayloadCd(
bytes calldata encodedVaa,
uint offset
uint payloadOffset
) internal pure returns (bytes calldata payload) {
payload = _decodeRemainderCd(encodedVaa, offset);
payload = _decodeRemainderCd(encodedVaa, payloadOffset);
}

function decodeVaaPayloadMemUnchecked(
bytes memory encoded,
uint offset,
uint payloadOffset,
uint vaaLength
) internal pure returns (bytes memory payload, uint newOffset) {
//check to avoid underflow in following subtraction
offset.checkBound(vaaLength);
(payload, newOffset) = encoded.sliceMemUnchecked(offset, vaaLength - offset);
payloadOffset.checkBound(vaaLength);
(payload, newOffset) = encoded.sliceMemUnchecked(payloadOffset, vaaLength - payloadOffset);
}

// ------------ Encoding ------------
Expand Down
19 changes: 14 additions & 5 deletions test/QueryResponse.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ contract QueryResponseTest is Test {
return string(abi.encodePacked(functionName, cd ? "Cd" : "Mem", parameters));
}

function runBoth(function(bool) test) internal {
test(true);
test(false);
}

function _verifyQueryResponseRaw(
bool cd,
bytes memory resp,
Expand Down Expand Up @@ -192,7 +197,7 @@ contract QueryResponseTest is Test {
);
}

function test_calcPrefixedResponseHash(bool cd) public {
function calcPrefixedResponseHash(bool cd) internal {
bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses);

(bool success, bytes memory encodedResult) =
Expand All @@ -205,13 +210,15 @@ contract QueryResponseTest is Test {
bytes32 expectedDigest = 0x5b84b19c68ee0b37899230175a92ee6eda4c5192e8bffca1d057d811bb3660e2;
assertEq(digest, expectedDigest);
}
function test_calcPrefixedResponseHash() public { runBoth(calcPrefixedResponseHash); }

function test_verifyQueryResponse(bool cd) public {
function verifyQueryResponse(bool cd) internal {
bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses);
_verifyQueryResponse(cd, resp, sign(resp));
}
function test_verifyQueryResponse() public { runBoth(verifyQueryResponse); }

function test_decodeAndVerifyQueryResponse(bool cd) public {
function decodeAndVerifyQueryResponse(bool cd) internal {
bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, signature, queryRequestVersion, queryRequestNonce, numPerChainQueries, perChainQueries, numPerChainResponses, perChainResponses);
QueryResponse memory r = _decodeAndVerifyQueryResponse(cd, resp, sign(resp));
assertEq(r.version, 1);
Expand All @@ -224,8 +231,9 @@ contract QueryResponseTest is Test {
assertEq(r.responses[0].request, hex"00000009307832613631616334020d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000406fdde030d500b1d8e8ef31e21c99d1db9a6444d3adf12700000000418160ddd");
assertEq(r.responses[0].response, hex"0000000002a61ac4c1adff9f6e180309e7d0d94c063338ddc61c1c4474cd6957c960efe659534d040005ff312e4f90c002000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d57726170706564204d6174696300000000000000000000000000000000000000000000200000000000000000000000000000000000000000007ae5649beabeddf889364a");
}
function test_decodeAndVerifyQueryResponse() public { runBoth(decodeAndVerifyQueryResponse); }

function test_decodeEthCallQueryResponse() public {
function test_decodeEthCallQueryResponse() internal {
// Take the data extracted by the previous test and break it down even further.
PerChainQueryResponse memory r = PerChainQueryResponse({
chainId: 5,
Expand Down Expand Up @@ -377,10 +385,11 @@ contract QueryResponseTest is Test {

// Start of Solana Stuff ///////////////////////////////////////////////////////////////////////////

function test_verifyQueryResponseForSolana(bool cd) public {
function verifyQueryResponseForSolana(bool cd) internal {
bytes memory resp = concatenateQueryResponseBytesOffChain(version, senderChainId, solanaAccountSignature, solanaAccountQueryRequestVersion, solanaAccountQueryRequestNonce, solanaAccountNumPerChainQueries, solanaAccountPerChainQueries, solanaAccountNumPerChainResponses, solanaAccountPerChainResponses);
_verifyQueryResponse(cd, resp, sign(resp));
}
function test_verifyQueryResponseForSolana() public { runBoth(verifyQueryResponseForSolana); }

function test_decodeSolanaAccountQueryResponse() public {
// Take the data extracted by the previous test and break it down even further.
Expand Down
Loading

0 comments on commit 4718928

Please sign in to comment.