Skip to content

Commit

Permalink
Deploy call proxy instead of using deployer executor keys (#15559)
Browse files Browse the repository at this point in the history
* Deploy call proxy instead of using deployer executor keys

* inject call proxies in execution methods

* skip call proxy when loading chain state

* revert all changes

* Revert "revert all changes"

This reverts commit c17911e.

* consolidate timelocks + callProxies in a single struct

* add comment to tiemlock deploy function

* update go.mod

* go mod tidy

* revert go.mod changes

* go mod tidy

* revert

* go mod tidy

* fix keystone test

* fix ccip tests
  • Loading branch information
akhilchainani authored Dec 10, 2024
1 parent ef0dd1c commit 88a0338
Show file tree
Hide file tree
Showing 22 changed files with 241 additions and 122 deletions.
15 changes: 10 additions & 5 deletions deployment/ccip/changeset/accept_ownership_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers"

commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset"
"github.com/smartcontractkit/chainlink/deployment/environment/memory"
Expand All @@ -29,9 +28,15 @@ func Test_NewAcceptOwnershipChangeset(t *testing.T) {
source := allChains[0]
dest := allChains[1]

timelocks := map[uint64]*gethwrappers.RBACTimelock{
source: state.Chains[source].Timelock,
dest: state.Chains[dest].Timelock,
timelockContracts := map[uint64]*commonchangeset.TimelockExecutionContracts{
source: &commonchangeset.TimelockExecutionContracts{
Timelock: state.Chains[source].Timelock,
CallProxy: state.Chains[source].CallProxy,
},
dest: &commonchangeset.TimelockExecutionContracts{
Timelock: state.Chains[dest].Timelock,
CallProxy: state.Chains[dest].CallProxy,
},
}

// at this point we have the initial deploys done, now we need to transfer ownership
Expand All @@ -40,7 +45,7 @@ func Test_NewAcceptOwnershipChangeset(t *testing.T) {
require.NoError(t, err)

// compose the transfer ownership and accept ownership changesets
_, err = commonchangeset.ApplyChangesets(t, e.Env, timelocks, []commonchangeset.ChangesetApplication{
_, err = commonchangeset.ApplyChangesets(t, e.Env, timelockContracts, []commonchangeset.ChangesetApplication{
// note this doesn't have proposals.
{
Changeset: commonchangeset.WrapChangeSet(commonchangeset.TransferToMCMSWithTimelock),
Expand Down
25 changes: 19 additions & 6 deletions deployment/ccip/changeset/cs_active_candidate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,15 @@ func TestActiveCandidate(t *testing.T) {
ConfirmExecWithSeqNrsForAll(t, e, state, expectedSeqNumExec, startBlocks)

// compose the transfer ownership and accept ownership changesets
timelocks := make(map[uint64]*gethwrappers.RBACTimelock)
timelockContracts := make(map[uint64]*commonchangeset.TimelockExecutionContracts)
for _, chain := range allChains {
timelocks[chain] = state.Chains[chain].Timelock
timelockContracts[chain] = &commonchangeset.TimelockExecutionContracts{
Timelock: state.Chains[chain].Timelock,
CallProxy: state.Chains[chain].CallProxy,
}
}
_, err = commonchangeset.ApplyChangesets(t, e, timelocks, []commonchangeset.ChangesetApplication{

_, err = commonchangeset.ApplyChangesets(t, e, timelockContracts, []commonchangeset.ChangesetApplication{
// note this doesn't have proposals.
{
Changeset: commonchangeset.WrapChangeSet(commonchangeset.TransferToMCMSWithTimelock),
Expand Down Expand Up @@ -177,7 +181,10 @@ 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, state.Chains[tenv.HomeChainSel].Timelock, tenv.HomeChainSel)
commonchangeset.ExecuteProposal(t, e, setCommitCandidateSigned, &commonchangeset.TimelockExecutionContracts{
Timelock: state.Chains[tenv.HomeChainSel].Timelock,
CallProxy: state.Chains[tenv.HomeChainSel].CallProxy,
}, tenv.HomeChainSel)

// create the op for the commit plugin as well
setExecCandidateOp, err := setCandidateOnExistingDon(
Expand All @@ -195,7 +202,10 @@ 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, state.Chains[tenv.HomeChainSel].Timelock, tenv.HomeChainSel)
commonchangeset.ExecuteProposal(t, e, setExecCandidateSigned, &commonchangeset.TimelockExecutionContracts{
Timelock: state.Chains[tenv.HomeChainSel].Timelock,
CallProxy: state.Chains[tenv.HomeChainSel].CallProxy,
}, tenv.HomeChainSel)

// check setup was successful by confirming number of nodes from cap reg
donInfo, err = state.Chains[tenv.HomeChainSel].CapabilityRegistry.GetDON(nil, donID)
Expand All @@ -222,7 +232,10 @@ 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, state.Chains[tenv.HomeChainSel].Timelock, tenv.HomeChainSel)
commonchangeset.ExecuteProposal(t, e, promoteSigned, &commonchangeset.TimelockExecutionContracts{
Timelock: state.Chains[tenv.HomeChainSel].Timelock,
CallProxy: state.Chains[tenv.HomeChainSel].CallProxy,
}, tenv.HomeChainSel)
// [NEW ACTIVE, NO CANDIDATE] done promoting

// [NEW ACTIVE, NO CANDIDATE] check onchain state
Expand Down
40 changes: 26 additions & 14 deletions deployment/ccip/changeset/cs_add_chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import (
"testing"
"time"

"github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers"

"github.com/smartcontractkit/chainlink/deployment/ccip/changeset/internal"
commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset"
commontypes "github.com/smartcontractkit/chainlink/deployment/common/types"
Expand Down Expand Up @@ -52,11 +50,10 @@ func TestAddChainInbound(t *testing.T) {
require.NoError(t, e.Env.ExistingAddresses.Merge(newAddresses))

cfg := commontypes.MCMSWithTimelockConfig{
Canceller: commonchangeset.SingleGroupMCMS(t),
Bypasser: commonchangeset.SingleGroupMCMS(t),
Proposer: commonchangeset.SingleGroupMCMS(t),
TimelockExecutors: e.Env.AllDeployerKeys(),
TimelockMinDelay: big.NewInt(0),
Canceller: commonchangeset.SingleGroupMCMS(t),
Bypasser: commonchangeset.SingleGroupMCMS(t),
Proposer: commonchangeset.SingleGroupMCMS(t),
TimelockMinDelay: big.NewInt(0),
}
e.Env, err = commonchangeset.ApplyChangesets(t, e.Env, nil, []commonchangeset.ChangesetApplication{
{
Expand Down Expand Up @@ -158,10 +155,19 @@ func TestAddChainInbound(t *testing.T) {
}

// transfer ownership to timelock
_, err = commonchangeset.ApplyChangesets(t, e.Env, map[uint64]*gethwrappers.RBACTimelock{
initialDeploy[0]: state.Chains[initialDeploy[0]].Timelock,
initialDeploy[1]: state.Chains[initialDeploy[1]].Timelock,
initialDeploy[2]: state.Chains[initialDeploy[2]].Timelock,
_, err = commonchangeset.ApplyChangesets(t, e.Env, map[uint64]*commonchangeset.TimelockExecutionContracts{
initialDeploy[0]: &commonchangeset.TimelockExecutionContracts{
Timelock: state.Chains[initialDeploy[0]].Timelock,
CallProxy: state.Chains[initialDeploy[0]].CallProxy,
},
initialDeploy[1]: &commonchangeset.TimelockExecutionContracts{
Timelock: state.Chains[initialDeploy[1]].Timelock,
CallProxy: state.Chains[initialDeploy[1]].CallProxy,
},
initialDeploy[2]: &commonchangeset.TimelockExecutionContracts{
Timelock: state.Chains[initialDeploy[2]].Timelock,
CallProxy: state.Chains[initialDeploy[2]].CallProxy,
},
}, []commonchangeset.ChangesetApplication{
{
Changeset: commonchangeset.WrapChangeSet(commonchangeset.TransferToMCMSWithTimelock),
Expand Down Expand Up @@ -191,9 +197,15 @@ func TestAddChainInbound(t *testing.T) {
nodeIDs = append(nodeIDs, node.NodeID)
}

_, err = commonchangeset.ApplyChangesets(t, e.Env, map[uint64]*gethwrappers.RBACTimelock{
e.HomeChainSel: state.Chains[e.HomeChainSel].Timelock,
newChain: state.Chains[newChain].Timelock,
_, err = commonchangeset.ApplyChangesets(t, e.Env, map[uint64]*commonchangeset.TimelockExecutionContracts{
e.HomeChainSel: &commonchangeset.TimelockExecutionContracts{
Timelock: state.Chains[e.HomeChainSel].Timelock,
CallProxy: state.Chains[e.HomeChainSel].CallProxy,
},
newChain: &commonchangeset.TimelockExecutionContracts{
Timelock: state.Chains[newChain].Timelock,
CallProxy: state.Chains[newChain].CallProxy,
},
}, []commonchangeset.ChangesetApplication{
{
Changeset: commonchangeset.WrapChangeSet(AddDonAndSetCandidateChangeset),
Expand Down
9 changes: 4 additions & 5 deletions deployment/ccip/changeset/cs_deploy_chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,10 @@ func TestDeployChainContractsChangeset(t *testing.T) {
cfg := make(map[uint64]commontypes.MCMSWithTimelockConfig)
for _, chain := range e.AllChainSelectors() {
cfg[chain] = commontypes.MCMSWithTimelockConfig{
Canceller: commonchangeset.SingleGroupMCMS(t),
Bypasser: commonchangeset.SingleGroupMCMS(t),
Proposer: commonchangeset.SingleGroupMCMS(t),
TimelockExecutors: e.AllDeployerKeys(),
TimelockMinDelay: big.NewInt(0),
Canceller: commonchangeset.SingleGroupMCMS(t),
Bypasser: commonchangeset.SingleGroupMCMS(t),
Proposer: commonchangeset.SingleGroupMCMS(t),
TimelockMinDelay: big.NewInt(0),
}
}
e, err = commonchangeset.ApplyChangesets(t, e, nil, []commonchangeset.ChangesetApplication{
Expand Down
13 changes: 8 additions & 5 deletions deployment/ccip/changeset/cs_update_rmn_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers"
mcmsWrappers "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"
"github.com/smartcontractkit/chainlink/deployment"
commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset"
"github.com/smartcontractkit/chainlink/deployment/common/proposalutils"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_home"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_remote"
Expand Down Expand Up @@ -274,10 +274,13 @@ func NewPromoteCandidateConfigChangeset(e deployment.Environment, config Promote
}, nil
}

func buildTimelockPerChain(e deployment.Environment, state CCIPOnChainState) map[uint64]*mcmsWrappers.RBACTimelock {
timelocksPerChain := make(map[uint64]*mcmsWrappers.RBACTimelock)
func buildTimelockPerChain(e deployment.Environment, state CCIPOnChainState) map[uint64]*commonchangeset.TimelockExecutionContracts {
timelocksPerChain := make(map[uint64]*commonchangeset.TimelockExecutionContracts)
for _, chain := range e.Chains {
timelocksPerChain[chain.Selector] = state.Chains[chain.Selector].Timelock
timelocksPerChain[chain.Selector] = &commonchangeset.TimelockExecutionContracts{
Timelock: state.Chains[chain.Selector].Timelock,
CallProxy: state.Chains[chain.Selector].CallProxy,
}
}
return timelocksPerChain
}
Expand All @@ -286,7 +289,7 @@ func buildTimelockAddressPerChain(e deployment.Environment, state CCIPOnChainSta
timelocksPerChain := buildTimelockPerChain(e, state)
timelockAddressPerChain := make(map[uint64]common.Address)
for chain, timelock := range timelocksPerChain {
timelockAddressPerChain[chain] = timelock.Address()
timelockAddressPerChain[chain] = timelock.Timelock.Address()
}
return timelockAddressPerChain
}
Expand Down
1 change: 1 addition & 0 deletions deployment/ccip/changeset/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ func LoadChainState(chain deployment.Chain, addresses map[string]deployment.Type
for address, tvStr := range addresses {
switch tvStr.String() {
case deployment.NewTypeAndVersion(commontypes.RBACTimelock, deployment.Version1_0_0).String(),
deployment.NewTypeAndVersion(commontypes.CallProxy, deployment.Version1_0_0).String(),
deployment.NewTypeAndVersion(commontypes.ProposerManyChainMultisig, deployment.Version1_0_0).String(),
deployment.NewTypeAndVersion(commontypes.CancellerManyChainMultisig, deployment.Version1_0_0).String(),
deployment.NewTypeAndVersion(commontypes.BypasserManyChainMultisig, deployment.Version1_0_0).String(),
Expand Down
24 changes: 14 additions & 10 deletions deployment/ccip/changeset/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/pkg/errors"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers"

"github.com/smartcontractkit/chainlink-ccip/pluginconfig"
commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config"
Expand Down Expand Up @@ -279,11 +278,10 @@ func NewMemoryEnvironmentWithJobsAndContracts(t *testing.T, lggr logger.Logger,
mcmsCfg := make(map[uint64]commontypes.MCMSWithTimelockConfig)
for _, c := range e.Env.AllChainSelectors() {
mcmsCfg[c] = commontypes.MCMSWithTimelockConfig{
Canceller: commonchangeset.SingleGroupMCMS(t),
Bypasser: commonchangeset.SingleGroupMCMS(t),
Proposer: commonchangeset.SingleGroupMCMS(t),
TimelockExecutors: e.Env.AllDeployerKeys(),
TimelockMinDelay: big.NewInt(0),
Canceller: commonchangeset.SingleGroupMCMS(t),
Bypasser: commonchangeset.SingleGroupMCMS(t),
Proposer: commonchangeset.SingleGroupMCMS(t),
TimelockMinDelay: big.NewInt(0),
}
}
var (
Expand Down Expand Up @@ -366,14 +364,17 @@ func NewMemoryEnvironmentWithJobsAndContracts(t *testing.T, lggr logger.Logger,
}
// Build the per chain config.
chainConfigs := make(map[uint64]CCIPOCRParams)
timelocksPerChain := make(map[uint64]*gethwrappers.RBACTimelock)
timelockContractsPerChain := make(map[uint64]*commonchangeset.TimelockExecutionContracts)
for _, chain := range allChains {
timelocksPerChain[chain] = state.Chains[chain].Timelock
timelockContractsPerChain[chain] = &commonchangeset.TimelockExecutionContracts{
Timelock: state.Chains[chain].Timelock,
CallProxy: state.Chains[chain].CallProxy,
}
tokenInfo := tokenConfig.GetTokenInfo(e.Env.Logger, state.Chains[chain].LinkToken, state.Chains[chain].Weth9)
chainConfigs[chain] = DefaultOCRParams(e.FeedChainSel, tokenInfo, tokenDataProviders)
}
// Deploy second set of changesets to deploy and configure the CCIP contracts.
e.Env, err = commonchangeset.ApplyChangesets(t, e.Env, timelocksPerChain, []commonchangeset.ChangesetApplication{
e.Env, err = commonchangeset.ApplyChangesets(t, e.Env, timelockContractsPerChain, []commonchangeset.ChangesetApplication{
{
Changeset: commonchangeset.WrapChangeSet(ConfigureNewChains),
Config: NewChainsConfig{
Expand Down Expand Up @@ -822,7 +823,10 @@ func ProcessChangeset(t *testing.T, e deployment.Environment, c deployment.Chang

signed := commonchangeset.SignProposal(t, e, &prop)
for _, sel := range chains.ToSlice() {
commonchangeset.ExecuteProposal(t, e, signed, state.Chains[sel].Timelock, sel)
commonchangeset.ExecuteProposal(t, e, signed, &commonchangeset.TimelockExecutionContracts{
Timelock: state.Chains[sel].Timelock,
CallProxy: state.Chains[sel].CallProxy,
}, sel)
}
}
}
Expand Down
43 changes: 39 additions & 4 deletions deployment/common/changeset/internal/mcms.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type MCMSWithTimelockDeploy struct {
Bypasser *deployment.ContractDeploy[*owner_helpers.ManyChainMultiSig]
Proposer *deployment.ContractDeploy[*owner_helpers.ManyChainMultiSig]
Timelock *deployment.ContractDeploy[*owner_helpers.RBACTimelock]
CallProxy *deployment.ContractDeploy[*owner_helpers.CallProxy]
}

func DeployMCMSWithTimelockContractsBatch(
Expand Down Expand Up @@ -106,10 +107,12 @@ func DeployMCMSWithTimelockContracts(
// TODO: Could expose this as config?
// Or keep this enforced to follow the same pattern?
chain.DeployerKey.From,
[]common.Address{proposer.Address}, // proposers
config.TimelockExecutors, //executors
[]common.Address{canceller.Address}, // cancellers
[]common.Address{bypasser.Address}, // bypassers
[]common.Address{proposer.Address}, // proposers
// Executors field is empty here because we grant the executor role to the call proxy later
// and the call proxy cannot be deployed before the timelock.
[]common.Address{},
[]common.Address{canceller.Address, proposer.Address, bypasser.Address}, // cancellers
[]common.Address{bypasser.Address}, // bypassers
)
return deployment.ContractDeploy[*owner_helpers.RBACTimelock]{
timelock, cc, tx2, deployment.NewTypeAndVersion(types.RBACTimelock, deployment.Version1_0_0), err2,
Expand All @@ -119,6 +122,37 @@ func DeployMCMSWithTimelockContracts(
lggr.Errorw("Failed to deploy timelock", "chain", chain.String(), "err", err)
return nil, err
}

callProxy, err := deployment.DeployContract(lggr, chain, ab,
func(chain deployment.Chain) deployment.ContractDeploy[*owner_helpers.CallProxy] {
callProxy, tx2, cc, err2 := owner_helpers.DeployCallProxy(
chain.DeployerKey,
chain.Client,
timelock.Address,
)
return deployment.ContractDeploy[*owner_helpers.CallProxy]{
callProxy, cc, tx2, deployment.NewTypeAndVersion(types.CallProxy, deployment.Version1_0_0), err2,
}
})
if err != nil {
lggr.Errorw("Failed to deploy call proxy", "chain", chain.String(), "err", err)
return nil, err
}

grantRoleTx, err := timelock.Contract.GrantRole(
chain.DeployerKey,
v1_0.EXECUTOR_ROLE.ID,
callProxy.Address,
)
if err != nil {
lggr.Errorw("Failed to grant timelock executor role", "chain", chain.String(), "err", err)
return nil, err
}

if _, err := deployment.ConfirmIfNoError(chain, grantRoleTx, err); err != nil {
lggr.Errorw("Failed to grant timelock executor role", "chain", chain.String(), "err", err)
return nil, err
}
// We grant the timelock the admin role on the MCMS contracts.
tx, err := timelock.Contract.GrantRole(chain.DeployerKey,
v1_0.ADMIN_ROLE.ID, timelock.Address)
Expand All @@ -133,5 +167,6 @@ func DeployMCMSWithTimelockContracts(
Bypasser: bypasser,
Proposer: proposer,
Timelock: timelock,
CallProxy: callProxy,
}, nil
}
12 changes: 4 additions & 8 deletions deployment/common/changeset/internal/mcms_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
chainsel "github.com/smartcontractkit/chain-selectors"
"github.com/stretchr/testify/require"

Expand Down Expand Up @@ -37,18 +36,15 @@ func TestDeployMCMSWithTimelockContracts(t *testing.T) {
_, err := internal.DeployMCMSWithTimelockContracts(lggr,
chains[chainsel.TEST_90000001.Selector],
ab, types.MCMSWithTimelockConfig{
Canceller: changeset.SingleGroupMCMS(t),
Bypasser: changeset.SingleGroupMCMS(t),
Proposer: changeset.SingleGroupMCMS(t),
TimelockExecutors: []common.Address{
chains[chainsel.TEST_90000001.Selector].DeployerKey.From,
},
Canceller: changeset.SingleGroupMCMS(t),
Bypasser: changeset.SingleGroupMCMS(t),
Proposer: changeset.SingleGroupMCMS(t),
TimelockMinDelay: big.NewInt(0),
})
require.NoError(t, err)
addresses, err := ab.AddressesForChain(chainsel.TEST_90000001.Selector)
require.NoError(t, err)
require.Len(t, addresses, 4)
require.Len(t, addresses, 5)
mcmsState, err := changeset.MaybeLoadMCMSWithTimelockState(chains[chainsel.TEST_90000001.Selector], addresses)
require.NoError(t, err)
v, err := mcmsState.GenerateMCMSWithTimelockView()
Expand Down
Loading

0 comments on commit 88a0338

Please sign in to comment.