From 3a5f3e37a486f1471dcf95d00c2d63727f10f515 Mon Sep 17 00:00:00 2001 From: PabloMansanet Date: Wed, 29 Jan 2025 19:13:49 +0100 Subject: [PATCH 1/2] Implement allowlist on onramp --- .../programs/ccip-router/src/context.rs | 35 ++++- .../ccip-router/src/instructions/v1/admin.rs | 4 +- .../src/instructions/v1/messages.rs | 2 + .../ccip-router/src/instructions/v1/onramp.rs | 8 +- .../contracts/programs/ccip-router/src/lib.rs | 4 +- .../programs/ccip-router/src/state.rs | 19 +++ .../contracts/target/idl/ccip_router.json | 18 +++ .../contracts/tests/ccip/ccip_router_test.go | 146 ++++++++++++++++++ .../ccip_router/UpdateDestChainConfig.go | 27 +++- chains/solana/gobindings/ccip_router/types.go | 25 +++ 10 files changed, 275 insertions(+), 13 deletions(-) diff --git a/chains/solana/contracts/programs/ccip-router/src/context.rs b/chains/solana/contracts/programs/ccip-router/src/context.rs index 3c89737bc..fb13e7bb1 100644 --- a/chains/solana/contracts/programs/ccip-router/src/context.rs +++ b/chains/solana/contracts/programs/ccip-router/src/context.rs @@ -7,7 +7,7 @@ use solana_program::sysvar::instructions; use crate::program::CcipRouter; use crate::state::{CommitReport, Config, Nonce}; use crate::{ - BillingTokenConfig, BillingTokenConfigWrapper, CcipRouterError, DestChain, + BillingTokenConfig, BillingTokenConfigWrapper, CcipRouterError, DestChain, DestChainConfig, ExecutionReportSingleChain, ExternalExecutionConfig, GlobalState, SVM2AnyMessage, SourceChain, }; @@ -254,11 +254,10 @@ pub struct AcceptOwnership<'info> { } #[derive(Accounts)] -#[instruction(new_chain_selector: u64)] +#[instruction(new_chain_selector: u64, dest_chain_config: DestChainConfig)] pub struct AddChainSelector<'info> { /// Adding a chain selector implies initializing the state for a new chain, /// hence the need to initialize two accounts. - #[account( init, seeds = [seed::SOURCE_CHAIN_STATE, new_chain_selector.to_le_bytes().as_ref()], @@ -273,7 +272,7 @@ pub struct AddChainSelector<'info> { seeds = [seed::DEST_CHAIN_STATE, new_chain_selector.to_le_bytes().as_ref()], bump, payer = authority, - space = ANCHOR_DISCRIMINATOR + DestChain::INIT_SPACE, + space = ANCHOR_DISCRIMINATOR + DestChain::INIT_SPACE + dest_chain_config.dynamic_space(), )] pub dest_chain_state: Account<'info, DestChain>, @@ -312,8 +311,34 @@ pub struct UpdateSourceChainSelectorConfig<'info> { } #[derive(Accounts)] -#[instruction(new_chain_selector: u64)] +#[instruction(new_chain_selector: u64, dest_chain_config: DestChainConfig)] pub struct UpdateDestChainSelectorConfig<'info> { + #[account( + mut, + seeds = [seed::DEST_CHAIN_STATE, new_chain_selector.to_le_bytes().as_ref()], + bump, + constraint = valid_version(dest_chain_state.version, MAX_CHAINSTATE_V) @ CcipRouterError::InvalidInputs, + realloc = ANCHOR_DISCRIMINATOR + DestChain::INIT_SPACE + dest_chain_config.dynamic_space(), + realloc::payer = authority, + realloc::zero = false + )] + pub dest_chain_state: Account<'info, DestChain>, + + #[account( + seeds = [seed::CONFIG], + bump, + constraint = valid_version(config.load()?.version, MAX_CONFIG_V) @ CcipRouterError::InvalidInputs, + )] + pub config: AccountLoader<'info, Config>, + + #[account(mut, address = config.load()?.owner @ CcipRouterError::Unauthorized)] + pub authority: Signer<'info>, + pub system_program: Program<'info, System>, +} + +#[derive(Accounts)] +#[instruction(new_chain_selector: u64)] +pub struct DisableDestChainSelectorConfig<'info> { #[account( mut, seeds = [seed::DEST_CHAIN_STATE, new_chain_selector.to_le_bytes().as_ref()], diff --git a/chains/solana/contracts/programs/ccip-router/src/instructions/v1/admin.rs b/chains/solana/contracts/programs/ccip-router/src/instructions/v1/admin.rs index 3f14e1f50..9abcdeaaf 100644 --- a/chains/solana/contracts/programs/ccip-router/src/instructions/v1/admin.rs +++ b/chains/solana/contracts/programs/ccip-router/src/instructions/v1/admin.rs @@ -1,7 +1,7 @@ use anchor_lang::prelude::*; use anchor_spl::token_interface; -use crate::seed; +use crate::{seed, DisableDestChainSelectorConfig}; use crate::{ AcceptOwnership, AddBillingTokenConfig, AddChainSelector, BillingTokenConfig, CcipRouterError, DestChainAdded, DestChainConfig, DestChainConfigUpdated, DestChainState, FeeTokenAdded, @@ -107,7 +107,7 @@ pub fn disable_source_chain_selector( } pub fn disable_dest_chain_selector( - ctx: Context, + ctx: Context, dest_chain_selector: u64, ) -> Result<()> { let chain_state = &mut ctx.accounts.dest_chain_state; 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 7d07b57ed..600ada468 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 @@ -429,6 +429,8 @@ pub mod ramps { gas_price_staleness_threshold: 90000, enforce_out_of_order: false, chain_family_selector: CHAIN_FAMILY_SELECTOR_EVM.to_be_bytes(), + allowed_senders: vec![], + allow_list_enabled: false, }, } } diff --git a/chains/solana/contracts/programs/ccip-router/src/instructions/v1/onramp.rs b/chains/solana/contracts/programs/ccip-router/src/instructions/v1/onramp.rs index 7f0a0e385..e7a1dd224 100644 --- a/chains/solana/contracts/programs/ccip-router/src/instructions/v1/onramp.rs +++ b/chains/solana/contracts/programs/ccip-router/src/instructions/v1/onramp.rs @@ -67,8 +67,15 @@ pub fn ccip_send<'info>( // The Config Account stores the default values for the Router, the SVM Chain Selector, the Default Gas Limit and the Default Allow Out Of Order Execution and Admin Ownership let config = ctx.accounts.config.load()?; + let sender = ctx.accounts.authority.key.to_owned(); let dest_chain = &mut ctx.accounts.dest_chain_state; + require!( + !dest_chain.config.allow_list_enabled + || dest_chain.config.allowed_senders.contains(&sender), + CcipRouterError::SenderNotAllowed + ); + let mut accounts_per_sent_token: Vec = vec![]; for (i, token_amount) in message.token_amounts.iter().enumerate() { @@ -163,7 +170,6 @@ pub fn ccip_send<'info>( ); dest_chain.state.sequence_number = overflow_add.unwrap(); - let sender = ctx.accounts.authority.key.to_owned(); let receiver = message.receiver.clone(); let source_chain_selector = config.svm_chain_selector; let nonce_counter_account: &mut Account<'info, Nonce> = &mut ctx.accounts.nonce; diff --git a/chains/solana/contracts/programs/ccip-router/src/lib.rs b/chains/solana/contracts/programs/ccip-router/src/lib.rs index a2bca9bbe..2ca374d58 100644 --- a/chains/solana/contracts/programs/ccip-router/src/lib.rs +++ b/chains/solana/contracts/programs/ccip-router/src/lib.rs @@ -178,7 +178,7 @@ pub mod ccip_router { /// * `ctx` - The context containing the accounts required for disabling the chain selector. /// * `dest_chain_selector` - The destination chain selector to be disabled. pub fn disable_dest_chain_selector( - ctx: Context, + ctx: Context, dest_chain_selector: u64, ) -> Result<()> { v1::admin::disable_dest_chain_selector(ctx, dest_chain_selector) @@ -660,4 +660,6 @@ pub enum CcipRouterError { InvalidTokenReceiver, #[msg("Invalid SVM address")] InvalidSVMAddress, + #[msg("Sender not allowed for that destination chain")] + SenderNotAllowed, } diff --git a/chains/solana/contracts/programs/ccip-router/src/state.rs b/chains/solana/contracts/programs/ccip-router/src/state.rs index db528127c..cc5523ac7 100644 --- a/chains/solana/contracts/programs/ccip-router/src/state.rs +++ b/chains/solana/contracts/programs/ccip-router/src/state.rs @@ -114,6 +114,25 @@ pub struct DestChainConfig { pub gas_price_staleness_threshold: u32, // The amount of time a gas price can be stale before it is considered invalid (0 means disabled) pub enforce_out_of_order: bool, // Whether to enforce the allowOutOfOrderExecution extraArg value to be true. pub chain_family_selector: [u8; 4], // Selector that identifies the destination chain's family. Used to determine the correct validations to perform for the dest chain. + + // list of senders authorized to send messages to this destination chain. + // Note: The attribute name `max_len` is slightly misleading: it is not in any + // way limiting the actual length of the vector during initialization; it just + // helps the InitSpace derive macro work out the initial space. We can leave it at + // zero and calculate the actual length in the instruction context. + #[max_len(0)] + pub allowed_senders: Vec, + pub allow_list_enabled: bool, +} + +impl DestChainConfig { + pub fn space(&self) -> usize { + Self::INIT_SPACE + self.dynamic_space() + } + + pub fn dynamic_space(&self) -> usize { + self.allowed_senders.len() * std::mem::size_of::() + } } #[account] diff --git a/chains/solana/contracts/target/idl/ccip_router.json b/chains/solana/contracts/target/idl/ccip_router.json index 7dc857e1c..3ee22da18 100644 --- a/chains/solana/contracts/target/idl/ccip_router.json +++ b/chains/solana/contracts/target/idl/ccip_router.json @@ -400,6 +400,11 @@ "name": "authority", "isMut": true, "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false } ], "args": [ @@ -2486,6 +2491,16 @@ 4 ] } + }, + { + "name": "allowedSenders", + "type": { + "vec": "publicKey" + } + }, + { + "name": "allowListEnabled", + "type": "bool" } ] } @@ -2773,6 +2788,9 @@ }, { "name": "InvalidSVMAddress" + }, + { + "name": "SenderNotAllowed" } ] } diff --git a/chains/solana/contracts/tests/ccip/ccip_router_test.go b/chains/solana/contracts/tests/ccip/ccip_router_test.go index 09f951c9e..4c0598dae 100644 --- a/chains/solana/contracts/tests/ccip/ccip_router_test.go +++ b/chains/solana/contracts/tests/ccip/ccip_router_test.go @@ -673,6 +673,7 @@ func TestCCIPRouter(t *testing.T) { destChainStatePDA, config.RouterConfigPDA, admin.PublicKey(), + solana.SystemProgramID, ).ValidateAndBuild() require.NoError(t, err) result := testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment, []string{"Error Code: " + ccip_router.InvalidInputs_CcipRouterError.String()}) @@ -702,6 +703,7 @@ func TestCCIPRouter(t *testing.T) { config.EvmDestChainStatePDA, config.RouterConfigPDA, user.PublicKey(), // unauthorized + solana.SystemProgramID, ).ValidateAndBuild() require.NoError(t, err) result := testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: " + ccip_router.Unauthorized_CcipRouterError.String()}) @@ -751,6 +753,7 @@ func TestCCIPRouter(t *testing.T) { config.EvmDestChainStatePDA, config.RouterConfigPDA, admin.PublicKey(), + solana.SystemProgramID, ).ValidateAndBuild() require.NoError(t, err) result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) @@ -2092,6 +2095,72 @@ func TestCCIPRouter(t *testing.T) { require.NotNil(t, result) }) + t.Run("When sending with an empty but enabled allowlist, it fails", func(t *testing.T) { + var initialDestChain ccip_router.DestChain + err := common.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmDestChainStatePDA, config.DefaultCommitment, &initialDestChain) + require.NoError(t, err, "failed to get account info") + modifiedDestChain := initialDestChain + modifiedDestChain.Config.AllowListEnabled = true + + updateDestChainIx, err := ccip_router.NewUpdateDestChainConfigInstruction( + config.EvmChainSelector, + modifiedDestChain.Config, + config.EvmDestChainStatePDA, + config.RouterConfigPDA, + anotherAdmin.PublicKey(), + solana.SystemProgramID, + ).ValidateAndBuild() + require.NoError(t, err) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{updateDestChainIx}, anotherAdmin, config.DefaultCommitment) + require.NotNil(t, result) + + destinationChainSelector := config.EvmChainSelector + destinationChainStatePDA := config.EvmDestChainStatePDA + message := ccip_router.SVM2AnyMessage{ + FeeToken: wsol.mint, + Receiver: validReceiverAddress[:], + Data: []byte{4, 5, 6}, + ExtraArgs: emptyEVMExtraArgsV2, + } + + raw := ccip_router.NewCcipSendInstruction( + destinationChainSelector, + message, + []byte{}, + config.RouterConfigPDA, + destinationChainStatePDA, + nonceEvmPDA, + user.PublicKey(), + solana.SystemProgramID, + wsol.program, + wsol.mint, + wsol.billingConfigPDA, + token2022.billingConfigPDA, + wsol.userATA, + wsol.billingATA, + config.BillingSignerPDA, + config.ExternalTokenPoolsSignerPDA, + ) + raw.GetFeeTokenUserAssociatedAccountAccount().WRITE() + instruction, err := raw.ValidateAndBuild() + require.NoError(t, err) + result = testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: SenderNotAllowed"}) + require.NotNil(t, result) + + // We now restore the config to keep the test state-neutral + updateDestChainIx, err = ccip_router.NewUpdateDestChainConfigInstruction( + config.EvmChainSelector, + initialDestChain.Config, + config.EvmDestChainStatePDA, + config.RouterConfigPDA, + anotherAdmin.PublicKey(), + solana.SystemProgramID, + ).ValidateAndBuild() + require.NoError(t, err) + result = testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{updateDestChainIx}, anotherAdmin, config.DefaultCommitment) + require.NotNil(t, result) + }) + t.Run("When sending a Valid CCIP Message Emits CCIPMessageSent", func(t *testing.T) { destinationChainSelector := config.EvmChainSelector destinationChainStatePDA := config.EvmDestChainStatePDA @@ -2851,6 +2920,83 @@ func TestCCIPRouter(t *testing.T) { }) }) + t.Run("When sending with an enabled allowlist including the sender, it succeeds", func(t *testing.T) { + var initialDestChain ccip_router.DestChain + err := common.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmDestChainStatePDA, config.DefaultCommitment, &initialDestChain) + require.NoError(t, err, "failed to get account info") + modifiedDestChain := initialDestChain + modifiedDestChain.Config.AllowListEnabled = true + modifiedDestChain.Config.AllowedSenders = []solana.PublicKey{ + user.PublicKey(), + anotherUser.PublicKey(), + } + + updateDestChainIx, err := ccip_router.NewUpdateDestChainConfigInstruction( + config.EvmChainSelector, + modifiedDestChain.Config, + config.EvmDestChainStatePDA, + config.RouterConfigPDA, + anotherAdmin.PublicKey(), + solana.SystemProgramID, + ).ValidateAndBuild() + require.NoError(t, err) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{updateDestChainIx}, anotherAdmin, config.DefaultCommitment) + require.NotNil(t, result) + + var parsedDestChain ccip_router.DestChain + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmDestChainStatePDA, config.DefaultCommitment, &parsedDestChain) + require.NoError(t, err, "failed to get account info") + + // This proves we're able to update the config with a dynamically sized element + require.Equal(t, parsedDestChain.Config.AllowedSenders, modifiedDestChain.Config.AllowedSenders) + + destinationChainSelector := config.EvmChainSelector + destinationChainStatePDA := config.EvmDestChainStatePDA + message := ccip_router.SVM2AnyMessage{ + FeeToken: wsol.mint, + Receiver: validReceiverAddress[:], + Data: []byte{4, 5, 6}, + ExtraArgs: emptyEVMExtraArgsV2, + } + + raw := ccip_router.NewCcipSendInstruction( + destinationChainSelector, + message, + []byte{}, + config.RouterConfigPDA, + destinationChainStatePDA, + nonceEvmPDA, + user.PublicKey(), + solana.SystemProgramID, + wsol.program, + wsol.mint, + wsol.billingConfigPDA, + token2022.billingConfigPDA, + wsol.userATA, + wsol.billingATA, + config.BillingSignerPDA, + config.ExternalTokenPoolsSignerPDA, + ) + raw.GetFeeTokenUserAssociatedAccountAccount().WRITE() + instruction, err := raw.ValidateAndBuild() + require.NoError(t, err) + result = testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment) + require.NotNil(t, result) + + // We now restore the config to keep the test state-neutral + updateDestChainIx, err = ccip_router.NewUpdateDestChainConfigInstruction( + config.EvmChainSelector, + initialDestChain.Config, + config.EvmDestChainStatePDA, + config.RouterConfigPDA, + anotherAdmin.PublicKey(), + solana.SystemProgramID, + ).ValidateAndBuild() + require.NoError(t, err) + result = testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{updateDestChainIx}, anotherAdmin, config.DefaultCommitment) + require.NotNil(t, result) + }) + t.Run("token pool accounts: validation", func(t *testing.T) { t.Parallel() // base transaction diff --git a/chains/solana/gobindings/ccip_router/UpdateDestChainConfig.go b/chains/solana/gobindings/ccip_router/UpdateDestChainConfig.go index 1958a4e72..c5ec2e48f 100644 --- a/chains/solana/gobindings/ccip_router/UpdateDestChainConfig.go +++ b/chains/solana/gobindings/ccip_router/UpdateDestChainConfig.go @@ -28,13 +28,15 @@ type UpdateDestChainConfig struct { // [1] = [] config // // [2] = [WRITE, SIGNER] authority + // + // [3] = [] systemProgram ag_solanago.AccountMetaSlice `bin:"-" borsh_skip:"true"` } // NewUpdateDestChainConfigInstructionBuilder creates a new `UpdateDestChainConfig` instruction builder. func NewUpdateDestChainConfigInstructionBuilder() *UpdateDestChainConfig { nd := &UpdateDestChainConfig{ - AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 3), + AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 4), } return nd } @@ -84,6 +86,17 @@ func (inst *UpdateDestChainConfig) GetAuthorityAccount() *ag_solanago.AccountMet return inst.AccountMetaSlice[2] } +// SetSystemProgramAccount sets the "systemProgram" account. +func (inst *UpdateDestChainConfig) SetSystemProgramAccount(systemProgram ag_solanago.PublicKey) *UpdateDestChainConfig { + inst.AccountMetaSlice[3] = ag_solanago.Meta(systemProgram) + return inst +} + +// GetSystemProgramAccount gets the "systemProgram" account. +func (inst *UpdateDestChainConfig) GetSystemProgramAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[3] +} + func (inst UpdateDestChainConfig) Build() *Instruction { return &Instruction{BaseVariant: ag_binary.BaseVariant{ Impl: inst, @@ -123,6 +136,9 @@ func (inst *UpdateDestChainConfig) Validate() error { if inst.AccountMetaSlice[2] == nil { return errors.New("accounts.Authority is not set") } + if inst.AccountMetaSlice[3] == nil { + return errors.New("accounts.SystemProgram is not set") + } } return nil } @@ -142,10 +158,11 @@ func (inst *UpdateDestChainConfig) EncodeToTree(parent ag_treeout.Branches) { }) // Accounts of the instruction: - instructionBranch.Child("Accounts[len=3]").ParentFunc(func(accountsBranch ag_treeout.Branches) { + instructionBranch.Child("Accounts[len=4]").ParentFunc(func(accountsBranch ag_treeout.Branches) { accountsBranch.Child(ag_format.Meta("destChainState", inst.AccountMetaSlice[0])) accountsBranch.Child(ag_format.Meta(" config", inst.AccountMetaSlice[1])) accountsBranch.Child(ag_format.Meta(" authority", inst.AccountMetaSlice[2])) + accountsBranch.Child(ag_format.Meta(" systemProgram", inst.AccountMetaSlice[3])) }) }) }) @@ -186,11 +203,13 @@ func NewUpdateDestChainConfigInstruction( // Accounts: destChainState ag_solanago.PublicKey, config ag_solanago.PublicKey, - authority ag_solanago.PublicKey) *UpdateDestChainConfig { + authority ag_solanago.PublicKey, + systemProgram ag_solanago.PublicKey) *UpdateDestChainConfig { return NewUpdateDestChainConfigInstructionBuilder(). SetDestChainSelector(destChainSelector). SetDestChainConfig(destChainConfig). SetDestChainStateAccount(destChainState). SetConfigAccount(config). - SetAuthorityAccount(authority) + SetAuthorityAccount(authority). + SetSystemProgramAccount(systemProgram) } diff --git a/chains/solana/gobindings/ccip_router/types.go b/chains/solana/gobindings/ccip_router/types.go index f8a2a6162..a95a0bb90 100644 --- a/chains/solana/gobindings/ccip_router/types.go +++ b/chains/solana/gobindings/ccip_router/types.go @@ -1149,6 +1149,8 @@ type DestChainConfig struct { GasPriceStalenessThreshold uint32 EnforceOutOfOrder bool ChainFamilySelector [4]uint8 + AllowedSenders []ag_solanago.PublicKey + AllowListEnabled bool } func (obj DestChainConfig) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { @@ -1247,6 +1249,16 @@ func (obj DestChainConfig) MarshalWithEncoder(encoder *ag_binary.Encoder) (err e if err != nil { return err } + // Serialize `AllowedSenders` param: + err = encoder.Encode(obj.AllowedSenders) + if err != nil { + return err + } + // Serialize `AllowListEnabled` param: + err = encoder.Encode(obj.AllowListEnabled) + if err != nil { + return err + } return nil } @@ -1346,6 +1358,16 @@ func (obj *DestChainConfig) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (er if err != nil { return err } + // Deserialize `AllowedSenders`: + err = decoder.Decode(&obj.AllowedSenders) + if err != nil { + return err + } + // Deserialize `AllowListEnabled`: + err = decoder.Decode(&obj.AllowListEnabled) + if err != nil { + return err + } return nil } @@ -1671,6 +1693,7 @@ const ( InvalidChainFamilySelector_CcipRouterError InvalidTokenReceiver_CcipRouterError InvalidSVMAddress_CcipRouterError + SenderNotAllowed_CcipRouterError ) func (value CcipRouterError) String() string { @@ -1767,6 +1790,8 @@ func (value CcipRouterError) String() string { return "InvalidTokenReceiver" case InvalidSVMAddress_CcipRouterError: return "InvalidSVMAddress" + case SenderNotAllowed_CcipRouterError: + return "SenderNotAllowed" default: return "" } From e4d4172e9e8818e09fd9f75e318d466ef78ff912 Mon Sep 17 00:00:00 2001 From: PabloMansanet Date: Fri, 31 Jan 2025 16:32:27 +0100 Subject: [PATCH 2/2] Add clarifying comment --- chains/solana/contracts/programs/ccip-router/src/context.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/chains/solana/contracts/programs/ccip-router/src/context.rs b/chains/solana/contracts/programs/ccip-router/src/context.rs index fb13e7bb1..141683ff9 100644 --- a/chains/solana/contracts/programs/ccip-router/src/context.rs +++ b/chains/solana/contracts/programs/ccip-router/src/context.rs @@ -320,6 +320,9 @@ pub struct UpdateDestChainSelectorConfig<'info> { constraint = valid_version(dest_chain_state.version, MAX_CHAINSTATE_V) @ CcipRouterError::InvalidInputs, realloc = ANCHOR_DISCRIMINATOR + DestChain::INIT_SPACE + dest_chain_config.dynamic_space(), realloc::payer = authority, + // `realloc::zero = true` is only necessary in cases where an instruction is capable of reallocating + // *down* and then *up*, during a single execution. In any other cases (such as this), it's not + // necessary as the memory will be zero'd automatically on instruction entry. realloc::zero = false )] pub dest_chain_state: Account<'info, DestChain>,