Skip to content

Commit

Permalink
feat: add changeset for set config on all 3 mcms contracts [DPA-1392] (
Browse files Browse the repository at this point in the history
…#15684)

* feat: add changeset for set config on all 3 mcms contracts

* fix: use _test package and change ctx function.

* fix: formatting and comments

* fix: unit tests set config

* fix: check different configs per role.

* fix: pass context and add info logs for success
  • Loading branch information
ecPablo authored Dec 17, 2024
1 parent 30e3a16 commit 2a2293c
Show file tree
Hide file tree
Showing 2 changed files with 522 additions and 0 deletions.
209 changes: 209 additions & 0 deletions deployment/common/changeset/set_config_mcms.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
package changeset

import (
"context"
"errors"
"fmt"
"math/big"
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/config"
"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"
chain_selectors "github.com/smartcontractkit/chain-selectors"

"github.com/smartcontractkit/chainlink-common/pkg/logger"

"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/deployment/common/proposalutils"
commontypes "github.com/smartcontractkit/chainlink/deployment/common/types"
)

type ConfigPerRole struct {
Proposer config.Config
Canceller config.Config
Bypasser config.Config
}
type TimelockConfig struct {
MinDelay time.Duration // delay for timelock worker to execute the transfers.
}

type MCMSConfig struct {
ConfigsPerChain map[uint64]ConfigPerRole
ProposalConfig *TimelockConfig
}

var _ deployment.ChangeSet[MCMSConfig] = SetConfigMCMS

// Validate checks that the MCMSConfig is valid
func (cfg MCMSConfig) Validate(e deployment.Environment, selectors []uint64) error {
if len(cfg.ConfigsPerChain) == 0 {
return errors.New("no chain configs provided")
}
// configs should have at least one chain
state, err := MaybeLoadMCMSWithTimelockState(e, selectors)
if err != nil {
return err
}
for chainSelector, c := range cfg.ConfigsPerChain {
family, err := chain_selectors.GetSelectorFamily(chainSelector)
if err != nil {
return err
}
if family != chain_selectors.FamilyEVM {
return fmt.Errorf("chain selector: %d is not an ethereum chain", chainSelector)
}
_, ok := e.Chains[chainSelector]
if !ok {
return fmt.Errorf("chain selector: %d not found in environment", chainSelector)
}
_, ok = state[chainSelector]
if !ok {
return fmt.Errorf("chain selector: %d not found for MCMS state", chainSelector)
}
if err := c.Proposer.Validate(); err != nil {
return err
}
if err := c.Canceller.Validate(); err != nil {
return err
}
if err := c.Bypasser.Validate(); err != nil {
return err
}
}
return nil
}

// setConfigOrTxData executes set config tx or gets the tx data for the MCMS proposal
func setConfigOrTxData(ctx context.Context, lggr logger.Logger, chain deployment.Chain, cfg config.Config, contract *gethwrappers.ManyChainMultiSig, useMCMS bool) (*types.Transaction, error) {
groupQuorums, groupParents, signerAddresses, signerGroups := cfg.ExtractSetConfigInputs()
opts := deployment.SimTransactOpts()
if !useMCMS {
opts = chain.DeployerKey
}
opts.Context = ctx
tx, err := contract.SetConfig(opts, signerAddresses, signerGroups, groupQuorums, groupParents, false)
if err != nil {
return nil, err
}
if !useMCMS {
_, err = deployment.ConfirmIfNoError(chain, tx, err)
if err != nil {
return nil, err
}
lggr.Infow("SetConfigMCMS tx confirmed", "txHash", tx.Hash().Hex())
}
return tx, nil
}

type setConfigTxs struct {
proposerTx *types.Transaction
cancellerTx *types.Transaction
bypasserTx *types.Transaction
}

// setConfigPerRole sets the configuration for each of the MCMS contract roles on the mcmsState.
func setConfigPerRole(ctx context.Context, lggr logger.Logger, chain deployment.Chain, cfg ConfigPerRole, mcmsState *MCMSWithTimelockState, useMCMS bool) (setConfigTxs, error) {
// Proposer set config
proposerTx, err := setConfigOrTxData(ctx, lggr, chain, cfg.Proposer, mcmsState.ProposerMcm, useMCMS)
if err != nil {
return setConfigTxs{}, err
}
// Canceller set config
cancellerTx, err := setConfigOrTxData(ctx, lggr, chain, cfg.Canceller, mcmsState.CancellerMcm, useMCMS)
if err != nil {
return setConfigTxs{}, err
}
// Bypasser set config
bypasserTx, err := setConfigOrTxData(ctx, lggr, chain, cfg.Bypasser, mcmsState.BypasserMcm, useMCMS)
if err != nil {
return setConfigTxs{}, err
}

return setConfigTxs{
proposerTx: proposerTx,
cancellerTx: cancellerTx,
bypasserTx: bypasserTx,
}, nil
}

func addTxsToProposalBatch(setConfigTxsChain setConfigTxs, chainSelector uint64, state MCMSWithTimelockState) timelock.BatchChainOperation {
result := timelock.BatchChainOperation{
ChainIdentifier: mcms.ChainIdentifier(chainSelector),
Batch: []mcms.Operation{},
}
result.Batch = append(result.Batch, mcms.Operation{
To: state.ProposerMcm.Address(),
Data: setConfigTxsChain.proposerTx.Data(),
Value: big.NewInt(0),
ContractType: string(commontypes.ProposerManyChainMultisig),
})
result.Batch = append(result.Batch, mcms.Operation{
To: state.CancellerMcm.Address(),
Data: setConfigTxsChain.cancellerTx.Data(),
Value: big.NewInt(0),
ContractType: string(commontypes.CancellerManyChainMultisig),
})
result.Batch = append(result.Batch, mcms.Operation{
To: state.BypasserMcm.Address(),
Data: setConfigTxsChain.bypasserTx.Data(),
Value: big.NewInt(0),
ContractType: string(commontypes.BypasserManyChainMultisig),
})
return result
}

// SetConfigMCMS sets the configuration of the MCMS contract on the chain identified by the chainSelector.
func SetConfigMCMS(e deployment.Environment, cfg MCMSConfig) (deployment.ChangesetOutput, error) {
selectors := []uint64{}
lggr := e.Logger
ctx := e.GetContext()
for chainSelector := range cfg.ConfigsPerChain {
selectors = append(selectors, chainSelector)
}
useMCMS := cfg.ProposalConfig != nil
err := cfg.Validate(e, selectors)
if err != nil {
return deployment.ChangesetOutput{}, err
}

batches := []timelock.BatchChainOperation{}
timelocksPerChain := map[uint64]common.Address{}
proposerMcmsPerChain := map[uint64]*gethwrappers.ManyChainMultiSig{}

mcmsStatePerChain, err := MaybeLoadMCMSWithTimelockState(e, selectors)
if err != nil {
return deployment.ChangesetOutput{}, err
}

for chainSelector, c := range cfg.ConfigsPerChain {
chain := e.Chains[chainSelector]
state := mcmsStatePerChain[chainSelector]
timelocksPerChain[chainSelector] = state.Timelock.Address()
proposerMcmsPerChain[chainSelector] = state.ProposerMcm
setConfigTxsChain, err := setConfigPerRole(ctx, lggr, chain, c, state, useMCMS)
if err != nil {
return deployment.ChangesetOutput{}, err
}
if useMCMS {
batch := addTxsToProposalBatch(setConfigTxsChain, chainSelector, *state)
batches = append(batches, batch)
}

}

if useMCMS {
// Create MCMS with timelock proposal
proposal, err := proposalutils.BuildProposalFromBatches(timelocksPerChain, proposerMcmsPerChain, batches, "Set config proposal", cfg.ProposalConfig.MinDelay)
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to build proposal from batch: %w", err)
}
lggr.Infow("SetConfigMCMS proposal created", "proposal", proposal)
return deployment.ChangesetOutput{Proposals: []timelock.MCMSWithTimelockProposal{*proposal}}, nil
}

return deployment.ChangesetOutput{}, nil
}
Loading

0 comments on commit 2a2293c

Please sign in to comment.