Skip to content

Commit

Permalink
Remove DON changeset (#15620)
Browse files Browse the repository at this point in the history
* Remove don

* More test

* Simplify a bit

* PR comments

* Fix merge conflicts
  • Loading branch information
connorwstein authored Dec 16, 2024
1 parent 8725bb0 commit ab79b79
Show file tree
Hide file tree
Showing 3 changed files with 231 additions and 1 deletion.
94 changes: 94 additions & 0 deletions deployment/ccip/changeset/cs_home_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}
114 changes: 114 additions & 0 deletions deployment/ccip/changeset/cs_home_chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
}
24 changes: 23 additions & 1 deletion deployment/ccip/changeset/internal/deploy_home_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}

0 comments on commit ab79b79

Please sign in to comment.