Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add changeset for set config on all 3 mcms contracts [DPA-1392] #15684

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
207 changes: 207 additions & 0 deletions deployment/common/changeset/set_config_mcms.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
package changeset

import (
"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/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 ProposalConfig struct {
ecPablo marked this conversation as resolved.
Show resolved Hide resolved
MinDelay time.Duration // delay for timelock worker to execute the transfers.
}

type SetConfigParams struct {
ConfigsPerChain map[uint64]ConfigPerRole
ProposalConfig *ProposalConfig
ecPablo marked this conversation as resolved.
Show resolved Hide resolved
}

var _ deployment.ChangeSet[SetConfigParams] = SetConfigMCMS

// Validate checks that the SetConfigParams is valid
func (cfg SetConfigParams) 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)
ecPablo marked this conversation as resolved.
Show resolved Hide resolved
}
_, 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(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
}
tx, err := contract.SetConfig(opts, signerAddresses, signerGroups, groupQuorums, groupParents, false)
if err != nil {
return nil, err
}
if !useMCMS {
_, err = deployment.ConfirmIfNoError(chain, tx, err)
ecPablo marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return nil, err
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use something like this or this

func ParseErrorFromABI(errorString string, contractABI string) (string, error) {
to return a meaningful error to the user

can be done in a quick follow up

}
}
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(chain deployment.Chain, cfg ConfigPerRole, mcmsState *MCMSWithTimelockState, useMCMS bool) (setConfigTxs, error) {
// Proposer set config
proposerTx, err := setConfigOrTxData(chain, cfg.Proposer, mcmsState.ProposerMcm, useMCMS)
if err != nil {
return setConfigTxs{}, err
}
// Canceller set config
cancellerTx, err := setConfigOrTxData(chain, cfg.Canceller, mcmsState.CancellerMcm, useMCMS)
if err != nil {
return setConfigTxs{}, err
}
// Bypasser set config
bypasserTx, err := setConfigOrTxData(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 {
ecPablo marked this conversation as resolved.
Show resolved Hide resolved
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 SetConfigParams) (deployment.ChangesetOutput, error) {
ecPablo marked this conversation as resolved.
Show resolved Hide resolved
selectors := []uint64{}
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, ok := e.Chains[chainSelector]
if !ok {
return deployment.ChangesetOutput{}, errors.New("chain not found in environment")
ecPablo marked this conversation as resolved.
Show resolved Hide resolved
}
state, ok := mcmsStatePerChain[chainSelector]
if !ok {
return deployment.ChangesetOutput{}, fmt.Errorf("MCMS state not found for chain selector: %d", chainSelector)
ecPablo marked this conversation as resolved.
Show resolved Hide resolved
}
timelocksPerChain[chainSelector] = state.Timelock.Address()
proposerMcmsPerChain[chainSelector] = state.ProposerMcm
setConfigTxsChain, err := setConfigPerRole(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)
}
return deployment.ChangesetOutput{Proposals: []timelock.MCMSWithTimelockProposal{*proposal}}, nil
}

return deployment.ChangesetOutput{}, nil
ecPablo marked this conversation as resolved.
Show resolved Hide resolved
}
Loading
Loading