From 9682e416c5d97732660e570a8d9071b46164d176 Mon Sep 17 00:00:00 2001 From: Michael Street <5597260+MStreet3@users.noreply.github.com> Date: Mon, 23 Dec 2024 15:31:50 -0500 Subject: [PATCH] feat(keystone): enable multiple OCR contracts per chain --- .../keystone/changeset/configure_contracts.go | 2 ++ deployment/keystone/changeset/deploy_ocr3.go | 2 ++ .../keystone/changeset/internal/deploy.go | 31 ++++++++++++++----- .../keystone/changeset/internal/state.go | 11 +++++-- deployment/keystone/changeset/test/helpers.go | 13 ++++++++ 5 files changed, 49 insertions(+), 10 deletions(-) diff --git a/deployment/keystone/changeset/configure_contracts.go b/deployment/keystone/changeset/configure_contracts.go index b57ebb0ed52..97b0f15d3e0 100644 --- a/deployment/keystone/changeset/configure_contracts.go +++ b/deployment/keystone/changeset/configure_contracts.go @@ -15,6 +15,7 @@ var _ deployment.ChangeSet[InitialContractsCfg] = ConfigureInitialContractsChang type InitialContractsCfg struct { RegistryChainSel uint64 Dons []kslib.DonCapabilities + OCR3Address string // Address of the OCR3 contract on the registry chain to configure OCR3Config *kslib.OracleConfig } @@ -23,6 +24,7 @@ func ConfigureInitialContractsChangeset(e deployment.Environment, cfg InitialCon Env: &e, RegistryChainSel: cfg.RegistryChainSel, Dons: cfg.Dons, + OCR3Address: cfg.OCR3Address, OCR3Config: cfg.OCR3Config, } return ConfigureInitialContracts(e.Logger, req) diff --git a/deployment/keystone/changeset/deploy_ocr3.go b/deployment/keystone/changeset/deploy_ocr3.go index 4e85590e521..67f13054ce2 100644 --- a/deployment/keystone/changeset/deploy_ocr3.go +++ b/deployment/keystone/changeset/deploy_ocr3.go @@ -37,6 +37,7 @@ var _ deployment.ChangeSet[ConfigureOCR3Config] = ConfigureOCR3Contract type ConfigureOCR3Config struct { ChainSel uint64 NodeIDs []string + Address string // hex encoded address of the OCR3 contract to configure OCR3Config *kslib.OracleConfig DryRun bool WriteGeneratedConfig io.Writer // if not nil, write the generated config to this writer as JSON [OCR2OracleConfig] @@ -54,6 +55,7 @@ func ConfigureOCR3Contract(env deployment.Environment, cfg ConfigureOCR3Config) ChainSel: cfg.ChainSel, NodeIDs: cfg.NodeIDs, OCR3Config: cfg.OCR3Config, + Address: cfg.Address, DryRun: cfg.DryRun, UseMCMS: cfg.UseMCMS(), }) diff --git a/deployment/keystone/changeset/internal/deploy.go b/deployment/keystone/changeset/internal/deploy.go index 5afcae11e93..3c28a1bfa09 100644 --- a/deployment/keystone/changeset/internal/deploy.go +++ b/deployment/keystone/changeset/internal/deploy.go @@ -30,6 +30,7 @@ import ( capabilities_registry "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry_1_1_0" kf "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/forwarder_1_0_0" + ocr3_capability "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/ocr3_capability_1_0_0" "github.com/smartcontractkit/chainlink-common/pkg/logger" ) @@ -38,8 +39,9 @@ type ConfigureContractsRequest struct { RegistryChainSel uint64 Env *deployment.Environment - Dons []DonCapabilities // externally sourced based on the environment - OCR3Config *OracleConfig // TODO: probably should be a map of don to config; but currently we only have one wf don therefore one config + Dons []DonCapabilities // externally sourced based on the environment + OCR3Address string // Address of the OCR3 contract on the registry chain to configure + OCR3Config *OracleConfig // TODO: probably should be a map of don to config; but currently we only have one wf don therefore one config // TODO rm this option; unused DoContractDeploy bool // if false, the contracts are assumed to be deployed and the address book is used @@ -99,7 +101,7 @@ func ConfigureContracts(ctx context.Context, lggr logger.Logger, req ConfigureCo return nil, fmt.Errorf("failed to configure forwarder contracts: %w", err) } - err = ConfigureOCR3Contract(req.Env, req.RegistryChainSel, dons, req.OCR3Config) + err = ConfigureOCR3Contract(req.Env, req.RegistryChainSel, req.OCR3Address, dons, req.OCR3Config) if err != nil { return nil, fmt.Errorf("failed to configure OCR3 contract: %w", err) } @@ -298,7 +300,7 @@ func ConfigureRegistry(ctx context.Context, lggr logger.Logger, req ConfigureCon // Depreciated: use changeset.ConfigureOCR3Contract instead // ocr3 contract on the registry chain for the wf dons -func ConfigureOCR3Contract(env *deployment.Environment, chainSel uint64, dons []RegisteredDon, cfg *OracleConfig) error { +func ConfigureOCR3Contract(env *deployment.Environment, chainSel uint64, ocr3Addr string, dons []RegisteredDon, cfg *OracleConfig) error { registryChain, ok := env.Chains[chainSel] if !ok { return fmt.Errorf("chain %d not found in environment", chainSel) @@ -321,9 +323,14 @@ func ConfigureOCR3Contract(env *deployment.Environment, chainSel uint64, dons [] if !ok { return fmt.Errorf("failed to get contract set for chain %d", chainSel) } - contract := contracts.OCR3 + + var contract *ocr3_capability.OCR3Capability + if contract, ok = contracts.OCR3[ocr3Addr]; !ok { + return fmt.Errorf("OCR contract address %s not found in contract set", ocr3Addr) + } + if contract == nil { - return fmt.Errorf("no ocr3 contract found for chain %d", chainSel) + return fmt.Errorf("no ocr3 contract found for chain %d at %s", chainSel, ocr3Addr) } _, err := configureOCR3contract(configureOCR3Request{ @@ -349,6 +356,7 @@ type ConfigureOCR3Resp struct { type ConfigureOCR3Config struct { ChainSel uint64 NodeIDs []string + Address string // hex encoded address of the OCR3 contract to configure OCR3Config *OracleConfig DryRun bool @@ -377,7 +385,16 @@ func ConfigureOCR3ContractFromJD(env *deployment.Environment, cfg ConfigureOCR3C if !ok { return nil, fmt.Errorf("failed to get contract set for chain %d", cfg.ChainSel) } - contract := contracts.OCR3 + + // get the OCR3 contract + if cfg.Address == "" { + return nil, errors.New("OCR contract address is unspecified") + } + + var contract *ocr3_capability.OCR3Capability + if contract, ok = contracts.OCR3[cfg.Address]; !ok { + return nil, fmt.Errorf("OCR contract address %s not found in contract set", cfg.Address) + } if contract == nil { return nil, fmt.Errorf("no ocr3 contract found for chain %d", cfg.ChainSel) } diff --git a/deployment/keystone/changeset/internal/state.go b/deployment/keystone/changeset/internal/state.go index d0817069d9a..676941d0d27 100644 --- a/deployment/keystone/changeset/internal/state.go +++ b/deployment/keystone/changeset/internal/state.go @@ -28,7 +28,7 @@ type GetContractSetsResponse struct { type ContractSet struct { commonchangeset.MCMSWithTimelockState - OCR3 *ocr3_capability.OCR3Capability + OCR3 map[string]*ocr3_capability.OCR3Capability Forwarder *forwarder.KeystoneForwarder CapabilitiesRegistry *capabilities_registry.CapabilitiesRegistry WorkflowRegistry *workflow_registry.WorkflowRegistry @@ -37,7 +37,9 @@ type ContractSet struct { func (cs ContractSet) TransferableContracts() []common.Address { var out []common.Address if cs.OCR3 != nil { - out = append(out, cs.OCR3.Address()) + for _, ocr := range cs.OCR3 { + out = append(out, ocr.Address()) + } } if cs.Forwarder != nil { out = append(out, cs.Forwarder.Address()) @@ -109,7 +111,10 @@ func loadContractSet(lggr logger.Logger, chain deployment.Chain, addresses map[s if err != nil { return nil, fmt.Errorf("failed to create OCR3Capability contract from address %s: %w", addr, err) } - out.OCR3 = c + if out.OCR3 == nil { + out.OCR3 = make(map[string]*ocr3_capability.OCR3Capability) + } + out.OCR3[addr] = c case WorkflowRegistry: c, err := workflow_registry.NewWorkflowRegistry(common.HexToAddress(addr), chain.Client) if err != nil { diff --git a/deployment/keystone/changeset/test/helpers.go b/deployment/keystone/changeset/test/helpers.go index 5ddaeda524e..7e25be4c5fe 100644 --- a/deployment/keystone/changeset/test/helpers.go +++ b/deployment/keystone/changeset/test/helpers.go @@ -195,6 +195,18 @@ func SetupTestEnv(t *testing.T, c TestConfig) TestEnv { err = env.ExistingAddresses.Merge(e.ExistingAddresses) require.NoError(t, err) + // Get the OCR3 address from the address book + var ocr3Addr string + addrs, err := env.ExistingAddresses.AddressesForChain(registryChainSel) + require.NoError(t, err) + + for k, tv := range addrs { + if tv.Type == internal.OCR3Capability { + ocr3Addr = k + break + } + } + var ocr3Config = internal.OracleConfig{ MaxFaultyOracles: len(wfNodes) / 3, } @@ -203,6 +215,7 @@ func SetupTestEnv(t *testing.T, c TestConfig) TestEnv { csOut, err := kschangeset.ConfigureInitialContractsChangeset(env, kschangeset.InitialContractsCfg{ RegistryChainSel: registryChainSel, Dons: allDons, + OCR3Address: ocr3Addr, // Address of the OCR3 contract on the registry chain to configure OCR3Config: &ocr3Config, }) require.NoError(t, err)