diff --git a/Justfile b/Justfile index c1074bbe..0d036641 100644 --- a/Justfile +++ b/Justfile @@ -48,3 +48,13 @@ test-coverage coverage_file="coverage.out": # coverage_file := env_var_or_default('COVERAGE_FILE', 'coverage.out') go test -v -race -fullpath -shuffle on -v -coverprofile={{coverage_file}} ./... +bump-chainlink-ccip sha: + @echo "Bumping chainlink-ccip dependencies in root..." + go get github.com/smartcontractkit/chainlink-ccip@{{sha}} + go get github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment@{{sha}} + go get github.com/smartcontractkit/chainlink-ccip/deployment@{{sha}} + + @echo "Bumping chainlink-ccip dependencies in build/devenv..." + (cd build/devenv && go get github.com/smartcontractkit/chainlink-ccip@{{sha}} && go get github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment@{{sha}} && go get github.com/smartcontractkit/chainlink-ccip/deployment@{{sha}}) + + @just tidy diff --git a/aggregator/aggregator.toml b/aggregator/aggregator.toml index 98307dbf..c9d1d51c 100644 --- a/aggregator/aggregator.toml +++ b/aggregator/aggregator.toml @@ -100,11 +100,15 @@ pyroscope_url = "http://pyroscope:4040" addresses = ["0x099125558781da4bcdb16e457e15d997ecac68a8"] [committees.default.quorumConfigs.4793464827907405086] committeeVerifierAddress = "0x9A9f2CCfdE556A7E9Ff0848998Aa4a0CFD8863AE" - threshold = 1 + threshold = 2 [[committees.default.quorumConfigs.4793464827907405086.signers]] participantID = "participant1" addresses = ["0x6b3131d871c63c7fa592863e173cba2da5ffa68b"] + + [[committees.default.quorumConfigs.4793464827907405086.signers]] + participantID = "participant2" + addresses = ["0x099125558781da4bcdb16e457e15d997ecac68a8"] [committees.default.sourceVerifierAddresses] 12922642891491394802 = "0x9A9f2CCfdE556A7E9Ff0848998Aa4a0CFD8863AE" 3379446385462418246 = "0x9A9f2CCfdE556A7E9Ff0848998Aa4a0CFD8863AE" diff --git a/build/devenv/cmd/ccv/ccv.go b/build/devenv/cmd/ccv/ccv.go index 08eee1ac..6f1a075d 100644 --- a/build/devenv/cmd/ccv/ccv.go +++ b/build/devenv/cmd/ccv/ccv.go @@ -604,7 +604,7 @@ var sendCmd = &cobra.Command{ Executor: protocol.UnknownAddress(common.HexToAddress(executorRef.Address).Bytes()), // executor address ExecutorArgs: nil, TokenArgs: nil, - MandatoryCCVs: []protocol.CCV{ + CCVs: []protocol.CCV{ { CCVAddress: common.HexToAddress(committeeVerifierProxyRef.Address).Bytes(), Args: []byte{}, diff --git a/build/devenv/environment.go b/build/devenv/environment.go index 78d4beb3..6c957bb4 100644 --- a/build/devenv/environment.go +++ b/build/devenv/environment.go @@ -227,7 +227,7 @@ func NewEnvironment() (*Cfg, error) { for i, ver := range in.Verifier { ver.ConfigFilePath = fmt.Sprintf("/app/cmd/verifier/verifier-%d.toml", i+1) - ver.SigningKey = fmt.Sprintf("dev-private-key%d-12345678901234567890", i) + ver.SigningKey = cciptestinterfaces.XXXNewVerifierPrivateKey(i) _, err = services.NewVerifier(ver) if err != nil { return nil, fmt.Errorf("failed to create verifier service: %w", err) diff --git a/build/devenv/go.mod b/build/devenv/go.mod index 300dbd97..7bcf3521 100644 --- a/build/devenv/go.mod +++ b/build/devenv/go.mod @@ -16,9 +16,9 @@ require ( github.com/prometheus/client_golang v1.23.0 github.com/rs/zerolog v1.34.0 github.com/smartcontractkit/chain-selectors v1.0.72 - github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20251017065736-83976da2ec64 - github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment v0.0.0-20251017065736-83976da2ec64 - github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20251017065736-83976da2ec64 // indirect + github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20251017174358-1a48fa8a246b + github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment v0.0.0-20251017174358-1a48fa8a246b + github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20251017174358-1a48fa8a246b // indirect github.com/smartcontractkit/chainlink-common v0.9.6-0.20250929154511-1f5fbda7ae76 github.com/smartcontractkit/chainlink-deployments-framework v0.55.1 github.com/smartcontractkit/chainlink-testing-framework/framework v0.10.33 diff --git a/build/devenv/go.sum b/build/devenv/go.sum index 5487ecce..066d7b8e 100644 --- a/build/devenv/go.sum +++ b/build/devenv/go.sum @@ -1034,16 +1034,16 @@ github.com/smartcontractkit/chain-selectors v1.0.72 h1:AExF2H3mABdLCN0QZd+IjU8Ck github.com/smartcontractkit/chain-selectors v1.0.72/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= github.com/smartcontractkit/chainlink-aptos v0.0.0-20250915164817-46a35eda083d h1:bcfnHPXAhrhUw95X60Y/lDhQAb4SxSyTrqyVCHqfXPI= github.com/smartcontractkit/chainlink-aptos v0.0.0-20250915164817-46a35eda083d/go.mod h1:tEjqontct1/5cKHm4q75nopZa1rwzaQZwd9U9wn0uZE= -github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20251017065736-83976da2ec64 h1:qHUX9Y8i4b/TBO2ILZCkeKf9G1AO1MmnmTpTTbictAI= -github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20251017065736-83976da2ec64/go.mod h1:W3d6TbZ4PNLGb8QOK8URc/tVWBhnAOwtAYsQ2iPgwtw= -github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment v0.0.0-20251017065736-83976da2ec64 h1:h9NmYo0YPySduRclcUZc1ba39nEEPyabwtelexsVj8Y= -github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment v0.0.0-20251017065736-83976da2ec64/go.mod h1:LvEaw/iOLs6UQRCqr7zyxhxDopd+KPp53GE77N8TugE= +github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20251017174358-1a48fa8a246b h1:tHOvMBWdYxQ/ovH10iUvN3XHmjg2lPPWOXKY99mSaD4= +github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20251017174358-1a48fa8a246b/go.mod h1:W3d6TbZ4PNLGb8QOK8URc/tVWBhnAOwtAYsQ2iPgwtw= +github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment v0.0.0-20251017174358-1a48fa8a246b h1:08jEs6dSBihScGcLiBPoJu4NiMwGTaiL8vSWqXFqT6Q= +github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment v0.0.0-20251017174358-1a48fa8a246b/go.mod h1:LvEaw/iOLs6UQRCqr7zyxhxDopd+KPp53GE77N8TugE= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250908144012-8184001834b5 h1:GmJQqNrWn5pNc8YTei6l2TOSYjK2fRd4+edFZIifCrU= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250908144012-8184001834b5/go.mod h1:Ve1xD71bl193YIZQEoJMmBqLGQJdNs29bwbuObwvbhQ= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250908144012-8184001834b5 h1:QhcYGEhRLInr1/qh/3RJiVdvJ0nxBHKhPe65WLbSBnU= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250908144012-8184001834b5/go.mod h1:xtZNi6pOKdC3sLvokDvXOhgHzT+cyBqH/gWwvxTxqrg= -github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20251017065736-83976da2ec64 h1:0EtIB0DChFUETLlpBHKm/VfGvgOEgaemr3G2mPhoRUY= -github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20251017065736-83976da2ec64/go.mod h1:oDe4huAcmrsakLGZxxGe+Y03cQ+sapxM0gZrTe20iLY= +github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20251017174358-1a48fa8a246b h1:koEaLC+3bStmauerlqJcvNlCUhpGwR6tRobb4qc7gdo= +github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20251017174358-1a48fa8a246b/go.mod h1:oDe4huAcmrsakLGZxxGe+Y03cQ+sapxM0gZrTe20iLY= github.com/smartcontractkit/chainlink-common v0.9.6-0.20250929154511-1f5fbda7ae76 h1:Slnws8RoXRUYGgEMYK6X2yYzjZwNgVb93PxU45VEObQ= github.com/smartcontractkit/chainlink-common v0.9.6-0.20250929154511-1f5fbda7ae76/go.mod h1:1r3aM96KHAESfnayJ3BTHCkP1qJS1BEG1r4czeoaXlA= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.4 h1:hvqATtrZ0iMRTI80cpBot/3JFbjz2j+2tvpfooVhRHw= diff --git a/build/devenv/tests/e2e/load_test.go b/build/devenv/tests/e2e/load_test.go index a54d3153..ffc2e9e9 100644 --- a/build/devenv/tests/e2e/load_test.go +++ b/build/devenv/tests/e2e/load_test.go @@ -190,14 +190,13 @@ func (m *EVMTXGun) Call(_ *wasp.Generator) *wasp.Response { }, cciptestinterfaces.MessageOptions{ Version: 3, FinalityConfig: uint16(1), - MandatoryCCVs: []protocol.CCV{ + CCVs: []protocol.CCV{ { CCVAddress: common.HexToAddress(committeeVerifierProxyRef.Address).Bytes(), Args: []byte{}, ArgsLen: 0, }, }, - OptionalThreshold: 0, }) if err != nil { return &wasp.Response{Error: fmt.Errorf("failed to send message: %w", err).Error(), Failed: true} diff --git a/build/devenv/tests/e2e/smoke_test.go b/build/devenv/tests/e2e/smoke_test.go index 39a15584..9f786af1 100644 --- a/build/devenv/tests/e2e/smoke_test.go +++ b/build/devenv/tests/e2e/smoke_test.go @@ -113,9 +113,17 @@ func TestE2ESmoke(t *testing.T) { require.NotNil(t, e) if tc.expectFail { - require.Equal(t, MessageExecutionStateFailed, e.(*offramp.OffRampExecutionStateChanged).State) + require.Equalf(t, + MessageExecutionStateFailed, + e.(*offramp.OffRampExecutionStateChanged).State, + "unexpected state, return data: %x", + e.(*offramp.OffRampExecutionStateChanged).ReturnData) } else { - require.Equal(t, MessageExecutionStateSuccess, e.(*offramp.OffRampExecutionStateChanged).State) + require.Equalf(t, + MessageExecutionStateSuccess, + e.(*offramp.OffRampExecutionStateChanged).State, + "unexpected state, return data: %x", + e.(*offramp.OffRampExecutionStateChanged).ReturnData) } }) } @@ -133,9 +141,7 @@ func TestE2ESmoke(t *testing.T) { dstSelector uint64 finality uint16 receiver protocol.UnknownAddress - mandatoryCCVs []protocol.CCV - optionalCCVs []protocol.CCV - threshold uint8 + ccvs []protocol.CCV expectFail bool tokenTransfer *tokenTransfer } @@ -147,7 +153,7 @@ func TestE2ESmoke(t *testing.T) { dstSelector: selectors[1], finality: 1, receiver: mustGetEOAReceiverAddress(t, c, selectors[1]), - mandatoryCCVs: []protocol.CCV{ + ccvs: []protocol.CCV{ { CCVAddress: getContractAddress(t, in, selectors[0], datastore.ContractType(committee_verifier.ProxyType), committee_verifier.Deploy.Version(), ccvEvm.DefaultCommitteeVerifierQualifier, "committee verifier proxy"), Args: []byte{}, @@ -161,7 +167,7 @@ func TestE2ESmoke(t *testing.T) { dstSelector: selectors[0], finality: 1, receiver: mustGetEOAReceiverAddress(t, c, selectors[0]), - mandatoryCCVs: []protocol.CCV{ + ccvs: []protocol.CCV{ { CCVAddress: getContractAddress(t, in, selectors[1], datastore.ContractType(committee_verifier.ProxyType), committee_verifier.Deploy.Version(), ccvEvm.DefaultCommitteeVerifierQualifier, "committee verifier proxy"), Args: []byte{}, @@ -175,7 +181,7 @@ func TestE2ESmoke(t *testing.T) { dstSelector: selectors[2], finality: 1, receiver: mustGetEOAReceiverAddress(t, c, selectors[2]), - mandatoryCCVs: []protocol.CCV{ + ccvs: []protocol.CCV{ { CCVAddress: getContractAddress(t, in, selectors[0], datastore.ContractType(committee_verifier.ProxyType), committee_verifier.Deploy.Version(), ccvEvm.DefaultCommitteeVerifierQualifier, "committee verifier proxy"), Args: []byte{}, @@ -190,7 +196,7 @@ func TestE2ESmoke(t *testing.T) { dstSelector: selectors[1], finality: 1, receiver: getContractAddress(t, in, selectors[1], datastore.ContractType(mock_receiver.ContractType), mock_receiver.Deploy.Version(), ccvEvm.DefaultReceiverQualifier, "mock receiver"), - mandatoryCCVs: []protocol.CCV{ + ccvs: []protocol.CCV{ { CCVAddress: getContractAddress(t, in, selectors[1], datastore.ContractType(committee_verifier.ProxyType), committee_verifier.Deploy.Version(), ccvEvm.DefaultCommitteeVerifierQualifier, "committee verifier proxy"), Args: []byte{}, @@ -207,7 +213,7 @@ func TestE2ESmoke(t *testing.T) { dstSelector: selectors[0], finality: 1, receiver: getContractAddress(t, in, selectors[0], datastore.ContractType(mock_receiver.ContractType), mock_receiver.Deploy.Version(), ccvEvm.DefaultReceiverQualifier, "mock receiver"), - mandatoryCCVs: []protocol.CCV{ + ccvs: []protocol.CCV{ { CCVAddress: getContractAddress(t, in, selectors[0], datastore.ContractType(committee_verifier.ProxyType), committee_verifier.Deploy.Version(), ccvEvm.DefaultCommitteeVerifierQualifier, "committee verifier proxy"), Args: []byte{}, @@ -224,7 +230,7 @@ func TestE2ESmoke(t *testing.T) { dstSelector: selectors[1], finality: 1, receiver: mustGetEOAReceiverAddress(t, c, selectors[1]), - mandatoryCCVs: []protocol.CCV{ + ccvs: []protocol.CCV{ { CCVAddress: getContractAddress(t, in, selectors[0], datastore.ContractType(committee_verifier.ProxyType), committee_verifier.Deploy.Version(), ccvEvm.DefaultCommitteeVerifierQualifier, "committee verifier proxy"), Args: []byte{}, @@ -265,12 +271,10 @@ func TestE2ESmoke(t *testing.T) { Data: []byte{}, TokenAmounts: tokenAmounts, }, cciptestinterfaces.MessageOptions{ - Version: 3, - FinalityConfig: tc.finality, - Executor: getContractAddress(t, in, tc.srcSelector, datastore.ContractType(executor.ContractType), executor.Deploy.Version(), "", "executor"), - MandatoryCCVs: tc.mandatoryCCVs, - OptionalCCVs: tc.optionalCCVs, - OptionalThreshold: tc.threshold, + Version: 3, + FinalityConfig: tc.finality, + Executor: getContractAddress(t, in, tc.srcSelector, datastore.ContractType(executor.ContractType), executor.Deploy.Version(), "", "executor"), + CCVs: tc.ccvs, }) require.NoError(t, err) _, err = c.WaitOneSentEventBySeqNo(ctx, tc.srcSelector, tc.dstSelector, seqNo, defaultSentTimeout) diff --git a/cciptestinterfaces/interface.go b/cciptestinterfaces/interface.go index e5934177..262b61fd 100644 --- a/cciptestinterfaces/interface.go +++ b/cciptestinterfaces/interface.go @@ -1,4 +1,4 @@ -package ccv +package cciptestinterfaces import ( "context" @@ -73,12 +73,8 @@ type MessageOptions struct { GasLimit uint32 // OutOfOrderExecution is whether to execute the message out of order OutOfOrderExecution bool - // MandatoryCCVs are the mandatory CCVs for the message - MandatoryCCVs []protocol.CCV - // OptionalCCVs are the optional CCVs for the message - OptionalCCVs []protocol.CCV - // OptionalThreshold is the threshold for the optional CCVs - OptionalThreshold uint8 + // CCVs are the CCVs for the message + CCVs []protocol.CCV // FinalityConfig is the finality config for the message FinalityConfig uint16 // Executor is the executor address diff --git a/cciptestinterfaces/keys.go b/cciptestinterfaces/keys.go new file mode 100644 index 00000000..1bf5a18f --- /dev/null +++ b/cciptestinterfaces/keys.go @@ -0,0 +1,10 @@ +package cciptestinterfaces + +import "fmt" + +// XXXNewVerifierPrivateKey generates a test-only private key for a verifier +// given its index in the test environment. +// This should never be used in production. +func XXXNewVerifierPrivateKey(idx int) string { + return fmt.Sprintf("dev-private-key%d-12345678901234567890", idx) +} diff --git a/ccv-evm/impl.go b/ccv-evm/impl.go index 056598ad..2c9812d0 100644 --- a/ccv-evm/impl.go +++ b/ccv-evm/impl.go @@ -36,6 +36,7 @@ import ( "github.com/smartcontractkit/chainlink-ccip/chains/evm/gobindings/generated/latest/offramp" "github.com/smartcontractkit/chainlink-ccip/chains/evm/gobindings/generated/latest/onramp" "github.com/smartcontractkit/chainlink-ccv/protocol" + "github.com/smartcontractkit/chainlink-ccv/verifier/commit" "github.com/smartcontractkit/chainlink-deployments-framework/chain/evm" "github.com/smartcontractkit/chainlink-deployments-framework/datastore" "github.com/smartcontractkit/chainlink-deployments-framework/deployment" @@ -61,9 +62,27 @@ const ( // In the smoke test deployments these are the qualifiers that are used by default. DefaultCommitteeVerifierQualifier = "default" DefaultReceiverQualifier = "default" + + SecondaryCommitteeVerifierQualifier = "secondary" + SecondaryReceiverQualifier = "secondary" + + TertiaryCommitteeVerifierQualifier = "tertiary" + TertiaryReceiverQualifier = "tertiary" + + QuaternaryReceiverQualifier = "quaternary" ) -var ccipMessageSentTopic = onramp.OnRampCCIPMessageSent{}.Topic() +var ( + ccipMessageSentTopic = onramp.OnRampCCIPMessageSent{}.Topic() + + // this is a hacky way to be able to programmatically generate the individual verifier + // signing addresses for each qualifier. + qualifierToVerifierIndexes = map[string][]int{ + DefaultCommitteeVerifierQualifier: {0, 1}, + SecondaryCommitteeVerifierQualifier: {2, 3}, + TertiaryCommitteeVerifierQualifier: {4, 5}, + } +) type CCIP17EVM struct { e *deployment.Environment @@ -700,9 +719,7 @@ func serializeExtraArgsV3(opts cciptestinterfaces.MessageOptions) []byte { opts.Executor.String(), opts.ExecutorArgs, opts.TokenArgs, - opts.MandatoryCCVs, - opts.OptionalCCVs, - opts.OptionalThreshold, + opts.CCVs, ) if err != nil { panic(fmt.Sprintf("failed to create V3 extra args: %v", err)) @@ -802,29 +819,29 @@ func (m *CCIP17EVM) ConfigureNodes(ctx context.Context, bc *blockchain.Input) (s ), nil } -// getCommitteeSignatureConfig returns the committee configuration for a specific chain selector. -func getCommitteeSignatureConfig(selector uint64) committee_verifier.SetSignatureConfigArgs { - // Default configuration with 2 signers and threshold=2 - defaultConfig := committee_verifier.SetSignatureConfigArgs{ - Threshold: 2, - Signers: []common.Address{ - // TODO: why are these addresses hardcoded? where are they fetched from? - common.HexToAddress("0x6b3131d871c63c7fa592863e173cba2da5ffa68b"), - common.HexToAddress("0x099125558781da4bcdb16e457e15d997ecac68a8"), - }, - } - - // Special configuration for chain 3337 (selector 4793464827907405086) - threshold=1 - if selector == 4793464827907405086 { - return committee_verifier.SetSignatureConfigArgs{ - Threshold: 1, - Signers: []common.Address{ - common.HexToAddress("0x6b3131d871c63c7fa592863e173cba2da5ffa68b"), - }, +// getCommitteeSignatureConfig returns the committee signature configuration for a given qualifier. +// The signer addresses are programmatically generated in an identical to fashion to what is done in +// NewEnvironment to avoid hardcoding hard-to-determine addresses in the code. +func getCommitteeSignatureConfig(qualifier string) committee_verifier.SetSignatureConfigArgs { + indexes, ok := qualifierToVerifierIndexes[qualifier] + if !ok { + panic(fmt.Sprintf("couldn't find verifier indexes for qualifier: %s", qualifier)) + } + signerAddresses := make([]common.Address, 0, len(indexes)) + for _, index := range indexes { + privKeyString := cciptestinterfaces.XXXNewVerifierPrivateKey(index) + privateKey := make([]byte, 32) + copy(privateKey, privKeyString) + signer, err := commit.NewECDSAMessageSigner(privateKey) + if err != nil { + panic(fmt.Sprintf("failed to create ECDSA message signer: %v", err)) } + signerAddresses = append(signerAddresses, common.HexToAddress(signer.GetSignerAddress().String())) + } + return committee_verifier.SetSignatureConfigArgs{ + Threshold: uint8(len(indexes)), //nolint:gosec + Signers: signerAddresses, } - - return defaultConfig } func (m *CCIP17EVM) DeployContractsForSelector(ctx context.Context, env *deployment.Environment, selector uint64) (datastore.DataStore, error) { @@ -867,14 +884,31 @@ func (m *CCIP17EVM) DeployContractsForSelector(ctx context.Context, env *deploym OffRamp: sequences.OffRampParams{ Version: semver.MustParse(offrampoperations.Deploy.Version()), }, + // Deploy multiple committee verifiers in order to test different receiver + // configurations. CommitteeVerifier: []sequences.CommitteeVerifierParams{ { Version: semver.MustParse(committee_verifier.Deploy.Version()), // TODO: add mocked contract here FeeAggregator: common.HexToAddress("0x01"), - SignatureConfigArgs: getCommitteeSignatureConfig(selector), + SignatureConfigArgs: getCommitteeSignatureConfig(DefaultCommitteeVerifierQualifier), Qualifier: DefaultCommitteeVerifierQualifier, }, + // TODO: deploy the offchain verifiers that correspond to these contracts. + { + Version: semver.MustParse(committee_verifier.Deploy.Version()), + // TODO: add mocked contract here + FeeAggregator: common.HexToAddress("0x01"), + SignatureConfigArgs: getCommitteeSignatureConfig(SecondaryCommitteeVerifierQualifier), + Qualifier: SecondaryCommitteeVerifierQualifier, + }, + { + Version: semver.MustParse(committee_verifier.Deploy.Version()), + // TODO: add mocked contract here + FeeAggregator: common.HexToAddress("0x01"), + SignatureConfigArgs: getCommitteeSignatureConfig(TertiaryCommitteeVerifierQualifier), + Qualifier: TertiaryCommitteeVerifierQualifier, + }, }, OnRamp: sequences.OnRampParams{ Version: semver.MustParse(onrampoperations.Deploy.Version()), @@ -895,6 +929,7 @@ func (m *CCIP17EVM) DeployContractsForSelector(ctx context.Context, env *deploym }, MockReceivers: []sequences.MockReceiverParams{ { + // single required verifier (default), no optional verifiers, no optional threshold Version: semver.MustParse(mock_receiver.Deploy.Version()), RequiredVerifiers: []datastore.AddressRef{ { @@ -906,6 +941,70 @@ func (m *CCIP17EVM) DeployContractsForSelector(ctx context.Context, env *deploym }, Qualifier: DefaultReceiverQualifier, }, + { + // single required verifier (secondary), no optional verifiers, no optional threshold + Version: semver.MustParse(mock_receiver.Deploy.Version()), + RequiredVerifiers: []datastore.AddressRef{ + { + Type: datastore.ContractType(committee_verifier.ProxyType), + Version: semver.MustParse(committee_verifier.Deploy.Version()), + ChainSelector: selector, + Qualifier: SecondaryCommitteeVerifierQualifier, + }, + }, + Qualifier: SecondaryReceiverQualifier, + }, + { + // single required verifier (secondary), single optional verifier (tertiary), optional threshold=1 + // this means that the message should only be executed after the required and optional verifiers have signed. + // optional threshold being 1, with one optional, means that it must be retrieved. + Version: semver.MustParse(mock_receiver.Deploy.Version()), + RequiredVerifiers: []datastore.AddressRef{ + { + Type: datastore.ContractType(committee_verifier.ProxyType), + Version: semver.MustParse(committee_verifier.Deploy.Version()), + ChainSelector: selector, + Qualifier: SecondaryCommitteeVerifierQualifier, + }, + }, + OptionalVerifiers: []datastore.AddressRef{ + { + Type: datastore.ContractType(committee_verifier.ProxyType), + Version: semver.MustParse(committee_verifier.Deploy.Version()), + ChainSelector: selector, + Qualifier: TertiaryCommitteeVerifierQualifier, + }, + }, + OptionalThreshold: 1, + Qualifier: TertiaryReceiverQualifier, + }, + { + Version: semver.MustParse(mock_receiver.Deploy.Version()), + RequiredVerifiers: []datastore.AddressRef{ + { + Type: datastore.ContractType(committee_verifier.ProxyType), + Version: semver.MustParse(committee_verifier.Deploy.Version()), + ChainSelector: selector, + Qualifier: DefaultCommitteeVerifierQualifier, + }, + }, + OptionalVerifiers: []datastore.AddressRef{ + { + Type: datastore.ContractType(committee_verifier.ProxyType), + Version: semver.MustParse(committee_verifier.Deploy.Version()), + ChainSelector: selector, + Qualifier: SecondaryCommitteeVerifierQualifier, + }, + { + Type: datastore.ContractType(committee_verifier.ProxyType), + Version: semver.MustParse(committee_verifier.Deploy.Version()), + ChainSelector: selector, + Qualifier: TertiaryCommitteeVerifierQualifier, + }, + }, + OptionalThreshold: 1, + Qualifier: QuaternaryReceiverQualifier, + }, }, }, }, @@ -987,11 +1086,21 @@ func (m *CCIP17EVM) ConnectContractsWithSelectors(ctx context.Context, e *deploy Version: semver.MustParse(offrampoperations.Deploy.Version()), }, DefaultInboundCCVs: []datastore.AddressRef{ - {Type: datastore.ContractType(committee_verifier.ProxyType), Version: semver.MustParse(committee_verifier.Deploy.Version())}, + { + Type: datastore.ContractType(committee_verifier.ProxyType), + Version: semver.MustParse(committee_verifier.Deploy.Version()), + ChainSelector: selector, + Qualifier: DefaultCommitteeVerifierQualifier, + }, }, // LaneMandatedInboundCCVs: []datastore.AddressRef{}, DefaultOutboundCCVs: []datastore.AddressRef{ - {Type: datastore.ContractType(committee_verifier.ProxyType), Version: semver.MustParse(committee_verifier.Deploy.Version())}, + { + Type: datastore.ContractType(committee_verifier.ProxyType), + Version: semver.MustParse(committee_verifier.Deploy.Version()), + ChainSelector: selector, + Qualifier: DefaultCommitteeVerifierQualifier, + }, }, // LaneMandatedOutboundCCVs: []datastore.AddressRef{}, DefaultExecutor: datastore.AddressRef{ @@ -1021,6 +1130,26 @@ func (m *CCIP17EVM) ConnectContractsWithSelectors(ctx context.Context, e *deploy Cfg: changesets.ConfigureChainForLanesCfg{ ChainSel: selector, RemoteChains: remoteChains, + CommitteeVerifiers: []datastore.AddressRef{ + { + Type: datastore.ContractType(committee_verifier.ContractType), + Version: semver.MustParse(committee_verifier.Deploy.Version()), + ChainSelector: selector, + Qualifier: DefaultCommitteeVerifierQualifier, + }, + { + Type: datastore.ContractType(committee_verifier.ContractType), + Version: semver.MustParse(committee_verifier.Deploy.Version()), + ChainSelector: selector, + Qualifier: SecondaryCommitteeVerifierQualifier, + }, + { + Type: datastore.ContractType(committee_verifier.ContractType), + Version: semver.MustParse(committee_verifier.Deploy.Version()), + ChainSelector: selector, + Qualifier: TertiaryCommitteeVerifierQualifier, + }, + }, }, }) if err != nil { @@ -1049,14 +1178,16 @@ func (m *CCIP17EVM) ConnectContractsWithSelectors(ctx context.Context, e *deploy }, OutboundCCVs: []datastore.AddressRef{ { - Type: datastore.ContractType(committee_verifier.ProxyType), - Version: semver.MustParse("1.7.0"), + Type: datastore.ContractType(committee_verifier.ProxyType), + Version: semver.MustParse("1.7.0"), + Qualifier: DefaultCommitteeVerifierQualifier, }, }, InboundCCVs: []datastore.AddressRef{ { - Type: datastore.ContractType(committee_verifier.ProxyType), - Version: semver.MustParse("1.7.0"), + Type: datastore.ContractType(committee_verifier.ProxyType), + Version: semver.MustParse("1.7.0"), + Qualifier: DefaultCommitteeVerifierQualifier, }, }, } diff --git a/ccv-evm/operations.go b/ccv-evm/operations.go index 233b6cf2..b2fdb099 100644 --- a/ccv-evm/operations.go +++ b/ccv-evm/operations.go @@ -88,7 +88,7 @@ func DeployReceiverForSelector(e *deployment.Environment, selector uint64, args } // NewV3ExtraArgs encodes v3 extra args params. -func NewV3ExtraArgs(finalityConfig uint16, execAddr string, execArgs, tokenArgs []byte, requiredCCVs, optionalCCVs []protocol.CCV, optionalThreshold uint8) ([]byte, error) { +func NewV3ExtraArgs(finalityConfig uint16, execAddr string, execArgs, tokenArgs []byte, ccvs []protocol.CCV) ([]byte, error) { // ABI definition matching the exact Solidity struct EVMExtraArgsV3 const clientABI = ` [ @@ -99,22 +99,13 @@ func NewV3ExtraArgs(finalityConfig uint16, execAddr string, execArgs, tokenArgs { "components": [ { - "name": "requiredCCV", + "name": "ccvs", "type": "tuple[]", "components": [ {"name": "ccvAddress", "type": "address"}, {"name": "args", "type": "bytes"} ] }, - { - "name": "optionalCCV", - "type": "tuple[]", - "components": [ - {"name": "ccvAddress", "type": "address"}, - {"name": "args", "type": "bytes"} - ] - }, - {"name": "optionalThreshold", "type": "uint8"}, {"name": "finalityConfig", "type": "uint16"}, {"name": "executor", "type": "address"}, {"name": "executorArgs", "type": "bytes"}, @@ -136,28 +127,13 @@ func NewV3ExtraArgs(finalityConfig uint16, execAddr string, execArgs, tokenArgs } // Convert CCV slices to match Solidity CCV struct exactly - requiredCCV := make([]struct { - CcvAddress common.Address - Args []byte - }, len(requiredCCVs)) - - for i, ccv := range requiredCCVs { - requiredCCV[i] = struct { - CcvAddress common.Address - Args []byte - }{ - CcvAddress: common.BytesToAddress(ccv.CCVAddress), - Args: ccv.Args, - } - } - - optionalCCV := make([]struct { + ccvStructs := make([]struct { CcvAddress common.Address Args []byte - }, len(optionalCCVs)) + }, len(ccvs)) - for i, ccv := range optionalCCVs { - optionalCCV[i] = struct { + for i, ccv := range ccvs { + ccvStructs[i] = struct { CcvAddress common.Address Args []byte }{ @@ -168,27 +144,20 @@ func NewV3ExtraArgs(finalityConfig uint16, execAddr string, execArgs, tokenArgs // Struct matching exactly the Solidity EVMExtraArgsV3 order and types extraArgs := struct { - RequiredCCV []struct { + Ccvs []struct { CcvAddress common.Address Args []byte } - OptionalCCV []struct { - CcvAddress common.Address - Args []byte - } - OptionalThreshold uint8 - FinalityConfig uint16 - Executor common.Address - ExecutorArgs []byte - TokenArgs []byte + FinalityConfig uint16 + Executor common.Address + ExecutorArgs []byte + TokenArgs []byte }{ - RequiredCCV: requiredCCV, - OptionalCCV: optionalCCV, - OptionalThreshold: optionalThreshold, - FinalityConfig: finalityConfig, - Executor: common.HexToAddress(execAddr), - ExecutorArgs: execArgs, - TokenArgs: tokenArgs, + Ccvs: ccvStructs, + FinalityConfig: finalityConfig, + Executor: common.HexToAddress(execAddr), + ExecutorArgs: execArgs, + TokenArgs: tokenArgs, } encoded, err := parsedABI.Methods["encodeEVMExtraArgsV3"].Inputs.Pack(extraArgs) @@ -289,9 +258,7 @@ func MergeAddresses(addrs []string, sel uint64, refs []datastore.AddressRef) ([] break } } - for _, r := range refs { - addresses = append(addresses, r) - } + addresses = append(addresses, refs...) addrBytes, err := json.Marshal(addresses) if err != nil { return nil, fmt.Errorf("failed to marshal addresses: %w", err) diff --git a/go.mod b/go.mod index 6257864c..b69c5913 100644 --- a/go.mod +++ b/go.mod @@ -33,9 +33,9 @@ require ( github.com/prometheus/client_golang v1.23.0 github.com/rs/zerolog v1.34.0 github.com/smartcontractkit/chain-selectors v1.0.72 - github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20251017065736-83976da2ec64 - github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment v0.0.0-20251017065736-83976da2ec64 - github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20251017065736-83976da2ec64 + github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20251017174358-1a48fa8a246b + github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment v0.0.0-20251017174358-1a48fa8a246b + github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20251017174358-1a48fa8a246b github.com/smartcontractkit/chainlink-common v0.9.6-0.20250929154511-1f5fbda7ae76 github.com/smartcontractkit/chainlink-deployments-framework v0.55.1 github.com/smartcontractkit/chainlink-evm v0.3.3 diff --git a/go.sum b/go.sum index 9cd78e6a..657d5d32 100644 --- a/go.sum +++ b/go.sum @@ -719,16 +719,16 @@ github.com/smartcontractkit/chain-selectors v1.0.72 h1:AExF2H3mABdLCN0QZd+IjU8Ck github.com/smartcontractkit/chain-selectors v1.0.72/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= github.com/smartcontractkit/chainlink-aptos v0.0.0-20250915164817-46a35eda083d h1:bcfnHPXAhrhUw95X60Y/lDhQAb4SxSyTrqyVCHqfXPI= github.com/smartcontractkit/chainlink-aptos v0.0.0-20250915164817-46a35eda083d/go.mod h1:tEjqontct1/5cKHm4q75nopZa1rwzaQZwd9U9wn0uZE= -github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20251017065736-83976da2ec64 h1:qHUX9Y8i4b/TBO2ILZCkeKf9G1AO1MmnmTpTTbictAI= -github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20251017065736-83976da2ec64/go.mod h1:W3d6TbZ4PNLGb8QOK8URc/tVWBhnAOwtAYsQ2iPgwtw= -github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment v0.0.0-20251017065736-83976da2ec64 h1:h9NmYo0YPySduRclcUZc1ba39nEEPyabwtelexsVj8Y= -github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment v0.0.0-20251017065736-83976da2ec64/go.mod h1:LvEaw/iOLs6UQRCqr7zyxhxDopd+KPp53GE77N8TugE= +github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20251017174358-1a48fa8a246b h1:tHOvMBWdYxQ/ovH10iUvN3XHmjg2lPPWOXKY99mSaD4= +github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20251017174358-1a48fa8a246b/go.mod h1:W3d6TbZ4PNLGb8QOK8URc/tVWBhnAOwtAYsQ2iPgwtw= +github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment v0.0.0-20251017174358-1a48fa8a246b h1:08jEs6dSBihScGcLiBPoJu4NiMwGTaiL8vSWqXFqT6Q= +github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment v0.0.0-20251017174358-1a48fa8a246b/go.mod h1:LvEaw/iOLs6UQRCqr7zyxhxDopd+KPp53GE77N8TugE= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250908144012-8184001834b5 h1:GmJQqNrWn5pNc8YTei6l2TOSYjK2fRd4+edFZIifCrU= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250908144012-8184001834b5/go.mod h1:Ve1xD71bl193YIZQEoJMmBqLGQJdNs29bwbuObwvbhQ= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250908144012-8184001834b5 h1:QhcYGEhRLInr1/qh/3RJiVdvJ0nxBHKhPe65WLbSBnU= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250908144012-8184001834b5/go.mod h1:xtZNi6pOKdC3sLvokDvXOhgHzT+cyBqH/gWwvxTxqrg= -github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20251017065736-83976da2ec64 h1:0EtIB0DChFUETLlpBHKm/VfGvgOEgaemr3G2mPhoRUY= -github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20251017065736-83976da2ec64/go.mod h1:oDe4huAcmrsakLGZxxGe+Y03cQ+sapxM0gZrTe20iLY= +github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20251017174358-1a48fa8a246b h1:koEaLC+3bStmauerlqJcvNlCUhpGwR6tRobb4qc7gdo= +github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20251017174358-1a48fa8a246b/go.mod h1:oDe4huAcmrsakLGZxxGe+Y03cQ+sapxM0gZrTe20iLY= github.com/smartcontractkit/chainlink-common v0.9.6-0.20250929154511-1f5fbda7ae76 h1:Slnws8RoXRUYGgEMYK6X2yYzjZwNgVb93PxU45VEObQ= github.com/smartcontractkit/chainlink-common v0.9.6-0.20250929154511-1f5fbda7ae76/go.mod h1:1r3aM96KHAESfnayJ3BTHCkP1qJS1BEG1r4czeoaXlA= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.4 h1:hvqATtrZ0iMRTI80cpBot/3JFbjz2j+2tvpfooVhRHw=