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

refactor helper to use in cli in CLD #15647

Merged
merged 8 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
121 changes: 121 additions & 0 deletions deployment/common/changeset/mcms_helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package changeset
krehermann marked this conversation as resolved.
Show resolved Hide resolved

import (
"bytes"
"fmt"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
owner_helpers "github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/mcms"
"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/deployment/common/types"
)

// TimelockExecutionContracts is a helper struct for executing timelock proposals. it contains
// the timelock and call proxy contracts.
type TimelockExecutionContracts struct {
Timelock *owner_helpers.RBACTimelock
CallProxy *owner_helpers.CallProxy
}

// NewTimelockExecutionContracts creates a new TimelockExecutionContracts struct.
krehermann marked this conversation as resolved.
Show resolved Hide resolved
// If there are multiple timelocks or call proxy on the chain, an error is returned.
// If there is a missing timelocks or call proxy on the chain, an error is returned.
func NewTimelockExecutionContracts(env deployment.Environment, chainSelector uint64) (*TimelockExecutionContracts, error) {
addrTypeVer, err := env.ExistingAddresses.AddressesForChain(chainSelector)
if err != nil {
return nil, fmt.Errorf("error getting addresses for chain: %w", err)
}
var timelock *owner_helpers.RBACTimelock
var callProxy *owner_helpers.CallProxy
for addr, tv := range addrTypeVer {
if tv.Type == types.RBACTimelock {
if timelock != nil {
return nil, fmt.Errorf("multiple timelocks found on chain %d", chainSelector)
}
var err error
timelock, err = owner_helpers.NewRBACTimelock(common.HexToAddress(addr), env.Chains[chainSelector].Client)
if err != nil {
return nil, fmt.Errorf("error creating timelock: %w", err)
}
}
if tv.Type == types.CallProxy {
if callProxy != nil {
return nil, fmt.Errorf("multiple call proxies found on chain %d", chainSelector)
}
var err error
callProxy, err = owner_helpers.NewCallProxy(common.HexToAddress(addr), env.Chains[chainSelector].Client)
if err != nil {
return nil, fmt.Errorf("error creating call proxy: %w", err)
}
}
}
if timelock == nil || callProxy == nil {
return nil, fmt.Errorf("missing timelock (%T) or call proxy(%T) on chain %d", timelock == nil, callProxy == nil, chainSelector)
}
return &TimelockExecutionContracts{
Timelock: timelock,
CallProxy: callProxy,
}, nil
}

// RunTimelockExecutor 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 RunTimelockExecutor(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)
krehermann marked this conversation as resolved.
Show resolved Hide resolved
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
}
50 changes: 1 addition & 49 deletions deployment/common/changeset/mcms_test_helpers.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
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"
owner_helpers "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"
chainsel "github.com/smartcontractkit/chain-selectors"
Expand All @@ -25,13 +21,6 @@ var (
TestXXXMCMSSigner *ecdsa.PrivateKey
)

// TimelockExecutionContracts is a helper struct for executing timelock proposals. it contains
// the timelock and call proxy contracts.
type TimelockExecutionContracts struct {
Timelock *owner_helpers.RBACTimelock
CallProxy *owner_helpers.CallProxy
}

func init() {
key, err := crypto.GenerateKey()
if err != nil {
Expand Down Expand Up @@ -82,42 +71,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, RunTimelockExecutor(env, executor, timelockContracts, sel))
}
Loading