-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add changeset for set config on all 3 mcms contracts [DPA-1392] (…
…#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
Showing
2 changed files
with
522 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
Oops, something went wrong.