Skip to content

Commit

Permalink
CCIP-4158 chain contract changeset (#15294)
Browse files Browse the repository at this point in the history
* initial deploy chain

* more updates

* rename

* fix DeployChainContracts
  • Loading branch information
AnieeG authored Nov 19, 2024
1 parent 3cadac2 commit c95cd54
Show file tree
Hide file tree
Showing 10 changed files with 265 additions and 63 deletions.
44 changes: 44 additions & 0 deletions deployment/ccip/changeset/deploy_chain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package changeset

import (
"fmt"

"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock"

"github.com/smartcontractkit/chainlink/deployment"
ccipdeployment "github.com/smartcontractkit/chainlink/deployment/ccip"
)

var _ deployment.ChangeSet[DeployChainContractsConfig] = DeployChainContracts

func DeployChainContracts(env deployment.Environment, c DeployChainContractsConfig) (deployment.ChangesetOutput, error) {
newAddresses := deployment.NewMemoryAddressBook()
err := ccipdeployment.DeployChainContractsForChains(env, newAddresses, c.HomeChainSelector, c.ChainSelectors, c.MCMSCfg)
if err != nil {
env.Logger.Errorw("Failed to deploy CCIP contracts", "err", err, "newAddresses", newAddresses)
return deployment.ChangesetOutput{AddressBook: newAddresses}, deployment.MaybeDataErr(err)
}
return deployment.ChangesetOutput{
Proposals: []timelock.MCMSWithTimelockProposal{},
AddressBook: newAddresses,
JobSpecs: nil,
}, nil
}

type DeployChainContractsConfig struct {
ChainSelectors []uint64
HomeChainSelector uint64
MCMSCfg ccipdeployment.MCMSConfig
}

func (c DeployChainContractsConfig) Validate() error {
for _, cs := range c.ChainSelectors {
if err := deployment.IsValidChainSelector(cs); err != nil {
return fmt.Errorf("invalid chain selector: %d - %w", cs, err)
}
}
if err := deployment.IsValidChainSelector(c.HomeChainSelector); err != nil {
return fmt.Errorf("invalid home chain selector: %d - %w", c.HomeChainSelector, err)
}
return nil
}
78 changes: 78 additions & 0 deletions deployment/ccip/changeset/deploy_chain_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package changeset

import (
"testing"

"github.com/stretchr/testify/require"
"go.uber.org/zap/zapcore"

"github.com/smartcontractkit/chainlink/deployment"
ccdeploy "github.com/smartcontractkit/chainlink/deployment/ccip"
"github.com/smartcontractkit/chainlink/deployment/environment/memory"
"github.com/smartcontractkit/chainlink/v2/core/logger"
)

func TestDeployChainContractsChangeset(t *testing.T) {
lggr := logger.TestLogger(t)
e := memory.NewMemoryEnvironment(t, lggr, zapcore.InfoLevel, memory.MemoryEnvironmentConfig{
Bootstraps: 1,
Chains: 2,
Nodes: 4,
})
selectors := e.AllChainSelectors()
homeChainSel := selectors[0]
nodes, err := deployment.NodeInfo(e.NodeIDs, e.Offchain)
require.NoError(t, err)
p2pIds := nodes.NonBootstraps().PeerIDs()
// deploy home chain
homeChainCfg := DeployHomeChainConfig{
HomeChainSel: homeChainSel,
RMNStaticConfig: ccdeploy.NewTestRMNStaticConfig(),
RMNDynamicConfig: ccdeploy.NewTestRMNDynamicConfig(),
NodeOperators: ccdeploy.NewTestNodeOperator(e.Chains[homeChainSel].DeployerKey.From),
NodeP2PIDsPerNodeOpAdmin: map[string][][32]byte{
"NodeOperator": p2pIds,
},
}
output, err := DeployHomeChain(e, homeChainCfg)
require.NoError(t, err)
require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook))

// deploy pre-requisites
prerequisites, err := DeployPrerequisites(e, DeployPrerequisiteConfig{
ChainSelectors: selectors,
})
require.NoError(t, err)
require.NoError(t, e.ExistingAddresses.Merge(prerequisites.AddressBook))

// deploy ccip chain contracts
output, err = DeployChainContracts(e, DeployChainContractsConfig{
ChainSelectors: selectors,
HomeChainSelector: homeChainSel,
MCMSCfg: ccdeploy.NewTestMCMSConfig(t, e),
})
require.NoError(t, err)
require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook))

// load onchain state
state, err := ccdeploy.LoadOnchainState(e)
require.NoError(t, err)

// verify all contracts populated
require.NotNil(t, state.Chains[homeChainSel].CapabilityRegistry)
require.NotNil(t, state.Chains[homeChainSel].CCIPHome)
require.NotNil(t, state.Chains[homeChainSel].RMNHome)
for _, sel := range selectors {
require.NotNil(t, state.Chains[sel].LinkToken)
require.NotNil(t, state.Chains[sel].Weth9)
require.NotNil(t, state.Chains[sel].TokenAdminRegistry)
require.NotNil(t, state.Chains[sel].RegistryModule)
require.NotNil(t, state.Chains[sel].Router)
require.NotNil(t, state.Chains[sel].RMNRemote)
require.NotNil(t, state.Chains[sel].TestRouter)
require.NotNil(t, state.Chains[sel].NonceManager)
require.NotNil(t, state.Chains[sel].FeeQuoter)
require.NotNil(t, state.Chains[sel].OffRamp)
require.NotNil(t, state.Chains[sel].OnRamp)
}
}
4 changes: 3 additions & 1 deletion deployment/ccip/changeset/home_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ func DeployHomeChain(env deployment.Environment, cfg DeployHomeChainConfig) (dep
_, err = ccipdeployment.DeployHomeChain(env.Logger, env, ab, env.Chains[cfg.HomeChainSel], cfg.RMNStaticConfig, cfg.RMNDynamicConfig, cfg.NodeOperators, cfg.NodeP2PIDsPerNodeOpAdmin)
if err != nil {
env.Logger.Errorw("Failed to deploy cap reg", "err", err, "addresses", env.ExistingAddresses)
return deployment.ChangesetOutput{}, err
return deployment.ChangesetOutput{
AddressBook: ab,
}, err
}

return deployment.ChangesetOutput{
Expand Down
21 changes: 21 additions & 0 deletions deployment/ccip/changeset/jobspec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package changeset

import (
"github.com/pkg/errors"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock"

"github.com/smartcontractkit/chainlink/deployment"
ccipdeployment "github.com/smartcontractkit/chainlink/deployment/ccip"
)

func Jobspec(env deployment.Environment, _ any) (deployment.ChangesetOutput, error) {
js, err := ccipdeployment.NewCCIPJobSpecs(env.NodeIDs, env.Offchain)
if err != nil {
return deployment.ChangesetOutput{}, errors.Wrapf(err, "failed to create job specs")
}
return deployment.ChangesetOutput{
Proposals: []timelock.MCMSWithTimelockProposal{},
AddressBook: deployment.NewMemoryAddressBook(),
JobSpecs: js,
}, nil
}
35 changes: 35 additions & 0 deletions deployment/ccip/changeset/jobspec_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package changeset

import (
"testing"

"github.com/stretchr/testify/require"
"go.uber.org/zap/zapcore"

"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/deployment/environment/memory"
ccip "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/validate"
"github.com/smartcontractkit/chainlink/v2/core/logger"
)

func TestJobSpecChangeset(t *testing.T) {
lggr := logger.TestLogger(t)
e := memory.NewMemoryEnvironment(t, lggr, zapcore.InfoLevel, memory.MemoryEnvironmentConfig{
Chains: 1,
Nodes: 4,
})
output, err := Jobspec(e, nil)
require.NoError(t, err)
require.NotNil(t, output.JobSpecs)
nodes, err := deployment.NodeInfo(e.NodeIDs, e.Offchain)
require.NoError(t, err)
for _, node := range nodes {
jobs, exists := output.JobSpecs[node.NodeID]
require.True(t, exists)
require.NotNil(t, jobs)
for _, job := range jobs {
_, err = ccip.ValidatedCCIPSpec(job)
require.NoError(t, err)
}
}
}
12 changes: 4 additions & 8 deletions deployment/ccip/changeset/prerequisites.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/pkg/errors"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock"
chain_selectors "github.com/smartcontractkit/chain-selectors"

"github.com/smartcontractkit/chainlink/deployment"
ccipdeployment "github.com/smartcontractkit/chainlink/deployment/ccip"
Expand All @@ -27,7 +26,9 @@ func DeployPrerequisites(env deployment.Environment, cfg DeployPrerequisiteConfi
err = ccipdeployment.DeployPrerequisiteChainContracts(env, ab, cfg.ChainSelectors)
if err != nil {
env.Logger.Errorw("Failed to deploy prerequisite contracts", "err", err, "addressBook", ab)
return deployment.ChangesetOutput{}, fmt.Errorf("failed to deploy prerequisite contracts: %w", err)
return deployment.ChangesetOutput{
AddressBook: ab,
}, fmt.Errorf("failed to deploy prerequisite contracts: %w", err)
}
return deployment.ChangesetOutput{
Proposals: []timelock.MCMSWithTimelockProposal{},
Expand All @@ -45,14 +46,9 @@ type DeployPrerequisiteConfig struct {

func (c DeployPrerequisiteConfig) Validate() error {
for _, cs := range c.ChainSelectors {
if cs == 0 {
return fmt.Errorf("chain selector must be set")
}
_, err := chain_selectors.ChainIdFromSelector(cs)
if err != nil {
if err := deployment.IsValidChainSelector(cs); err != nil {
return fmt.Errorf("invalid chain selector: %d - %w", cs, err)
}

}
return nil
}
7 changes: 1 addition & 6 deletions deployment/ccip/changeset/save_existing.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/pkg/errors"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock"
chain_selectors "github.com/smartcontractkit/chain-selectors"

"github.com/smartcontractkit/chainlink/deployment"
)
Expand All @@ -27,11 +26,7 @@ type ExistingContractsConfig struct {

func (cfg ExistingContractsConfig) Validate() error {
for _, ec := range cfg.ExistingContracts {
if ec.ChainSelector == 0 {
return fmt.Errorf("chain selectors must be set")
}
_, err := chain_selectors.ChainIdFromSelector(ec.ChainSelector)
if err != nil {
if err := deployment.IsValidChainSelector(ec.ChainSelector); err != nil {
return fmt.Errorf("invalid chain selector: %d - %w", ec.ChainSelector, err)
}
if ec.Address == (common.Address{}) {
Expand Down
101 changes: 67 additions & 34 deletions deployment/ccip/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,29 +321,11 @@ func DeployCCIPContracts(e deployment.Environment, ab deployment.AddressBook, c
e.Logger.Errorw("Failed to get capability registry")
return fmt.Errorf("capability registry not found")
}
cr, err := capReg.GetHashedCapabilityId(
&bind.CallOpts{}, CapabilityLabelledName, CapabilityVersion)
if err != nil {
e.Logger.Errorw("Failed to get hashed capability id", "err", err)
return err
ccipHome := existingState.Chains[c.HomeChainSel].CCIPHome
if ccipHome == nil {
e.Logger.Errorw("Failed to get ccip home", "err", err)
return fmt.Errorf("ccip home not found")
}
if cr != CCIPCapabilityID {
return fmt.Errorf("capability registry does not support CCIP %s %s", hexutil.Encode(cr[:]), hexutil.Encode(CCIPCapabilityID[:]))
}
capability, err := capReg.GetCapability(nil, CCIPCapabilityID)
if err != nil {
e.Logger.Errorw("Failed to get capability", "err", err)
return err
}
ccipHome, err := ccip_home.NewCCIPHome(capability.ConfigurationContract, e.Chains[c.HomeChainSel].Client)
if err != nil {
e.Logger.Errorw("Failed to get ccip config", "err", err)
return err
}
if ccipHome.Address() != existingState.Chains[c.HomeChainSel].CCIPHome.Address() {
return fmt.Errorf("ccip home address mismatch")
}

rmnHome := existingState.Chains[c.HomeChainSel].RMNHome
if rmnHome == nil {
e.Logger.Errorw("Failed to get rmn home", "err", err)
Expand All @@ -352,18 +334,10 @@ func DeployCCIPContracts(e deployment.Environment, ab deployment.AddressBook, c

usdcConfiguration := make(map[cciptypes.ChainSelector]pluginconfig.USDCCCTPTokenConfig)
for _, chainSel := range c.ChainsToDeploy {
chain, ok := e.Chains[chainSel]
if !ok {
chain, exists := e.Chains[chainSel]
if !exists {
return fmt.Errorf("chain %d not found", chainSel)
}
if existingState.Chains[chainSel].LinkToken == nil || existingState.Chains[chainSel].Weth9 == nil {
return fmt.Errorf("fee tokens not found for chain %d", chainSel)
}
err = DeployChainContracts(e, chain, ab, c.MCMSConfig, rmnHome)
if err != nil {
return err
}

if c.USDCConfig.Enabled {
token, pool, messenger, transmitter, err1 := DeployUSDC(e.Logger, chain, ab, existingState.Chains[chainSel])
if err1 != nil {
Expand All @@ -383,10 +357,13 @@ func DeployCCIPContracts(e deployment.Environment, ab deployment.AddressBook, c
}
}
}

err = DeployChainContractsForChains(e, ab, c.HomeChainSel, c.ChainsToDeploy, c.MCMSConfig)
if err != nil {
e.Logger.Errorw("Failed to deploy chain contracts", "err", err)
return err
}
for _, chainSel := range c.ChainsToDeploy {
chain, _ := e.Chains[chainSel]

chainAddresses, err := ab.AddressesForChain(chain.Selector)
if err != nil {
e.Logger.Errorw("Failed to get chain addresses", "err", err)
Expand Down Expand Up @@ -553,6 +530,62 @@ func DeployMCMSContracts(
}, nil
}

func DeployChainContractsForChains(e deployment.Environment, ab deployment.AddressBook, homeChainSel uint64, chainsToDeploy []uint64, mcmsConfig MCMSConfig) error {
existingState, err := LoadOnchainState(e)
if err != nil {
e.Logger.Errorw("Failed to load existing onchain state", "err")
return err
}

capReg := existingState.Chains[homeChainSel].CapabilityRegistry
if capReg == nil {
e.Logger.Errorw("Failed to get capability registry")
return fmt.Errorf("capability registry not found")
}
cr, err := capReg.GetHashedCapabilityId(
&bind.CallOpts{}, CapabilityLabelledName, CapabilityVersion)
if err != nil {
e.Logger.Errorw("Failed to get hashed capability id", "err", err)
return err
}
if cr != CCIPCapabilityID {
return fmt.Errorf("capability registry does not support CCIP %s %s", hexutil.Encode(cr[:]), hexutil.Encode(CCIPCapabilityID[:]))
}
capability, err := capReg.GetCapability(nil, CCIPCapabilityID)
if err != nil {
e.Logger.Errorw("Failed to get capability", "err", err)
return err
}
ccipHome, err := ccip_home.NewCCIPHome(capability.ConfigurationContract, e.Chains[homeChainSel].Client)
if err != nil {
e.Logger.Errorw("Failed to get ccip config", "err", err)
return err
}
if ccipHome.Address() != existingState.Chains[homeChainSel].CCIPHome.Address() {
return fmt.Errorf("ccip home address mismatch")
}
rmnHome := existingState.Chains[homeChainSel].RMNHome
if rmnHome == nil {
e.Logger.Errorw("Failed to get rmn home", "err", err)
return fmt.Errorf("rmn home not found")
}
for _, chainSel := range chainsToDeploy {
chain, ok := e.Chains[chainSel]
if !ok {
return fmt.Errorf("chain %d not found", chainSel)
}
if existingState.Chains[chainSel].LinkToken == nil || existingState.Chains[chainSel].Weth9 == nil {
return fmt.Errorf("fee tokens not found for chain %d", chainSel)
}
err := DeployChainContracts(e, chain, ab, mcmsConfig, rmnHome)
if err != nil {
e.Logger.Errorw("Failed to deploy chain contracts", "chain", chainSel, "err", err)
return fmt.Errorf("failed to deploy chain contracts for chain %d: %w", chainSel, err)
}
}
return nil
}

func DeployChainContracts(
e deployment.Environment,
chain deployment.Chain,
Expand Down
Loading

0 comments on commit c95cd54

Please sign in to comment.