Skip to content

Commit

Permalink
More changeset/view typing (#14872)
Browse files Browse the repository at this point in the history
* More changeset/view typing

* Signal json

* Apply structure to keystone

* Extract common for shared views

* Fix lint

* Simplify

* One more ks test

* CCIP home assertion

* Remove some comments

* Fix import

* More tests

* Lint

* CCIP owns smoke

* Remove codeowner change, separate pr

* Fix smoke test

* Remove dupe

* More merge conflicts

* More conflicts

* Rm unused fn

* Fix a lint

* Tidy mod

* Generate

* Exclude deployment project from flaky detector

* Remove chainlink-relay from flaky detector excludes

---------

Co-authored-by: lukaszcl <120112546+lukaszcl@users.noreply.github.com>
  • Loading branch information
connorwstein and lukaszcl authored Oct 31, 2024
1 parent 21290c2 commit 709566a
Show file tree
Hide file tree
Showing 43 changed files with 608 additions and 382 deletions.
12 changes: 11 additions & 1 deletion deployment/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,21 @@ and then exposed for use in persistent environments like testnet/mainnet.
- Product agnostic environment abstractions and helpers using those
abstractions

/deployment/memory
/deployment/environment/memory
- package name `memory`
- In-memory environment for fast integration testing
- EVM only

/deployment/environment/devenv
- package name `devenv`
- Docker environment for higher fidelity testing
- Support non-EVMs (yet to be implemented)

/deployment/common
- Deploymnet/configuration/view logic for product agnostic
contracts (like MCMS, LinkToken etc) which can be shared
by products.

/deployment/ccip
- package name `ccipdeployment`
- Files and tests per product deployment/configuration workflows
Expand Down
15 changes: 7 additions & 8 deletions deployment/ccip/add_lane_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,13 @@ func TestAddLane(t *testing.T) {
}
// Set up CCIP contracts and a DON per chain.
err = DeployCCIPContracts(e.Env, e.Ab, DeployCCIPContractConfig{
HomeChainSel: e.HomeChainSel,
FeedChainSel: e.FeedChainSel,
TokenConfig: tokenConfig,
MCMSConfig: NewTestMCMSConfig(t, e.Env),
FeeTokenContracts: feeTokenContracts,
ChainsToDeploy: []uint64{chain1, chain2},
CapabilityRegistry: state.Chains[e.HomeChainSel].CapabilityRegistry.Address(),
OCRSecrets: deployment.XXXGenerateTestOCRSecrets(),
HomeChainSel: e.HomeChainSel,
FeedChainSel: e.FeedChainSel,
TokenConfig: tokenConfig,
MCMSConfig: NewTestMCMSConfig(t, e.Env),
ExistingAddressBook: e.Ab,
ChainsToDeploy: []uint64{chain1, chain2},
OCRSecrets: deployment.XXXGenerateTestOCRSecrets(),
})
require.NoError(t, err)

Expand Down
18 changes: 9 additions & 9 deletions deployment/ccip/changeset/active_candidate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,18 @@ func TestActiveCandidate(t *testing.T) {
},
)

output, err := InitialDeployChangeSet(tenv.Ab, tenv.Env, ccdeploy.DeployCCIPContractConfig{
HomeChainSel: tenv.HomeChainSel,
FeedChainSel: tenv.FeedChainSel,
ChainsToDeploy: tenv.Env.AllChainSelectors(),
TokenConfig: tokenConfig,
MCMSConfig: ccdeploy.NewTestMCMSConfig(t, e),
FeeTokenContracts: tenv.FeeTokenContracts,
CapabilityRegistry: state.Chains[tenv.HomeChainSel].CapabilityRegistry.Address(),
OCRSecrets: deployment.XXXGenerateTestOCRSecrets(),
output, err := InitialDeploy(tenv.Env, ccdeploy.DeployCCIPContractConfig{
HomeChainSel: tenv.HomeChainSel,
FeedChainSel: tenv.FeedChainSel,
ChainsToDeploy: tenv.Env.AllChainSelectors(),
TokenConfig: tokenConfig,
MCMSConfig: ccdeploy.NewTestMCMSConfig(t, e),
ExistingAddressBook: tenv.Ab,
OCRSecrets: deployment.XXXGenerateTestOCRSecrets(),
})
require.NoError(t, err)
// Get new state after migration.
require.NoError(t, tenv.Ab.Merge(output.AddressBook))
state, err = ccdeploy.LoadOnchainState(e, tenv.Ab)
require.NoError(t, err)
homeCS, destCS := tenv.HomeChainSel, tenv.FeedChainSel
Expand Down
15 changes: 7 additions & 8 deletions deployment/ccip/changeset/add_chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,13 @@ func TestAddChainInbound(t *testing.T) {

tokenConfig := ccipdeployment.NewTestTokenConfig(state.Chains[e.FeedChainSel].USDFeeds)
err = ccipdeployment.DeployCCIPContracts(e.Env, e.Ab, ccipdeployment.DeployCCIPContractConfig{
HomeChainSel: e.HomeChainSel,
FeedChainSel: e.FeedChainSel,
ChainsToDeploy: initialDeploy,
TokenConfig: tokenConfig,
MCMSConfig: ccipdeployment.NewTestMCMSConfig(t, e.Env),
FeeTokenContracts: e.FeeTokenContracts,
CapabilityRegistry: state.Chains[e.HomeChainSel].CapabilityRegistry.Address(),
OCRSecrets: deployment.XXXGenerateTestOCRSecrets(),
HomeChainSel: e.HomeChainSel,
FeedChainSel: e.FeedChainSel,
ChainsToDeploy: initialDeploy,
TokenConfig: tokenConfig,
MCMSConfig: ccipdeployment.NewTestMCMSConfig(t, e.Env),
ExistingAddressBook: e.Ab,
OCRSecrets: deployment.XXXGenerateTestOCRSecrets(),
})
require.NoError(t, err)
state, err = ccipdeployment.LoadOnchainState(e.Env, e.Ab)
Expand Down
10 changes: 8 additions & 2 deletions deployment/ccip/changeset/cap_reg.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@ import (
ccipdeployment "github.com/smartcontractkit/chainlink/deployment/ccip"
)

// Separated changset because cap reg is an env var for CL nodes.
func CapRegChangeSet(env deployment.Environment, homeChainSel uint64) (deployment.ChangesetOutput, error) {
var _ deployment.ChangeSet = DeployCapReg

// DeployCapReg is a separate changeset because cap reg is an env var for CL nodes.
func DeployCapReg(env deployment.Environment, config interface{}) (deployment.ChangesetOutput, error) {
homeChainSel, ok := config.(uint64)
if !ok {
return deployment.ChangesetOutput{}, deployment.ErrInvalidConfig
}
// Note we also deploy the cap reg.
ab := deployment.NewMemoryAddressBook()
_, err := ccipdeployment.DeployCapReg(env.Logger, ab, env.Chains[homeChainSel])
Expand Down
13 changes: 8 additions & 5 deletions deployment/ccip/changeset/initial_deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ import (
ccipdeployment "github.com/smartcontractkit/chainlink/deployment/ccip"
)

// We expect the change set input to be unique per change set.
// TODO: Maybe there's a generics approach here?
// Note if the change set is a deployment and it fails we have 2 options:
// - Just throw away the addresses, fix issue and try again (potentially expensive on mainnet)
func InitialDeployChangeSet(ab deployment.AddressBook, env deployment.Environment, c ccipdeployment.DeployCCIPContractConfig) (deployment.ChangesetOutput, error) {
var _ deployment.ChangeSet = InitialDeploy

func InitialDeploy(env deployment.Environment, config interface{}) (deployment.ChangesetOutput, error) {
c, ok := config.(ccipdeployment.DeployCCIPContractConfig)
if !ok {
return deployment.ChangesetOutput{}, deployment.ErrInvalidConfig
}
ab := deployment.NewMemoryAddressBook()
err := ccipdeployment.DeployCCIPContracts(env, ab, c)
if err != nil {
env.Logger.Errorw("Failed to deploy CCIP contracts", "err", err, "addresses", ab)
Expand Down
19 changes: 9 additions & 10 deletions deployment/ccip/changeset/initial_deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package changeset
import (
"testing"


"github.com/smartcontractkit/chainlink/deployment"

ccdeploy "github.com/smartcontractkit/chainlink/deployment/ccip"
Expand All @@ -27,18 +26,18 @@ func TestInitialDeploy(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, state.Chains[tenv.HomeChainSel].LinkToken)

output, err := InitialDeployChangeSet(tenv.Ab, tenv.Env, ccdeploy.DeployCCIPContractConfig{
HomeChainSel: tenv.HomeChainSel,
FeedChainSel: tenv.FeedChainSel,
ChainsToDeploy: tenv.Env.AllChainSelectors(),
TokenConfig: ccdeploy.NewTestTokenConfig(state.Chains[tenv.FeedChainSel].USDFeeds),
MCMSConfig: ccdeploy.NewTestMCMSConfig(t, e),
CapabilityRegistry: state.Chains[tenv.HomeChainSel].CapabilityRegistry.Address(),
FeeTokenContracts: tenv.FeeTokenContracts,
OCRSecrets: deployment.XXXGenerateTestOCRSecrets(),
output, err := InitialDeploy(tenv.Env, ccdeploy.DeployCCIPContractConfig{
HomeChainSel: tenv.HomeChainSel,
FeedChainSel: tenv.FeedChainSel,
ChainsToDeploy: tenv.Env.AllChainSelectors(),
TokenConfig: ccdeploy.NewTestTokenConfig(state.Chains[tenv.FeedChainSel].USDFeeds),
MCMSConfig: ccdeploy.NewTestMCMSConfig(t, e),
ExistingAddressBook: tenv.Ab,
OCRSecrets: deployment.XXXGenerateTestOCRSecrets(),
})
require.NoError(t, err)
// Get new state after migration.
require.NoError(t, tenv.Ab.Merge(output.AddressBook))
state, err = ccdeploy.LoadOnchainState(e, tenv.Ab)
require.NoError(t, err)

Expand Down
31 changes: 31 additions & 0 deletions deployment/ccip/changeset/view.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package changeset

import (
"encoding/json"

"github.com/smartcontractkit/chainlink/deployment"
ccipdeployment "github.com/smartcontractkit/chainlink/deployment/ccip"
ccipview "github.com/smartcontractkit/chainlink/deployment/ccip/view"
"github.com/smartcontractkit/chainlink/deployment/common/view"
)

var _ deployment.ViewState = ViewCCIP

func ViewCCIP(e deployment.Environment, ab deployment.AddressBook) (json.Marshaler, error) {
state, err := ccipdeployment.LoadOnchainState(e, ab)
if err != nil {
return nil, err
}
chainView, err := state.View(e.AllChainSelectors())
if err != nil {
return nil, err
}
nopsView, err := view.GenerateNopsView(e.NodeIDs, e.Offchain)
if err != nil {
return nil, err
}
return ccipview.CCIPView{
Chains: chainView,
Nops: nopsView,
}, nil
}
64 changes: 35 additions & 29 deletions deployment/ccip/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,22 +118,27 @@ func deployContract[C Contracts](
}

type DeployCCIPContractConfig struct {
HomeChainSel uint64
FeedChainSel uint64
ChainsToDeploy []uint64
TokenConfig TokenConfig
CapabilityRegistry common.Address
FeeTokenContracts map[uint64]FeeTokenContracts
HomeChainSel uint64
FeedChainSel uint64
ChainsToDeploy []uint64
TokenConfig TokenConfig
ExistingAddressBook deployment.AddressBook
// I believe it makes sense to have the same signers across all chains
// since that's the point MCMS.
MCMSConfig MCMSConfig
// For setting OCR configuration
OCRSecrets deployment.OCRSecrets
}

// DeployCCIPContracts assumes that the capability registry and ccip home contracts
// are already deployed (needed as a first step because the chainlink nodes point to them).
// It then deploys
// DeployCCIPContracts assumes the following contracts are deployed:
// - Capability registry
// - CCIP home
// - RMN home
// - Fee tokens on all chains.
// and present in ExistingAddressBook.
// It then deploys the rest of the CCIP chain contracts to the selected chains
// registers the nodes with the capability registry and creates a DON for
// each new chain. TODO: Might be better to break this down a bit?
func DeployCCIPContracts(e deployment.Environment, ab deployment.AddressBook, c DeployCCIPContractConfig) error {
if c.OCRSecrets.IsEmpty() {
return fmt.Errorf("OCR secrets are empty")
Expand All @@ -143,11 +148,16 @@ func DeployCCIPContracts(e deployment.Environment, ab deployment.AddressBook, c
e.Logger.Errorw("Failed to get node info", "err", err)
return err
}
capReg, err := capabilities_registry.NewCapabilitiesRegistry(c.CapabilityRegistry, e.Chains[c.HomeChainSel].Client)
existingState, err := LoadOnchainState(e, c.ExistingAddressBook)
if err != nil {
e.Logger.Errorw("Failed to get capability registry", "err", err)
e.Logger.Errorw("Failed to load existing onchain state", "err")
return err
}
capReg := existingState.Chains[c.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 {
Expand All @@ -167,6 +177,9 @@ func DeployCCIPContracts(e deployment.Environment, ab deployment.AddressBook, c
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")
}

// Signal to CR that our nodes support CCIP capability.
if err := AddNodes(
Expand All @@ -177,31 +190,24 @@ func DeployCCIPContracts(e deployment.Environment, ab deployment.AddressBook, c
); err != nil {
return err
}

rmnHomeAddress, err := deployment.SearchAddressBook(ab, c.HomeChainSel, RMNHome)
if err != nil {
return fmt.Errorf("rmn home address not found: %w", err)
}
if !common.IsHexAddress(rmnHomeAddress) {
return fmt.Errorf("rmn home address %s is not a valid address", rmnHomeAddress)
}

rmnHome, err := rmn_home.NewRMNHome(common.HexToAddress(rmnHomeAddress), e.Chains[c.HomeChainSel].Client)
if err != nil {
rmnHome := existingState.Chains[c.HomeChainSel].RMNHome
if rmnHome == nil {
e.Logger.Errorw("Failed to get rmn home", "err", err)
return err
return fmt.Errorf("rmn home not found")
}

for _, chainSel := range c.ChainsToDeploy {
chain, ok := e.Chains[chainSel]
if !ok {
return fmt.Errorf("chain %d not found", chainSel)
}
chainConfig, ok := c.FeeTokenContracts[chainSel]
if !ok {
return fmt.Errorf("chain %d config 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, chainConfig, c.MCMSConfig, rmnHome)
err = DeployChainContracts(e, chain, ab, FeeTokenContracts{
LinkToken: existingState.Chains[chainSel].LinkToken,
Weth9: existingState.Chains[chainSel].Weth9,
}, c.MCMSConfig, rmnHome)
if err != nil {
return err
}
Expand All @@ -216,7 +222,7 @@ func DeployCCIPContracts(e deployment.Environment, ab deployment.AddressBook, c
return err
}

tokenInfo := c.TokenConfig.GetTokenInfo(e.Logger, c.FeeTokenContracts[chainSel].LinkToken, c.FeeTokenContracts[chainSel].Weth9)
tokenInfo := c.TokenConfig.GetTokenInfo(e.Logger, existingState.Chains[chainSel].LinkToken, existingState.Chains[chainSel].Weth9)
// TODO: Do we want to extract this?
// Add chain config for each chain.
_, err = AddChainConfig(
Expand All @@ -235,7 +241,7 @@ func DeployCCIPContracts(e deployment.Environment, ab deployment.AddressBook, c
c.OCRSecrets,
capReg,
ccipHome,
common.HexToAddress(rmnHomeAddress),
rmnHome.Address(),
chainState.OffRamp,
c.FeedChainSel,
tokenInfo,
Expand Down
17 changes: 8 additions & 9 deletions deployment/ccip/deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func TestDeployCCIPContracts(t *testing.T) {
// Deploy all the CCIP contracts.
ab := deployment.NewMemoryAddressBook()
homeChainSel, feedChainSel := allocateCCIPChainSelectors(e.Chains)
feeTokenContracts, _ := DeployTestContracts(t, lggr, ab, homeChainSel, feedChainSel, e.Chains)
_, _ = DeployTestContracts(t, lggr, ab, homeChainSel, feedChainSel, e.Chains)

// Load the state after deploying the cap reg and feeds.
s, err := LoadOnchainState(e, ab)
Expand All @@ -34,14 +34,13 @@ func TestDeployCCIPContracts(t *testing.T) {
require.NotNil(t, s.Chains[feedChainSel].USDFeeds)

err = DeployCCIPContracts(e, ab, DeployCCIPContractConfig{
HomeChainSel: homeChainSel,
FeedChainSel: feedChainSel,
ChainsToDeploy: e.AllChainSelectors(),
TokenConfig: NewTokenConfig(),
CapabilityRegistry: s.Chains[homeChainSel].CapabilityRegistry.Address(),
FeeTokenContracts: feeTokenContracts,
MCMSConfig: NewTestMCMSConfig(t, e),
OCRSecrets: deployment.XXXGenerateTestOCRSecrets(),
HomeChainSel: homeChainSel,
FeedChainSel: feedChainSel,
ChainsToDeploy: e.AllChainSelectors(),
TokenConfig: NewTokenConfig(),
ExistingAddressBook: ab,
MCMSConfig: NewTestMCMSConfig(t, e),
OCRSecrets: deployment.XXXGenerateTestOCRSecrets(),
})
require.NoError(t, err)
state, err := LoadOnchainState(e, ab)
Expand Down
Loading

0 comments on commit 709566a

Please sign in to comment.