From ab79b79904c7b26a6d9970329e5cb6212a95c897 Mon Sep 17 00:00:00 2001 From: Connor Stein Date: Mon, 16 Dec 2024 13:15:30 -0500 Subject: [PATCH] Remove DON changeset (#15620) * Remove don * More test * Simplify a bit * PR comments * Fix merge conflicts --- deployment/ccip/changeset/cs_home_chain.go | 94 +++++++++++++++ .../ccip/changeset/cs_home_chain_test.go | 114 ++++++++++++++++++ .../changeset/internal/deploy_home_chain.go | 24 +++- 3 files changed, 231 insertions(+), 1 deletion(-) diff --git a/deployment/ccip/changeset/cs_home_chain.go b/deployment/ccip/changeset/cs_home_chain.go index 750b21229aa..44658d41016 100644 --- a/deployment/ccip/changeset/cs_home_chain.go +++ b/deployment/ccip/changeset/cs_home_chain.go @@ -5,14 +5,18 @@ import ( "context" "encoding/json" "fmt" + "math/big" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" + "github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers" + "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/mcms" "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" "golang.org/x/exp/maps" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/internal" @@ -322,3 +326,93 @@ func addNodes( _, err = chain.Confirm(tx) return err } + +type RemoveDONsConfig struct { + HomeChainSel uint64 + DonIDs []uint32 + MCMS *MCMSConfig +} + +func (c RemoveDONsConfig) Validate(homeChain CCIPChainState) error { + if err := deployment.IsValidChainSelector(c.HomeChainSel); err != nil { + return fmt.Errorf("home chain selector must be set %w", err) + } + if len(c.DonIDs) == 0 { + return fmt.Errorf("don ids must be set") + } + // Cap reg must exist + if homeChain.CapabilityRegistry == nil { + return fmt.Errorf("cap reg does not exist") + } + if homeChain.CCIPHome == nil { + return fmt.Errorf("ccip home does not exist") + } + if err := internal.DONIdExists(homeChain.CapabilityRegistry, c.DonIDs); err != nil { + return err + } + return nil +} + +// RemoveDONs removes DONs from the CapabilitiesRegistry contract. +// TODO: Could likely be moved to common, but needs +// a common state struct first. +func RemoveDONs(e deployment.Environment, cfg RemoveDONsConfig) (deployment.ChangesetOutput, error) { + state, err := LoadOnchainState(e) + if err != nil { + return deployment.ChangesetOutput{}, err + } + homeChain, ok := e.Chains[cfg.HomeChainSel] + if !ok { + return deployment.ChangesetOutput{}, fmt.Errorf("home chain %d not found", cfg.HomeChainSel) + } + homeChainState := state.Chains[cfg.HomeChainSel] + if err := cfg.Validate(homeChainState); err != nil { + return deployment.ChangesetOutput{}, err + } + txOpts := homeChain.DeployerKey + if cfg.MCMS != nil { + txOpts = deployment.SimTransactOpts() + } + + tx, err := homeChainState.CapabilityRegistry.RemoveDONs(txOpts, cfg.DonIDs) + if err != nil { + return deployment.ChangesetOutput{}, err + } + if cfg.MCMS == nil { + _, err = homeChain.Confirm(tx) + if err != nil { + return deployment.ChangesetOutput{}, err + } + e.Logger.Infof("Removed dons using deployer key tx %s", tx.Hash().String()) + return deployment.ChangesetOutput{}, nil + } + p, err := proposalutils.BuildProposalFromBatches( + map[uint64]common.Address{ + cfg.HomeChainSel: homeChainState.Timelock.Address(), + }, + map[uint64]*gethwrappers.ManyChainMultiSig{ + cfg.HomeChainSel: homeChainState.ProposerMcm, + }, + []timelock.BatchChainOperation{ + { + ChainIdentifier: mcms.ChainIdentifier(cfg.HomeChainSel), + Batch: []mcms.Operation{ + { + To: homeChainState.CapabilityRegistry.Address(), + Data: tx.Data(), + Value: big.NewInt(0), + }, + }, + }, + }, + "Remove DONs", + cfg.MCMS.MinDelay, + ) + if err != nil { + return deployment.ChangesetOutput{}, err + } + e.Logger.Infof("Created proposal to remove dons") + return deployment.ChangesetOutput{Proposals: []timelock.MCMSWithTimelockProposal{ + *p, + }}, nil +} diff --git a/deployment/ccip/changeset/cs_home_chain_test.go b/deployment/ccip/changeset/cs_home_chain_test.go index eb620691db0..8a2d4f87709 100644 --- a/deployment/ccip/changeset/cs_home_chain_test.go +++ b/deployment/ccip/changeset/cs_home_chain_test.go @@ -3,10 +3,13 @@ package changeset import ( "testing" + "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" "github.com/smartcontractkit/chainlink/deployment" + commoncs "github.com/smartcontractkit/chainlink/deployment/common/changeset" + "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" "github.com/smartcontractkit/chainlink/deployment/common/view/v1_0" "github.com/smartcontractkit/chainlink/deployment/environment/memory" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -57,3 +60,114 @@ func TestDeployHomeChain(t *testing.T) { }) require.Len(t, capRegSnap.Nodes, len(p2pIds)) } + +func TestRemoveDonsValidate(t *testing.T) { + e := NewMemoryEnvironment(t) + s, err := LoadOnchainState(e.Env) + require.NoError(t, err) + homeChain := s.Chains[e.HomeChainSel] + var tt = []struct { + name string + config RemoveDONsConfig + expectErr bool + }{ + { + name: "invalid home", + config: RemoveDONsConfig{ + HomeChainSel: 0, + DonIDs: []uint32{1}, + }, + expectErr: true, + }, + { + name: "invalid dons", + config: RemoveDONsConfig{ + HomeChainSel: e.HomeChainSel, + DonIDs: []uint32{1377}, + }, + expectErr: true, + }, + { + name: "no dons", + config: RemoveDONsConfig{ + HomeChainSel: e.HomeChainSel, + DonIDs: []uint32{}, + }, + expectErr: true, + }, + { + name: "success", + config: RemoveDONsConfig{ + HomeChainSel: e.HomeChainSel, + DonIDs: []uint32{1}, + }, + expectErr: false, + }, + } + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + err := tc.config.Validate(homeChain) + if tc.expectErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestRemoveDons(t *testing.T) { + e := NewMemoryEnvironment(t) + s, err := LoadOnchainState(e.Env) + require.NoError(t, err) + homeChain := s.Chains[e.HomeChainSel] + + // Remove a don w/o MCMS + donsBefore, err := homeChain.CapabilityRegistry.GetDONs(nil) + require.NoError(t, err) + e.Env, err = commoncs.ApplyChangesets(t, e.Env, nil, []commoncs.ChangesetApplication{ + { + Changeset: commoncs.WrapChangeSet(RemoveDONs), + Config: RemoveDONsConfig{ + HomeChainSel: e.HomeChainSel, + DonIDs: []uint32{donsBefore[0].Id}, + }, + }, + }) + require.NoError(t, err) + donsAfter, err := homeChain.CapabilityRegistry.GetDONs(nil) + require.NoError(t, err) + require.Len(t, donsAfter, len(donsBefore)-1) + + // Remove a don w/ MCMS + donsBefore, err = homeChain.CapabilityRegistry.GetDONs(nil) + require.NoError(t, err) + e.Env, err = commoncs.ApplyChangesets(t, e.Env, map[uint64]*proposalutils.TimelockExecutionContracts{ + e.HomeChainSel: { + Timelock: s.Chains[e.HomeChainSel].Timelock, + CallProxy: s.Chains[e.HomeChainSel].CallProxy, + }, + }, []commoncs.ChangesetApplication{ + { + Changeset: commoncs.WrapChangeSet(commoncs.TransferToMCMSWithTimelock), + Config: commoncs.TransferToMCMSWithTimelockConfig{ + ContractsByChain: map[uint64][]common.Address{ + e.HomeChainSel: {homeChain.CapabilityRegistry.Address()}, + }, + MinDelay: 0, + }, + }, + { + Changeset: commoncs.WrapChangeSet(RemoveDONs), + Config: RemoveDONsConfig{ + HomeChainSel: e.HomeChainSel, + DonIDs: []uint32{donsBefore[0].Id}, + MCMS: &MCMSConfig{MinDelay: 0}, + }, + }, + }) + require.NoError(t, err) + donsAfter, err = homeChain.CapabilityRegistry.GetDONs(nil) + require.NoError(t, err) + require.Len(t, donsAfter, len(donsBefore)-1) +} diff --git a/deployment/ccip/changeset/internal/deploy_home_chain.go b/deployment/ccip/changeset/internal/deploy_home_chain.go index 5697c0e2f73..e4c97cae326 100644 --- a/deployment/ccip/changeset/internal/deploy_home_chain.go +++ b/deployment/ccip/changeset/internal/deploy_home_chain.go @@ -8,10 +8,11 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-ccip/pluginconfig" "github.com/smartcontractkit/chainlink/deployment" @@ -566,3 +567,24 @@ func BuildOCR3ConfigForCCIPHome( return ocr3Configs, nil } + +func DONIdExists(cr *capabilities_registry.CapabilitiesRegistry, donIDs []uint32) error { + // DON ids must exist + dons, err := cr.GetDONs(nil) + if err != nil { + return fmt.Errorf("failed to get dons: %w", err) + } + for _, donID := range donIDs { + exists := false + for _, don := range dons { + if don.Id == donID { + exists = true + break + } + } + if !exists { + return fmt.Errorf("don id %d does not exist", donID) + } + } + return nil +}