From 978b3aa7f118cbe21aa1b3c330764d0140273bd7 Mon Sep 17 00:00:00 2001 From: Bolek Kulbabinski <1416262+bolekk@users.noreply.github.com> Date: Wed, 11 Feb 2026 09:20:53 -0800 Subject: [PATCH 1/2] [LocalCRE] Use OCR config from CapRegistry for evm capability --- .../keystone/changeset/internal/types.go | 4 + system-tests/lib/cre/contracts/keystone.go | 8 +- system-tests/lib/cre/contracts/ocr3.go | 17 ++- .../cre/features/consensus/v1/consensus.go | 5 +- .../cre/features/consensus/v2/consensus.go | 2 + .../lib/cre/features/don_time/don_time.go | 5 +- system-tests/lib/cre/features/evm/v2/evm.go | 114 +++++------------- system-tests/lib/cre/features/vault/vault.go | 5 +- 8 files changed, 46 insertions(+), 114 deletions(-) diff --git a/deployment/keystone/changeset/internal/types.go b/deployment/keystone/changeset/internal/types.go index 2aaa35e2009..2b6ffa01303 100644 --- a/deployment/keystone/changeset/internal/types.go +++ b/deployment/keystone/changeset/internal/types.go @@ -18,6 +18,7 @@ import ( cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment" "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/deployment/cre/ocr3" capabilities_registry "github.com/smartcontractkit/chainlink-evm/gethwrappers/keystone/generated/capabilities_registry_1_1_0" kcr "github.com/smartcontractkit/chainlink-evm/gethwrappers/keystone/generated/capabilities_registry_1_1_0" @@ -103,6 +104,9 @@ type DONCapabilityWithConfig struct { Capability kcr.CapabilitiesRegistryCapability Config *capabilitiespb.CapabilityConfig UseCapRegOCRConfig bool + + // Used only by LocalCRE + OverrideOCR3Config *ocr3.OracleConfig } func (v DonCapabilities) Validate() error { diff --git a/system-tests/lib/cre/contracts/keystone.go b/system-tests/lib/cre/contracts/keystone.go index eb3c68464ee..92f1de7390d 100644 --- a/system-tests/lib/cre/contracts/keystone.go +++ b/system-tests/lib/cre/contracts/keystone.go @@ -137,11 +137,7 @@ func (d *dons) allDonCapabilities() []keystone_changeset.DonCapabilities { // embedOCR3Config computes the full OCR3 configuration for a consensus V2 DON // and embeds it in the capability config proto's Ocr3Configs map. -func (d *dons) embedOCR3Config(capConfig *capabilitiespb.CapabilityConfig, don donConfig, registryChainSelector uint64) error { - oracleConfig, err := DefaultOCR3Config() - if err != nil { - return fmt.Errorf("failed to get default OCR3 config: %w", err) - } +func (d *dons) embedOCR3Config(capConfig *capabilitiespb.CapabilityConfig, don donConfig, registryChainSelector uint64, oracleConfig *ocr3.OracleConfig) error { oracleConfig.TransmissionSchedule = []int{len(don.Nops[0].Nodes)} var allNodeIDs []string @@ -264,7 +260,7 @@ func (d *dons) mustToV2ConfigureInput(chainSelector uint64, contractAddress stri configBytes := []byte("{}") if cap.Config != nil { if cap.UseCapRegOCRConfig { - if err := d.embedOCR3Config(cap.Config, don, chainSelector); err != nil { + if err := d.embedOCR3Config(cap.Config, don, chainSelector, cap.OverrideOCR3Config); err != nil { panic(fmt.Sprintf("failed to embed OCR3 config for consensus V2: %s", err)) } } diff --git a/system-tests/lib/cre/contracts/ocr3.go b/system-tests/lib/cre/contracts/ocr3.go index 9b9fc78ecee..0916a83e607 100644 --- a/system-tests/lib/cre/contracts/ocr3.go +++ b/system-tests/lib/cre/contracts/ocr3.go @@ -49,7 +49,7 @@ func DeployOCR3Contract(logger zerolog.Logger, qualifier string, selector uint64 } // values supplied by Alexandr Yepishev as the expected values for OCR3 config -func DefaultOCR3Config() (*keystone_changeset.OracleConfig, error) { +func DefaultOCR3Config() *ocr3.OracleConfig { // values supplied by Alexandr Yepishev as the expected values for OCR3 config oracleConfig := &keystone_changeset.OracleConfig{ DeltaProgressMillis: 5000, @@ -75,10 +75,10 @@ func DefaultOCR3Config() (*keystone_changeset.OracleConfig, error) { UniqueReports: true, } - return oracleConfig, nil + return oracleConfig } -func DefaultOCR3_1Config(numWorkers int) (*ocr3_1.V3_1OracleConfig, error) { +func DefaultOCR3_1Config(numWorkers int) *ocr3_1.V3_1OracleConfig { return &ocr3_1.V3_1OracleConfig{ DeltaProgressMillis: 5000, // DKG 10-15 seconds; Vault 5 sec // check bandwidth from nops DeltaRoundMillis: 200, @@ -103,14 +103,11 @@ func DefaultOCR3_1Config(numWorkers int) (*ocr3_1.V3_1OracleConfig, error) { PrevConfigDigest: "", PrevSeqNr: 0, PrevHistoryDigest: "", - }, nil + } } -func DefaultChainCapabilityOCR3Config() (*keystone_changeset.OracleConfig, error) { - cfg, err := DefaultOCR3Config() - if err != nil { - return nil, fmt.Errorf("failed to generate default OCR3 config: %w", err) - } +func DefaultChainCapabilityOCR3Config() *ocr3.OracleConfig { + cfg := DefaultOCR3Config() cfg.DeltaRoundMillis = 1000 const kib = 1024 @@ -124,5 +121,5 @@ func DefaultChainCapabilityOCR3Config() (*keystone_changeset.OracleConfig, error MaxReportCount: 1000, MaxBatchSize: 200, } - return cfg, nil + return cfg } diff --git a/system-tests/lib/cre/features/consensus/v1/consensus.go b/system-tests/lib/cre/features/consensus/v1/consensus.go index c7e4c657f92..27111084a9f 100644 --- a/system-tests/lib/cre/features/consensus/v1/consensus.go +++ b/system-tests/lib/cre/features/consensus/v1/consensus.go @@ -93,10 +93,7 @@ func (c *Consensus) PostEnvStartup( return errors.Wrap(err, "failed while waiting for Log Poller to become healthy") } - ocr3Config, ocr3confErr := contracts.DefaultOCR3Config() - if ocr3confErr != nil { - return fmt.Errorf("failed to get default OCR3 config: %w", ocr3confErr) - } + ocr3Config := contracts.DefaultOCR3Config() chain, ok := creEnv.CldfEnvironment.BlockChains.EVMChains()[creEnv.RegistryChainSelector] if !ok { diff --git a/system-tests/lib/cre/features/consensus/v2/consensus.go b/system-tests/lib/cre/features/consensus/v2/consensus.go index cb2eed4f428..3a91e5aa995 100644 --- a/system-tests/lib/cre/features/consensus/v2/consensus.go +++ b/system-tests/lib/cre/features/consensus/v2/consensus.go @@ -24,6 +24,7 @@ import ( keystone_changeset "github.com/smartcontractkit/chainlink/deployment/keystone/changeset" "github.com/smartcontractkit/chainlink/system-tests/lib/cre" + "github.com/smartcontractkit/chainlink/system-tests/lib/cre/contracts" credon "github.com/smartcontractkit/chainlink/system-tests/lib/cre/don" "github.com/smartcontractkit/chainlink/system-tests/lib/cre/don/jobs" "github.com/smartcontractkit/chainlink/system-tests/lib/cre/don/jobs/standardcapability" @@ -55,6 +56,7 @@ func (c *Consensus) PreEnvStartup( LocalOnly: don.HasOnlyLocalCapabilities(), }, UseCapRegOCRConfig: true, + OverrideOCR3Config: contracts.DefaultOCR3Config(), }} return &cre.PreEnvStartupOutput{ diff --git a/system-tests/lib/cre/features/don_time/don_time.go b/system-tests/lib/cre/features/don_time/don_time.go index e9225243aa3..3d13859a44d 100644 --- a/system-tests/lib/cre/features/don_time/don_time.go +++ b/system-tests/lib/cre/features/don_time/don_time.go @@ -67,10 +67,7 @@ func (o *DONTime) PostEnvStartup( return fmt.Errorf("failed to create DON Time jobs: %w", jobErr) } - ocr3Config, ocr3confErr := contracts.DefaultOCR3Config() - if ocr3confErr != nil { - return fmt.Errorf("failed to get default OCR3 config: %w", ocr3confErr) - } + ocr3Config := contracts.DefaultOCR3Config() chain, ok := creEnv.CldfEnvironment.BlockChains.EVMChains()[creEnv.RegistryChainSelector] if !ok { diff --git a/system-tests/lib/cre/features/evm/v2/evm.go b/system-tests/lib/cre/features/evm/v2/evm.go index 7c4af6e3975..057091300f4 100644 --- a/system-tests/lib/cre/features/evm/v2/evm.go +++ b/system-tests/lib/cre/features/evm/v2/evm.go @@ -11,7 +11,6 @@ import ( "dario.cat/mergo" "github.com/Masterminds/semver/v3" - "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" "github.com/rs/zerolog" "google.golang.org/protobuf/types/known/durationpb" @@ -21,7 +20,6 @@ import ( kcr "github.com/smartcontractkit/chainlink-evm/gethwrappers/keystone/generated/capabilities_registry_1_1_0" "github.com/smartcontractkit/chainlink-testing-framework/framework/components/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/ptr" - "github.com/smartcontractkit/chainlink/deployment/cre/common/strategies" cre_jobs "github.com/smartcontractkit/chainlink/deployment/cre/jobs" cre_jobs_ops "github.com/smartcontractkit/chainlink/deployment/cre/jobs/operations" cre_jobs_pkg "github.com/smartcontractkit/chainlink/deployment/cre/jobs/pkg" @@ -32,7 +30,6 @@ import ( capabilitiespb "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" "github.com/smartcontractkit/chainlink-deployments-framework/datastore" - "github.com/smartcontractkit/chainlink-deployments-framework/operations" "github.com/smartcontractkit/chainlink/system-tests/lib/cre" "github.com/smartcontractkit/chainlink/system-tests/lib/cre/contracts" credon "github.com/smartcontractkit/chainlink/system-tests/lib/cre/don" @@ -124,6 +121,8 @@ func (o *EVM) PreEnvStartup( MethodConfigs: evmMethodConfigs, LocalOnly: don.HasOnlyLocalCapabilities(), }, + UseCapRegOCRConfig: true, + OverrideOCR3Config: contracts.DefaultChainCapabilityOCR3Config(), }) } @@ -139,15 +138,6 @@ func (o *EVM) PostEnvStartup( dons *cre.Dons, creEnv *cre.Environment, ) error { - chainsWithEVMCapability := chainsWithEVMCapability(creEnv.Blockchains, dons.DonsWithFlag(flag)) - for chainID, selector := range chainsWithEVMCapability { - qualifier := ks_contracts_op.CapabilityContractIdentifier(uint64(chainID)) - _, _, seqErr := contracts.DeployOCR3Contract(testLogger, qualifier, uint64(selector), creEnv.CldfEnvironment, creEnv.ContractVersions) - if seqErr != nil { - return fmt.Errorf("failed to deploy EVM OCR3 contract for chainID %d, selector %d: %w", chainID, selector, seqErr) - } - } - jobsErr := createJobs( ctx, don, @@ -158,71 +148,12 @@ func (o *EVM) PostEnvStartup( return jobsErr } - // TODO should we make sure that log poller is listening before we try to configure contracts? - - // configure OCR3 contracts - for chainID, selector := range chainsWithEVMCapability { - qualifier := ks_contracts_op.CapabilityContractIdentifier(uint64(chainID)) - evmOCR3Addr := contracts.MustGetAddressFromDataStore(creEnv.CldfEnvironment.DataStore, uint64(selector), keystone_changeset.OCR3Capability.String(), semver.MustParse("1.0.0"), qualifier) - var evmDON *cre.Don - for _, don := range dons.DonsWithFlag(cre.EVMCapability) { - if flags.HasFlagForChain(don.Flags, cre.EVMCapability, uint64(chainID)) { - evmDON = don - break - } - } - - if evmDON == nil { - return fmt.Errorf("failed to find DON for EVM chainID %d. This should never happen", chainID) - } - - ocr3Config, ocr3confErr := contracts.DefaultChainCapabilityOCR3Config() - if ocr3confErr != nil { - return fmt.Errorf("failed to get default OCR3 config: %w", ocr3confErr) - } - - chain, ok := creEnv.CldfEnvironment.BlockChains.EVMChains()[uint64(selector)] - if !ok { - return fmt.Errorf("chain with selector %d not found in environment", selector) - } - - strategy, err := strategies.CreateStrategy( - chain, - *creEnv.CldfEnvironment, - nil, - nil, - common.HexToAddress(evmOCR3Addr), - "PostEnvStartup - Configure OCR3 Contract - EVM Capability", - ) - if err != nil { - return fmt.Errorf("failed to create strategy: %w", err) - } - - _, err = operations.ExecuteOperation( - creEnv.CldfEnvironment.OperationsBundle, - ks_contracts_op.ConfigureOCR3Op, - ks_contracts_op.ConfigureOCR3OpDeps{ - Env: creEnv.CldfEnvironment, - Strategy: strategy, - }, - ks_contracts_op.ConfigureOCR3OpInput{ - ContractAddress: ptr.Ptr(common.HexToAddress(evmOCR3Addr)), - ChainSelector: uint64(selector), - DON: evmDON.KeystoneDONConfig(), - Config: evmDON.ResolveORC3Config(ocr3Config), - DryRun: false, - }, - ) - if err != nil { - return errors.Wrap(err, fmt.Sprintf("failed to configure EVM OCR3 contract for chainID: %d, address:%s", uint64(chainID), evmOCR3Addr)) - } - } - // configure EVM forwarders consensusDons := dons.DonsWithFlags(cre.ConsensusCapability, cre.ConsensusCapabilityV2) // for now we end up configuring forwarders twice, if the same chain has both evm v1 and v2 capabilities enabled // it doesn't create any issues, but ideally we wouldn't do that + chainsWithEVMCapability := chainsWithEVMCapability(creEnv.Blockchains, dons.DonsWithFlag(flag)) if len(chainsWithEVMCapability) > 0 { evmChainsWithForwarders := make([]uint64, 0) for chainID := range chainsWithEVMCapability { @@ -290,24 +221,17 @@ func createJobs( return fmt.Errorf("could not find enabled chainIDs for '%s' in don '%s': %w", flag, don.Name, err) } + registryChainID, rcErr := chainselectors.ChainIdFromSelector(creEnv.RegistryChainSelector) + if rcErr != nil { + return fmt.Errorf("failed to get chain ID from registry chain selector %d: %w", creEnv.RegistryChainSelector, rcErr) + } + for _, chainID := range enabledChainIDs { chainSelector, selErr := chainselectors.SelectorFromChainId(chainID) if selErr != nil { return errors.Wrapf(selErr, "failed to get chain selector from chainID %d", chainID) } chainIDStr := strconv.FormatUint(chainID, 10) - qualifier := ks_contracts_op.CapabilityContractIdentifier(chainID) - - ocr3Key := datastore.NewAddressRefKey( - chainSelector, - datastore.ContractType(keystone_changeset.OCR3Capability.String()), - semver.MustParse("1.0.0"), - qualifier, - ) - ocr3ConfigContractAddress, err := creEnv.CldfEnvironment.DataStore.Addresses().Get(ocr3Key) - if err != nil { - return errors.Wrapf(err, "failed to get contract address for key %s and chainID %d", ocr3Key, chainID) - } capabilityConfig, resolveErr := cre.ResolveCapabilityConfig(nodeSet, flag, cre.ChainCapabilityScope(chainID)) if resolveErr != nil { @@ -326,6 +250,12 @@ func createJobs( } nodeAddress := evmKey.PublicAddress.Hex() + evmRegistryKey, ok := workerNode.Keys.EVM[registryChainID] + if !ok { + return fmt.Errorf("failed to get registry EVM key (chainID %d, node index %d) enabledChainIDs: %v", registryChainID, workerNode.Index, enabledChainIDs) + } + nodeRegistryAddress := evmRegistryKey.PublicAddress.Hex() + creForwarderKey := datastore.NewAddressRefKey( chainSelector, datastore.ContractType(keystone_changeset.KeystoneForwarder.String()), @@ -374,6 +304,16 @@ func createJobs( strategyName = "multi-chain" } + capRegVersion, ok := creEnv.ContractVersions[keystone_changeset.CapabilitiesRegistry.String()] + if !ok { + return errors.New("CapabilitiesRegistry version not found in contract versions") + } + registryAddrRefKey := cre_jobs_pkg.GetCapRegAddressRefKey(creEnv.RegistryChainSelector, "", capRegVersion.String()) + registryContractAddrRef, err := creEnv.CldfEnvironment.DataStore.Addresses().Get(registryAddrRefKey) + if err != nil { + return fmt.Errorf("failed to get contract address for ref key %s: %w", registryAddrRefKey, err) + } + workerInput := cre_jobs.ProposeJobSpecInput{ Domain: offchain.ProductLabel, Environment: cre.EnvironmentName, @@ -392,14 +332,16 @@ func createJobs( Enabled: true, ChainID: chainIDStr, BootstrapPeers: bootstrapPeers, - OCRContractAddress: ocr3ConfigContractAddress.Address, + OCRContractAddress: registryContractAddrRef.Address, OCRKeyBundleID: evmKeyBundle, - TransmitterID: nodeAddress, + TransmitterID: nodeRegistryAddress, OnchainSigningStrategy: cre_jobs_pkg.OnchainSigningStrategy{ StrategyName: strategyName, Config: workerNode.Keys.OCR2BundleIDs, }, }, + "useCapRegOCRConfig": true, + "capRegVersion": capRegVersion.String(), }, } diff --git a/system-tests/lib/cre/features/vault/vault.go b/system-tests/lib/cre/features/vault/vault.go index f510c9e9185..f36bc931e8f 100644 --- a/system-tests/lib/cre/features/vault/vault.go +++ b/system-tests/lib/cre/features/vault/vault.go @@ -179,10 +179,7 @@ func (o *Vault) PostEnvStartup( return fmt.Errorf("failed to create OCR3 jobs: %w", jobErr) } - ocr3Config, ocr3confErr := contracts.DefaultOCR3_1Config(don.WorkersCount()) - if ocr3confErr != nil { - return fmt.Errorf("failed to get default OCR3 config: %w", ocr3confErr) - } + ocr3Config := contracts.DefaultOCR3_1Config(don.WorkersCount()) dkgConfig, dErr := dkgReportingPluginConfig(don) if dErr != nil { From 6fd813c8e9975f46a455f2fef69ee77ab4a115c7 Mon Sep 17 00:00:00 2001 From: Bolek Kulbabinski <1416262+bolekk@users.noreply.github.com> Date: Thu, 12 Feb 2026 10:15:09 -0800 Subject: [PATCH 2/2] feedback --- .../keystone/changeset/internal/types.go | 4 - system-tests/lib/cre/contracts/keystone.go | 12 +- .../lib/cre/contracts/keystone_test.go | 211 +++++++++++++----- .../lib/cre/environment/environment.go | 4 + .../cre/features/consensus/v2/consensus.go | 8 +- system-tests/lib/cre/features/evm/v2/evm.go | 8 +- system-tests/lib/cre/types.go | 6 + 7 files changed, 189 insertions(+), 64 deletions(-) diff --git a/deployment/keystone/changeset/internal/types.go b/deployment/keystone/changeset/internal/types.go index 2b6ffa01303..2aaa35e2009 100644 --- a/deployment/keystone/changeset/internal/types.go +++ b/deployment/keystone/changeset/internal/types.go @@ -18,7 +18,6 @@ import ( cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment" "github.com/smartcontractkit/chainlink/deployment" - "github.com/smartcontractkit/chainlink/deployment/cre/ocr3" capabilities_registry "github.com/smartcontractkit/chainlink-evm/gethwrappers/keystone/generated/capabilities_registry_1_1_0" kcr "github.com/smartcontractkit/chainlink-evm/gethwrappers/keystone/generated/capabilities_registry_1_1_0" @@ -104,9 +103,6 @@ type DONCapabilityWithConfig struct { Capability kcr.CapabilitiesRegistryCapability Config *capabilitiespb.CapabilityConfig UseCapRegOCRConfig bool - - // Used only by LocalCRE - OverrideOCR3Config *ocr3.OracleConfig } func (v DonCapabilities) Validate() error { diff --git a/system-tests/lib/cre/contracts/keystone.go b/system-tests/lib/cre/contracts/keystone.go index 92f1de7390d..472b4e4aca5 100644 --- a/system-tests/lib/cre/contracts/keystone.go +++ b/system-tests/lib/cre/contracts/keystone.go @@ -178,7 +178,7 @@ func (d *dons) embedOCR3Config(capConfig *capabilitiespb.CapabilityConfig, don d return nil } -func (d *dons) mustToV2ConfigureInput(chainSelector uint64, contractAddress string) cap_reg_v2_seq.ConfigureCapabilitiesRegistryInput { +func (d *dons) mustToV2ConfigureInput(chainSelector uint64, contractAddress string, capabilityToOCR3Config map[string]*ocr3.OracleConfig) cap_reg_v2_seq.ConfigureCapabilitiesRegistryInput { nops := make([]capabilities_registry_v2.CapabilitiesRegistryNodeOperatorParams, 0) nodes := make([]contracts.NodesInput, 0) capabilities := make([]contracts.RegisterableCapability, 0) @@ -260,8 +260,12 @@ func (d *dons) mustToV2ConfigureInput(chainSelector uint64, contractAddress stri configBytes := []byte("{}") if cap.Config != nil { if cap.UseCapRegOCRConfig { - if err := d.embedOCR3Config(cap.Config, don, chainSelector, cap.OverrideOCR3Config); err != nil { - panic(fmt.Sprintf("failed to embed OCR3 config for consensus V2: %s", err)) + ocrConfig := capabilityToOCR3Config[cap.Capability.LabelledName] + if ocrConfig == nil { + panic("no OCR3 config found for capability " + cap.Capability.LabelledName) + } + if err := d.embedOCR3Config(cap.Config, don, chainSelector, ocrConfig); err != nil { + panic(fmt.Sprintf("failed to embed OCR3 config for capability %s: %s", cap.Capability.LabelledName, err)) } } if protoBytes, err := proto.Marshal(cap.Config); err == nil { @@ -471,7 +475,7 @@ func ConfigureCapabilityRegistry(input cre.ConfigureCapabilityRegistryInput) (Ca } // Transform dons data to V2 sequence input format - v2Input := dons.mustToV2ConfigureInput(input.ChainSelector, input.CapabilitiesRegistryAddress.Hex()) + v2Input := dons.mustToV2ConfigureInput(input.ChainSelector, input.CapabilitiesRegistryAddress.Hex(), input.CapabilityToOCR3Config) _, seqErr := operations.ExecuteSequence( input.CldEnv.OperationsBundle, cap_reg_v2_seq.ConfigureCapabilitiesRegistry, diff --git a/system-tests/lib/cre/contracts/keystone_test.go b/system-tests/lib/cre/contracts/keystone_test.go index cdd6905d788..94ebc4cdbae 100644 --- a/system-tests/lib/cre/contracts/keystone_test.go +++ b/system-tests/lib/cre/contracts/keystone_test.go @@ -1,18 +1,97 @@ package contracts import ( + "context" + "errors" + "fmt" "math/big" + "strings" "testing" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" + "google.golang.org/grpc" + + chainselectors "github.com/smartcontractkit/chain-selectors" capabilitiespb "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" + cldf_offchain "github.com/smartcontractkit/chainlink-deployments-framework/offchain" kcr "github.com/smartcontractkit/chainlink-evm/gethwrappers/keystone/generated/capabilities_registry_1_1_0" + nodev1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/node" + "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/shared/ptypes" + keystone_changeset "github.com/smartcontractkit/chainlink/deployment/keystone/changeset" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" ) +// fakeOffchainClient implements offchain.Client; only ListNodes and +// ListNodeChainConfigs are wired, every other method panics via the embedded +// nil interface. +type fakeOffchainClient struct { + cldf_offchain.Client + nodesByID map[string]*fakeNodeInfo +} + +type fakeNodeInfo struct { + id string + name string + csaKey string + workflowKey string + p2pID string + chainConfigs []*nodev1.ChainConfig +} + +func newFakeOffchainClient(nodes []*fakeNodeInfo) *fakeOffchainClient { + f := &fakeOffchainClient{nodesByID: make(map[string]*fakeNodeInfo)} + for _, n := range nodes { + f.nodesByID[n.id] = n + } + return f +} + +func (f *fakeOffchainClient) ListNodes(_ context.Context, in *nodev1.ListNodesRequest, _ ...grpc.CallOption) (*nodev1.ListNodesResponse, error) { + var wantP2P map[string]bool + if in.Filter != nil { + for _, sel := range in.Filter.Selectors { + if sel.Key == "p2p_id" && sel.Op == ptypes.SelectorOp_IN && sel.Value != nil { + wantP2P = make(map[string]bool) + for _, v := range strings.Split(*sel.Value, ",") { + wantP2P[v] = true + } + } + } + } + + out := make([]*nodev1.Node, 0, len(f.nodesByID)) + for _, n := range f.nodesByID { + if wantP2P != nil && !wantP2P[n.p2pID] { + continue + } + p2pVal := n.p2pID + wfKey := n.workflowKey + out = append(out, &nodev1.Node{ + Id: n.id, + Name: n.name, + PublicKey: n.csaKey, + WorkflowKey: &wfKey, + IsEnabled: true, + Labels: []*ptypes.Label{{Key: "p2p_id", Value: &p2pVal}}, + }) + } + return &nodev1.ListNodesResponse{Nodes: out}, nil +} + +func (f *fakeOffchainClient) ListNodeChainConfigs(_ context.Context, in *nodev1.ListNodeChainConfigsRequest, _ ...grpc.CallOption) (*nodev1.ListNodeChainConfigsResponse, error) { + if in.Filter == nil || len(in.Filter.NodeIds) == 0 { + return nil, errors.New("filter with node IDs required") + } + n, ok := f.nodesByID[in.Filter.NodeIds[0]] + if !ok { + return nil, fmt.Errorf("node not found: %s", in.Filter.NodeIds[0]) + } + return &nodev1.ListNodeChainConfigsResponse{ChainConfigs: n.chainConfigs}, nil +} + func TestDonsOrderedByID(t *testing.T) { // Test donsOrderedByID sorts by id ascending d := dons{ @@ -34,16 +113,77 @@ func TestDonsOrderedByID(t *testing.T) { } func TestToV2ConfigureInput(t *testing.T) { - // Create test peer IDs - peerID1 := p2pkey.MustNewV2XXXTestingOnly(big.NewInt(1)).PeerID().String() - peerID2 := p2pkey.MustNewV2XXXTestingOnly(big.NewInt(2)).PeerID().String() + chainSel := chainselectors.ETHEREUM_TESTNET_SEPOLIA.Selector + chainID, err := chainselectors.GetChainIDFromSelector(chainSel) + require.NoError(t, err) + + key1 := p2pkey.MustNewV2XXXTestingOnly(big.NewInt(1)) + key2 := p2pkey.MustNewV2XXXTestingOnly(big.NewInt(2)) + peerID1 := key1.PeerID().String() + peerID2 := key2.PeerID().String() + + fakeNodes := []*fakeNodeInfo{ + { + id: "node_01", + name: "test-node-1", + csaKey: "403b72f0b1b3b5f5a91bcfedb7f28599767502a04b5b7e067fcf3782e23eeb9c", + workflowKey: "5193f72fc7b4323a86088fb0acb4e4494ae351920b3944bd726a59e8dbcdd45f", + p2pID: peerID1, + chainConfigs: []*nodev1.ChainConfig{{ + Chain: &nodev1.Chain{ + Type: nodev1.ChainType_CHAIN_TYPE_EVM, + Id: chainID, + }, + Ocr2Config: &nodev1.OCR2Config{ + OcrKeyBundle: &nodev1.OCR2Config_OCRKeyBundle{ + OffchainPublicKey: "03dacd15fc96c965c648e3623180de002b71a97cf6eeca9affb91f461dcd6ce1", + OnchainSigningAddress: "b35409a8d4f9a18da55c5b2bb08a3f5f68d44442", + ConfigPublicKey: "5193f72fc7b4323a86088fb0acb4e4494ae351920b3944bd726a59e8dbcdd45f", + BundleId: "665a101d79d310cb0a5ebf695b06e8fc8082b5cbe62d7d362d80d47447a31fea", + }, + P2PKeyBundle: &nodev1.OCR2Config_P2PKeyBundle{ + PeerId: peerID1, + }, + IsBootstrap: false, + }, + AccountAddress: "0x2877F08d9c5Cc9F401F730Fa418fAE563A9a2FF3", + }}, + }, + { + id: "node_02", + name: "test-node-2", + csaKey: "28b91143ec9111796a7d63e14c1cf6bb01b4ed59667ab54f5bc72ebe49c881be", + workflowKey: "2c45fec2320f6bcd36444529a86d9f8b4439499a5d8272dec9bcbbebb5e1bf01", + p2pID: peerID2, + chainConfigs: []*nodev1.ChainConfig{{ + Chain: &nodev1.Chain{ + Type: nodev1.ChainType_CHAIN_TYPE_EVM, + Id: chainID, + }, + Ocr2Config: &nodev1.OCR2Config{ + OcrKeyBundle: &nodev1.OCR2Config_OCRKeyBundle{ + OffchainPublicKey: "255096a3b7ade10e29c648e0b407fc486180464f713446b1da04f013df6179c8", + OnchainSigningAddress: "8258f4c4761cc445333017608044a204fd0c006a", + ConfigPublicKey: "2c45fec2320f6bcd36444529a86d9f8b4439499a5d8272dec9bcbbebb5e1bf01", + BundleId: "7a9b75510b8d09932b98142419bef52436ff725dd9395469473b487ef87fdfb0", + }, + P2PKeyBundle: &nodev1.OCR2Config_P2PKeyBundle{ + PeerId: peerID2, + }, + IsBootstrap: false, + }, + AccountAddress: "0x415aa1E9a1bcB3929ed92bFa1F9735Dc0D45AD31", + }}, + }, + } + + offchainClient := newFakeOffchainClient(fakeNodes) - // Create test dons with sample data d := &dons{ - c: make(map[string]donConfig), + c: make(map[string]donConfig), + offChain: offchainClient, } - // Add a DON with capabilities and nodes d.c["test-don"] = donConfig{ id: 1, DonCapabilities: keystone_changeset.DonCapabilities{ @@ -68,58 +208,23 @@ func TestToV2ConfigureInput(t *testing.T) { }, } - // Call the method under test - result := d.mustToV2ConfigureInput(123, "0x1234567890abcdef") + result := d.mustToV2ConfigureInput(chainSel, "0x1234567890abcdef", nil) - // Verify the transformation - if result.RegistryChainSel != 123 { - t.Errorf("expected RegistryChainSel 123, got %d", result.RegistryChainSel) - } + require.Equal(t, chainSel, result.RegistryChainSel) - if result.ContractAddress != "0x1234567890abcdef" { //nolint:staticcheck // we won't migrate tests - t.Errorf("expected ContractAddress 0x1234567890abcdef, got %s", result.ContractAddress) //nolint:staticcheck // we won't migrate tests - } + require.Len(t, result.Nops, 1) + require.Equal(t, "test-nop", result.Nops[0].Name) - if len(result.Nops) != 1 { - t.Fatalf("expected 1 NOP, got %d", len(result.Nops)) - } + require.Len(t, result.Nodes, 2) - if result.Nops[0].Name != "test-nop" { - t.Errorf("expected NOP name 'test-nop', got %s", result.Nops[0].Name) - } - - if len(result.Nodes) != 2 { - t.Fatalf("expected 2 nodes, got %d", len(result.Nodes)) - } + require.Len(t, result.Capabilities, 1) + require.Equal(t, "test-capability@1.0.0", result.Capabilities[0].CapabilityID) - if len(result.Capabilities) != 1 { - t.Fatalf("expected 1 capability, got %d", len(result.Capabilities)) - } - - expectedCapID := "test-capability@1.0.0" - if result.Capabilities[0].CapabilityID != expectedCapID { - t.Errorf("expected capability ID '%s', got %s", expectedCapID, result.Capabilities[0].CapabilityID) - } - - if len(result.DONs) != 1 { - t.Fatalf("expected 1 DON, got %d", len(result.DONs)) - } - - if result.DONs[0].Name != "test-don" { - t.Errorf("expected DON name 'test-don', got %s", result.DONs[0].Name) - } - - if result.DONs[0].F != 1 { - t.Errorf("expected DON F value 1, got %d", result.DONs[0].F) - } - - if len(result.DONs[0].Nodes) != 2 { - t.Errorf("expected DON to have 2 nodes, got %d", len(result.DONs[0].Nodes)) - } - - if len(result.DONs[0].CapabilityConfigurations) != 1 { - t.Errorf("expected DON to have 1 capability configuration, got %d", len(result.DONs[0].CapabilityConfigurations)) - } + require.Len(t, result.DONs, 1) + require.Equal(t, "test-don", result.DONs[0].Name) + require.Equal(t, uint8(1), result.DONs[0].F) + require.Len(t, result.DONs[0].Nodes, 2) + require.Len(t, result.DONs[0].CapabilityConfigurations, 1) } // TestGenerateAdminAddresses contains all the test cases for the function. diff --git a/system-tests/lib/cre/environment/environment.go b/system-tests/lib/cre/environment/environment.go index e0096735e28..ed5d77694a8 100644 --- a/system-tests/lib/cre/environment/environment.go +++ b/system-tests/lib/cre/environment/environment.go @@ -26,6 +26,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/framework/components/s3provider" "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/ptr" + "github.com/smartcontractkit/chainlink/deployment/cre/ocr3" keystone_changeset "github.com/smartcontractkit/chainlink/deployment/keystone/changeset" "github.com/smartcontractkit/chainlink/system-tests/lib/cre" crecontracts "github.com/smartcontractkit/chainlink/system-tests/lib/cre/contracts" @@ -188,6 +189,7 @@ func SetupTestEnvironment( fmt.Print(libformat.PurpleText("%s", input.StageGen.Wrap("Applying Features before environment startup"))) var donsCapabilities = make(map[uint64][]keystone_changeset.DONCapabilityWithConfig) + var capabilityToOCR3Config = make(map[string]*ocr3.OracleConfig) for _, feature := range input.Features.List() { for _, donMetadata := range topology.DonsMetadataWithFlag(feature.Flag()) { testLogger.Info().Msgf("Executing PreEnvStartup for feature %s for don '%s'", feature.Flag(), donMetadata.Name) @@ -206,6 +208,7 @@ func SetupTestEnvironment( donsCapabilities[donMetadata.ID] = []keystone_changeset.DONCapabilityWithConfig{} } donsCapabilities[donMetadata.ID] = append(donsCapabilities[donMetadata.ID], output.DONCapabilityWithConfig...) + maps.Copy(capabilityToOCR3Config, output.CapabilityToOCR3Config) } testLogger.Info().Msgf("PreEnvStartup for feature %s executed successfully", feature.Flag()) } @@ -379,6 +382,7 @@ func SetupTestEnvironment( NodeSets: input.NodeSets, WithV2Registries: input.WithV2Registries, DONCapabilityWithConfigs: make(map[uint64][]keystone_changeset.DONCapabilityWithConfig), + CapabilityToOCR3Config: capabilityToOCR3Config, } for _, capability := range input.Capabilities { diff --git a/system-tests/lib/cre/features/consensus/v2/consensus.go b/system-tests/lib/cre/features/consensus/v2/consensus.go index 3a91e5aa995..3572ccac312 100644 --- a/system-tests/lib/cre/features/consensus/v2/consensus.go +++ b/system-tests/lib/cre/features/consensus/v2/consensus.go @@ -20,6 +20,7 @@ import ( cre_jobs "github.com/smartcontractkit/chainlink/deployment/cre/jobs" cre_jobs_ops "github.com/smartcontractkit/chainlink/deployment/cre/jobs/operations" job_types "github.com/smartcontractkit/chainlink/deployment/cre/jobs/types" + "github.com/smartcontractkit/chainlink/deployment/cre/ocr3" "github.com/smartcontractkit/chainlink/deployment/cre/pkg/offchain" keystone_changeset "github.com/smartcontractkit/chainlink/deployment/keystone/changeset" @@ -31,6 +32,7 @@ import ( ) const flag = cre.ConsensusCapabilityV2 +const consensusLabelledName = "consensus" type Consensus struct{} @@ -47,7 +49,7 @@ func (c *Consensus) PreEnvStartup( ) (*cre.PreEnvStartupOutput, error) { capabilities := []keystone_changeset.DONCapabilityWithConfig{{ Capability: kcr.CapabilitiesRegistryCapability{ - LabelledName: "consensus", + LabelledName: consensusLabelledName, Version: "1.0.0-alpha", CapabilityType: 2, // CONSENSUS ResponseType: 0, // REPORT @@ -56,11 +58,13 @@ func (c *Consensus) PreEnvStartup( LocalOnly: don.HasOnlyLocalCapabilities(), }, UseCapRegOCRConfig: true, - OverrideOCR3Config: contracts.DefaultOCR3Config(), }} return &cre.PreEnvStartupOutput{ DONCapabilityWithConfig: capabilities, + CapabilityToOCR3Config: map[string]*ocr3.OracleConfig{ + consensusLabelledName: contracts.DefaultOCR3Config(), + }, }, nil } diff --git a/system-tests/lib/cre/features/evm/v2/evm.go b/system-tests/lib/cre/features/evm/v2/evm.go index 057091300f4..87f03f510f8 100644 --- a/system-tests/lib/cre/features/evm/v2/evm.go +++ b/system-tests/lib/cre/features/evm/v2/evm.go @@ -24,6 +24,7 @@ import ( cre_jobs_ops "github.com/smartcontractkit/chainlink/deployment/cre/jobs/operations" cre_jobs_pkg "github.com/smartcontractkit/chainlink/deployment/cre/jobs/pkg" job_types "github.com/smartcontractkit/chainlink/deployment/cre/jobs/types" + "github.com/smartcontractkit/chainlink/deployment/cre/ocr3" "github.com/smartcontractkit/chainlink/deployment/cre/pkg/offchain" keystone_changeset "github.com/smartcontractkit/chainlink/deployment/keystone/changeset" ks_contracts_op "github.com/smartcontractkit/chainlink/deployment/keystone/changeset/operations/contracts" @@ -122,12 +123,17 @@ func (o *EVM) PreEnvStartup( LocalOnly: don.HasOnlyLocalCapabilities(), }, UseCapRegOCRConfig: true, - OverrideOCR3Config: contracts.DefaultChainCapabilityOCR3Config(), }) } + capabilityToOCR3Config := make(map[string]*ocr3.OracleConfig, len(capabilities)) + for _, cap := range capabilities { + capabilityToOCR3Config[cap.Capability.LabelledName] = contracts.DefaultChainCapabilityOCR3Config() + } + return &cre.PreEnvStartupOutput{ DONCapabilityWithConfig: capabilities, + CapabilityToOCR3Config: capabilityToOCR3Config, }, nil } diff --git a/system-tests/lib/cre/types.go b/system-tests/lib/cre/types.go index f1aede5a318..8853bfb967d 100644 --- a/system-tests/lib/cre/types.go +++ b/system-tests/lib/cre/types.go @@ -25,6 +25,7 @@ import ( coretoml "github.com/smartcontractkit/chainlink/v2/core/config/toml" corechainlink "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" + "github.com/smartcontractkit/chainlink/deployment/cre/ocr3" keystone_changeset "github.com/smartcontractkit/chainlink/deployment/keystone/changeset" "github.com/smartcontractkit/chainlink/system-tests/lib/cre/don/secrets" "github.com/smartcontractkit/chainlink/system-tests/lib/cre/environment/blockchains" @@ -390,6 +391,9 @@ type ConfigureCapabilityRegistryInput struct { WithV2Registries bool DONCapabilityWithConfigs map[uint64][]keystone_changeset.DONCapabilityWithConfig + + // keyed by LabelledName + CapabilityToOCR3Config map[string]*ocr3.OracleConfig } func (c *ConfigureCapabilityRegistryInput) Validate() error { @@ -1605,4 +1609,6 @@ type Feature interface { type PreEnvStartupOutput struct { DONCapabilityWithConfig []keystone_changeset.DONCapabilityWithConfig + // keyed by LabelledName + CapabilityToOCR3Config map[string]*ocr3.OracleConfig }