From 6ff89e07eafd4ac83af199fbefbc0e07ab7312bf Mon Sep 17 00:00:00 2001 From: aalu1418 <50029043+aalu1418@users.noreply.github.com> Date: Tue, 21 Jan 2025 15:49:33 -0700 Subject: [PATCH] solana: validate evm extra args --- .../src/instructions/v1/messages.rs | 14 ++++ .../contracts/programs/ccip-router/src/lib.rs | 4 + .../contracts/target/idl/ccip_router.json | 6 ++ .../contracts/tests/ccip/ccip_router_test.go | 79 ++++++++++++++++++- chains/solana/gobindings/ccip_router/types.go | 6 ++ 5 files changed, 108 insertions(+), 1 deletion(-) diff --git a/chains/solana/contracts/programs/ccip-router/src/instructions/v1/messages.rs b/chains/solana/contracts/programs/ccip-router/src/instructions/v1/messages.rs index 462455cb4..19b14546b 100644 --- a/chains/solana/contracts/programs/ccip-router/src/instructions/v1/messages.rs +++ b/chains/solana/contracts/programs/ccip-router/src/instructions/v1/messages.rs @@ -120,6 +120,20 @@ pub mod ramps { CcipRouterError::UnsupportedNumberOfTokens ); + require_gte!( + dest_chain.config.max_per_msg_gas_limit as u128, + msg.extra_args + .gas_limit + .unwrap_or(dest_chain.config.default_tx_gas_limit as u128), + CcipRouterError::MessageGasLimitTooHigh, + ); + + require!( + !dest_chain.config.enforce_out_of_order + || msg.extra_args.allow_out_of_order_execution.unwrap_or(false), + CcipRouterError::ExtraArgOutOfOrderExecutionMustBeTrue, + ); + validate_dest_family_address(msg, dest_chain.config.chain_family_selector) } diff --git a/chains/solana/contracts/programs/ccip-router/src/lib.rs b/chains/solana/contracts/programs/ccip-router/src/lib.rs index 8088b1623..f231a4d26 100644 --- a/chains/solana/contracts/programs/ccip-router/src/lib.rs +++ b/chains/solana/contracts/programs/ccip-router/src/lib.rs @@ -683,4 +683,8 @@ pub enum CcipRouterError { MessageFeeTooHigh, #[msg("Source token data is too large")] SourceTokenDataTooLarge, + #[msg("Message gas limit too high")] + MessageGasLimitTooHigh, + #[msg("Extra arg out of order execution must be true")] + ExtraArgOutOfOrderExecutionMustBeTrue, } diff --git a/chains/solana/contracts/target/idl/ccip_router.json b/chains/solana/contracts/target/idl/ccip_router.json index ad06a4792..264023c9c 100644 --- a/chains/solana/contracts/target/idl/ccip_router.json +++ b/chains/solana/contracts/target/idl/ccip_router.json @@ -2773,6 +2773,12 @@ }, { "name": "SourceTokenDataTooLarge" + }, + { + "name": "MessageGasLimitTooHigh" + }, + { + "name": "ExtraArgOutOfOrderExecutionMustBeTrue" } ] } diff --git a/chains/solana/contracts/tests/ccip/ccip_router_test.go b/chains/solana/contracts/tests/ccip/ccip_router_test.go index 788ced2ec..bf9d3c6f4 100644 --- a/chains/solana/contracts/tests/ccip/ccip_router_test.go +++ b/chains/solana/contracts/tests/ccip/ccip_router_test.go @@ -56,6 +56,7 @@ func TestCCIPRouter(t *testing.T) { require.NoError(t, gerr) var nonceEvmPDA solana.PublicKey + var nonceSvmPDA solana.PublicKey // billing type AccountsPerToken struct { @@ -393,6 +394,8 @@ func TestCCIPRouter(t *testing.T) { nonceEvmPDA, err = ccip.GetNoncePDA(config.EvmChainSelector, user.PublicKey()) require.NoError(t, err) + nonceSvmPDA, err = ccip.GetNoncePDA(config.SVMChainSelector, user.PublicKey()) + require.NoError(t, err) }) t.Run("When admin updates the default gas limit it's updated", func(t *testing.T) { @@ -586,7 +589,9 @@ func TestCCIPRouter(t *testing.T) { // minimal valid config DefaultTxGasLimit: 1, MaxPerMsgGasLimit: 100, - ChainFamilySelector: [4]uint8{3, 2, 1, 0}}, + ChainFamilySelector: [4]uint8{3, 2, 1, 0}, + EnforceOutOfOrder: true, + }, config.SVMSourceChainStatePDA, config.SVMDestChainStatePDA, config.RouterConfigPDA, @@ -2374,6 +2379,78 @@ func TestCCIPRouter(t *testing.T) { require.Equal(t, uint64(3), ccipMessageSentEvent.Message.Header.Nonce) }) + t.Run("When gasLimit is too high, it fails", func(t *testing.T) { + destinationChainSelector := config.EvmChainSelector + destinationChainStatePDA := config.EvmDestChainStatePDA + message := ccip_router.SVM2AnyMessage{ + FeeToken: token2022.mint, + Receiver: validReceiverAddress[:], + Data: []byte{4, 5, 6}, + ExtraArgs: ccip_router.ExtraArgsInput{ + GasLimit: &bin.Uint128{Lo: 0, Hi: 1_000_000_000}, + }, + } + + raw := ccip_router.NewCcipSendInstruction( + destinationChainSelector, + message, + []byte{}, + config.RouterConfigPDA, + destinationChainStatePDA, + nonceEvmPDA, + user.PublicKey(), + solana.SystemProgramID, + token2022.program, + token2022.mint, + token2022.billingConfigPDA, + token2022.billingConfigPDA, + token2022.userATA, + token2022.billingATA, + config.BillingSignerPDA, + config.ExternalTokenPoolsSignerPDA, + ) + raw.GetFeeTokenUserAssociatedAccountAccount().WRITE() + instruction, err := raw.ValidateAndBuild() + require.NoError(t, err) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{ccip_router.MessageGasLimitTooHigh_CcipRouterError.String()}) + }) + + t.Run("When out of order execution is enforced, it fails when not enabled", func(t *testing.T) { + destinationChainSelector := config.SVMChainSelector // SVM dest chain requires out of order execution + destinationChainStatePDA := config.SVMDestChainStatePDA + falseVal := false + message := ccip_router.SVM2AnyMessage{ + FeeToken: token2022.mint, + Receiver: validReceiverAddress[:], + ExtraArgs: ccip_router.ExtraArgsInput{ + AllowOutOfOrderExecution: &falseVal, + }, + } + + raw := ccip_router.NewCcipSendInstruction( + destinationChainSelector, + message, + []byte{}, + config.RouterConfigPDA, + destinationChainStatePDA, + nonceSvmPDA, + user.PublicKey(), + solana.SystemProgramID, + token2022.program, + token2022.mint, + token2022.billingConfigPDA, + token2022.billingConfigPDA, + token2022.userATA, + token2022.billingATA, + config.BillingSignerPDA, + config.ExternalTokenPoolsSignerPDA, + ) + raw.GetFeeTokenUserAssociatedAccountAccount().WRITE() + instruction, err := raw.ValidateAndBuild() + require.NoError(t, err) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{ccip_router.ExtraArgOutOfOrderExecutionMustBeTrue_CcipRouterError.String()}) + }) + t.Run("When sending a message with an invalid nonce account, it fails", func(t *testing.T) { destinationChainSelector := config.EvmChainSelector destinationChainStatePDA := config.EvmDestChainStatePDA diff --git a/chains/solana/gobindings/ccip_router/types.go b/chains/solana/gobindings/ccip_router/types.go index 7eabdc6be..2db3fcca5 100644 --- a/chains/solana/gobindings/ccip_router/types.go +++ b/chains/solana/gobindings/ccip_router/types.go @@ -1677,6 +1677,8 @@ const ( InvalidInputsMissingTokenConfig_CcipRouterError MessageFeeTooHigh_CcipRouterError SourceTokenDataTooLarge_CcipRouterError + MessageGasLimitTooHigh_CcipRouterError + ExtraArgOutOfOrderExecutionMustBeTrue_CcipRouterError ) func (value CcipRouterError) String() string { @@ -1759,6 +1761,10 @@ func (value CcipRouterError) String() string { return "MessageFeeTooHigh" case SourceTokenDataTooLarge_CcipRouterError: return "SourceTokenDataTooLarge" + case MessageGasLimitTooHigh_CcipRouterError: + return "MessageGasLimitTooHigh" + case ExtraArgOutOfOrderExecutionMustBeTrue_CcipRouterError: + return "ExtraArgOutOfOrderExecutionMustBeTrue" default: return "" }