diff --git a/deployment/ccip/changeset/cs_ccip_home_test.go b/deployment/ccip/changeset/cs_ccip_home_test.go index 92784551957..fbc5d51832a 100644 --- a/deployment/ccip/changeset/cs_ccip_home_test.go +++ b/deployment/ccip/changeset/cs_ccip_home_test.go @@ -177,7 +177,7 @@ func TestActiveCandidate(t *testing.T) { }}, "set new candidates on commit plugin", 0) require.NoError(t, err) setCommitCandidateSigned := commonchangeset.SignProposal(t, e, setCommitCandidateProposal) - commonchangeset.ExecuteProposal(t, e, setCommitCandidateSigned, &commonchangeset.TimelockExecutionContracts{ + commonchangeset.ExecuteProposalTest(t, e, setCommitCandidateSigned, &commonchangeset.TimelockExecutionContracts{ Timelock: state.Chains[tenv.HomeChainSel].Timelock, CallProxy: state.Chains[tenv.HomeChainSel].CallProxy, }, tenv.HomeChainSel) @@ -198,7 +198,7 @@ func TestActiveCandidate(t *testing.T) { }}, "set new candidates on commit and exec plugins", 0) require.NoError(t, err) setExecCandidateSigned := commonchangeset.SignProposal(t, e, setExecCandidateProposal) - commonchangeset.ExecuteProposal(t, e, setExecCandidateSigned, &commonchangeset.TimelockExecutionContracts{ + commonchangeset.ExecuteProposalTest(t, e, setExecCandidateSigned, &commonchangeset.TimelockExecutionContracts{ Timelock: state.Chains[tenv.HomeChainSel].Timelock, CallProxy: state.Chains[tenv.HomeChainSel].CallProxy, }, tenv.HomeChainSel) @@ -235,7 +235,7 @@ func TestActiveCandidate(t *testing.T) { }}, "promote candidates and revoke actives", 0) require.NoError(t, err) promoteSigned := commonchangeset.SignProposal(t, e, promoteProposal) - commonchangeset.ExecuteProposal(t, e, promoteSigned, &commonchangeset.TimelockExecutionContracts{ + commonchangeset.ExecuteProposalTest(t, e, promoteSigned, &commonchangeset.TimelockExecutionContracts{ Timelock: state.Chains[tenv.HomeChainSel].Timelock, CallProxy: state.Chains[tenv.HomeChainSel].CallProxy, }, tenv.HomeChainSel) diff --git a/deployment/common/changeset/mcms_helpers.go b/deployment/common/changeset/mcms_helpers.go new file mode 100644 index 00000000000..2ad9336c877 --- /dev/null +++ b/deployment/common/changeset/mcms_helpers.go @@ -0,0 +1,71 @@ +package changeset + +import ( + "bytes" + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + owner_helpers "github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers" + "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/mcms" + "github.com/smartcontractkit/chainlink/deployment" +) + +// ExecuteProposal executes all the operation in the given executor on the given chain. +// It is an error if there are no operations for the given chain. +func ExecuteProposal(env deployment.Environment, executor *mcms.Executor, timelockContracts *TimelockExecutionContracts, sel uint64) error { + // TODO: This sort of helper probably should move to the MCMS lib. + // Execute all the transactions in the proposal which are for this chain. + if len(executor.Operations[mcms.ChainIdentifier(sel)]) == 0 { + return fmt.Errorf("no operations for chain %d", sel) + } + for _, chainOp := range executor.Operations[mcms.ChainIdentifier(sel)] { + for idx, op := range executor.ChainAgnosticOps { + if bytes.Equal(op.Data, chainOp.Data) && op.To == chainOp.To { + opTx, err2 := executor.ExecuteOnChain(env.Chains[sel].Client, env.Chains[sel].DeployerKey, idx) + if err2 != nil { + return fmt.Errorf("error executing on chain: %w", err2) + } + block, err2 := env.Chains[sel].Confirm(opTx) + if err2 != nil { + return fmt.Errorf("error confirming on chain: %w", err2) + } + it, err2 := timelockContracts.Timelock.FilterCallScheduled(&bind.FilterOpts{ + Start: block, + End: &block, + Context: env.GetContext(), + }, nil, nil) + if err2 != nil { + return fmt.Errorf("error filtering call scheduled: %w", err2) + } + var calls []owner_helpers.RBACTimelockCall + var pred, salt [32]byte + for it.Next() { + // Note these are the same for the whole batch, can overwrite + pred = it.Event.Predecessor + salt = it.Event.Salt + env.Logger.Info("scheduled", "event", it.Event) + calls = append(calls, owner_helpers.RBACTimelockCall{ + Target: it.Event.Target, + Data: it.Event.Data, + Value: it.Event.Value, + }) + } + + timelockExecutorProxy, err := owner_helpers.NewRBACTimelock(timelockContracts.CallProxy.Address(), env.Chains[sel].Client) + if err != nil { + return fmt.Errorf("error creating timelock executor proxy: %w", err) + } + tx, err := timelockExecutorProxy.ExecuteBatch( + env.Chains[sel].DeployerKey, calls, pred, salt) + if err != nil { + return fmt.Errorf("error executing batch: %w", err) + } + _, err = env.Chains[sel].Confirm(tx) + if err != nil { + return fmt.Errorf("error confirming batch: %w", err) + } + } + } + } + return nil +} diff --git a/deployment/common/changeset/mcms_test_helpers.go b/deployment/common/changeset/mcms_test_helpers.go index ffa99114d74..a3f4402d77f 100644 --- a/deployment/common/changeset/mcms_test_helpers.go +++ b/deployment/common/changeset/mcms_test_helpers.go @@ -1,12 +1,9 @@ package changeset import ( - "bytes" - "context" "crypto/ecdsa" "testing" - "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/smartcontractkit/ccip-owner-contracts/pkg/config" @@ -71,7 +68,7 @@ func SignProposal(t *testing.T, env deployment.Environment, proposal *timelock.M return executor } -func ExecuteProposal(t *testing.T, env deployment.Environment, executor *mcms.Executor, +func ExecuteProposalTest(t *testing.T, env deployment.Environment, executor *mcms.Executor, timelockContracts *TimelockExecutionContracts, sel uint64) { t.Log("Executing proposal on chain", sel) // Set the root. @@ -82,42 +79,5 @@ func ExecuteProposal(t *testing.T, env deployment.Environment, executor *mcms.Ex _, err2 = env.Chains[sel].Confirm(tx) require.NoError(t, err2) - // TODO: This sort of helper probably should move to the MCMS lib. - // Execute all the transactions in the proposal which are for this chain. - for _, chainOp := range executor.Operations[mcms.ChainIdentifier(sel)] { - for idx, op := range executor.ChainAgnosticOps { - if bytes.Equal(op.Data, chainOp.Data) && op.To == chainOp.To { - opTx, err3 := executor.ExecuteOnChain(env.Chains[sel].Client, env.Chains[sel].DeployerKey, idx) - require.NoError(t, err3) - block, err3 := env.Chains[sel].Confirm(opTx) - require.NoError(t, err3) - t.Log("executed", chainOp) - it, err3 := timelockContracts.Timelock.FilterCallScheduled(&bind.FilterOpts{ - Start: block, - End: &block, - Context: context.Background(), - }, nil, nil) - require.NoError(t, err3) - var calls []owner_helpers.RBACTimelockCall - var pred, salt [32]byte - for it.Next() { - // Note these are the same for the whole batch, can overwrite - pred = it.Event.Predecessor - salt = it.Event.Salt - t.Log("scheduled", it.Event) - calls = append(calls, owner_helpers.RBACTimelockCall{ - Target: it.Event.Target, - Data: it.Event.Data, - Value: it.Event.Value, - }) - } - timelockExecutorProxy, err := owner_helpers.NewRBACTimelock(timelockContracts.CallProxy.Address(), env.Chains[sel].Client) - tx, err := timelockExecutorProxy.ExecuteBatch( - env.Chains[sel].DeployerKey, calls, pred, salt) - require.NoError(t, err) - _, err = env.Chains[sel].Confirm(tx) - require.NoError(t, err) - } - } - } + require.NoError(t, ExecuteProposal(env, executor, timelockContracts, sel)) } diff --git a/deployment/common/changeset/test_helpers.go b/deployment/common/changeset/test_helpers.go index 8fce5ea79f2..13f93bde939 100644 --- a/deployment/common/changeset/test_helpers.go +++ b/deployment/common/changeset/test_helpers.go @@ -79,7 +79,7 @@ func ApplyChangesets(t *testing.T, e deployment.Environment, timelockContractsPe return deployment.Environment{}, fmt.Errorf("timelock contracts not found for chain %d", sel) } - ExecuteProposal(t, e, signed, timelockContracts, sel) + ExecuteProposalTest(t, e, signed, timelockContracts, sel) } } }