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

Tooling deployment scaffolding #1252

Merged
merged 51 commits into from
Aug 16, 2024
Merged

Conversation

connorwstein
Copy link
Collaborator

@connorwstein connorwstein commented Aug 1, 2024

  • NOTE ~5k is generated protobuf code to unblock. That will be imported once exposed.
  • We put the deployment/configuration logic in integration-tests module for a few reasons:
    • Keeps the chain dependencies out of the core module, in particular helpful for eventual cross family tests
    • It can become the canonical deployment logic to be used for CRIB envs as well (eventually can replace the actions + contracts dirs)
  • To accomplish the lightweight tests (chainlink.Application + simulated.Backend) we expose some test utilities in util/testutils/
  • integration-tests/deployment holds product agnostic deployment utilities including a general purpose environment structure to write environment abstracted code against and migration output components (address books, proposals etc)
  • integration-tests/deployment/ccip holds all product specific deployment code including
    • Top level migrations and migration tests where a "migration" is defined to be a function which operates against an environment and outputs a MigrationOutput structure with one or more artifacts (MCMS proposals, job specs). Notably migration tests can apply those outputs to an ephemeral environment to ensure correctness. These migrations are intended for export and use against real environments (testnet/mainnet).
    • Re-usable product specific components of top level migrations and associated tests

Next steps / follow up PRs:

  • Port testutils export to chainlink repo
  • Example solana setup
  • Once cross family validated, start deeper testing and real CCIP use cases

@connorwstein connorwstein marked this pull request as ready for review August 8, 2024 15:26
## Motivation


## Solution
integration-tests/deployment/environment.go Outdated Show resolved Hide resolved
return ""
}

func UBigInt(i uint64) *big.Int {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about moving to a utils/helper ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in a follow up PR I'll dig around for a better home. Something in chainlink-common or existing tools should exist for this

return new(big.Int).SetUint64(i)
}

func E18Mult(amount uint64) *big.Int {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about moving to a utils/helper ?

// OnchainPublicKey types.OnchainPublicKey
// PeerID string
// TransmitAccount types.Account
type Node struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
type Node struct {
type OCRNode struct {

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

today I actually don't think there are non-OCR nodes anymore heh

type OnchainClient interface {
// For EVM specifically we can use existing geth interface
// to abstract chain clients.
bind.ContractBackend
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we also include bind.DeployBackend ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idt its needed because Confirm is a separate function pointer which can enclose transactionReceipt?

Name string
Chains map[uint64]Chain
Offchain OffchainClient
NodeIDs []string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these NodeIDs specific to JD ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right now I'm using the peerIDs to unblock but JD does have its own. Will have to move to theirs at some point or convince them to use peerIDs

integration-tests/deployment/memory/environment.go Outdated Show resolved Hide resolved
Copy link
Contributor

@makramkd makramkd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR is kinda huge so hard to review the whole thing but looks good overall. Have some comments about the test logic changes, not sure it makes the test more robust


type AddressBook interface {
Save(chainSelector uint64, address string, typeAndVersion string) error
Addresses() (map[uint64]map[string]string, error)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having comments on what the keys on this map mean might help. I'm guessing its chainSelector => (typeAndVersion => address) but not 100% sure.

Alternatively a type alias on ChainSelector/Address and maybe a concrete TypeAndVersion type e.g

type (
  ChainSelector = uint64
  TypeAndVersion struct {
    Type, Version string
  }
  Address = string
)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah type alias sgtm. Can add in a follow up with further address book testing

integration-tests/deployment/address_book.go Outdated Show resolved Hide resolved
}
for chain, chainAddresses := range addresses {
for address, typeAndVersions := range chainAddresses {
return m.Save(chain, address, typeAndVersions)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a premature return here, is this intended?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope good catch, merge to be tested in a follow up / once we determine its use case a bit better

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added a quick merge test


import "fmt"

type AddressBook interface {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doc comment on this would be useful to explain significance and also to indicate whether implementations should be thread-safe

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I think a full polish address book follow up PR needed

AddressesByChain map[uint64]map[string]string
}

func (m *AddressBookMap) Save(chainSelector uint64, address string, typeAndVersion string) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we check for edge cases here e.g address or typeAndVersion being empty strings?

integration-tests/deployment/memory/environment.go Outdated Show resolved Hide resolved
m.AddressesByChain[chainSelector] = make(map[string]string)
}
if _, exists := m.AddressesByChain[chainSelector][address]; exists {
return fmt.Errorf("address %s already exists for chain %d", address, chainSelector)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's okay if it has same value not to error, just a log should be enough.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in this context I think it'd imply caller has a bug so should error

)

type Contracts interface {
*capabilities_registry.CapabilitiesRegistry |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL this feature in Go :D

Copy link
Contributor

@makramkd makramkd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some minor follow up comments; lets make sure we have tickets for all the follow up work (which seems to be quite a lot)

for node, jb := range js {
fmt.Println(node, jb)
}
// TODO: Add job assertions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe later can we have a ticket for each TODO (or group relevant TODO's together)?

CapabilityVersion: CapabilityVersion,
CapabilityLabelledName: CapabilityLabelledName,
OCRKeyBundleIDs: map[string]string{
// TODO: Validate that that all EVM chains are using the same keybundle.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Offchain OCR key usage for CCIP capability is by definition one per family (this is enforced by the job). That's why we require the bundle IDs to be set explicitly in the job spec in case there is more than one per family

wg.Add(1)
go func(src, dest uint64) {
defer wg.Done()
waitForCommitWithInterval(t, srcChain, dstChain, state.EvmOffRampsV160[dest], ccipocr3.SeqNumRange{1, 1})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: you can just use src and dest here since they're passed in as args to the function - those are eval'd at the go statement callsite unlike closure vars

wg.Add(1)
go func(src, dest uint64) {
defer wg.Done()
waitForExecWithSeqNr(t, srcChain, dstChain, state.EvmOffRampsV160[dest], 1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto here re: src and dest

Comment on lines +242 to +243
src.Client.(*backends.SimulatedBackend).Commit()
dest.Client.(*backends.SimulatedBackend).Commit()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There a reason we don't have these commits in a background goroutine?

@cl-sonarqube-production
Copy link

@AnieeG AnieeG merged commit 07adf8a into ccip-develop Aug 16, 2024
109 of 110 checks passed
@AnieeG AnieeG deleted the CCIP-2906-tooling-scaffolding-basic branch August 16, 2024 18:08
dhaidashenko pushed a commit that referenced this pull request Sep 2, 2024
- NOTE ~5k is generated protobuf code to unblock. That will be imported
once exposed.
- We put the deployment/configuration logic in integration-tests module
for a few reasons:
- Keeps the chain dependencies out of the core module, in particular
helpful for eventual cross family tests
- It can become the canonical deployment logic to be used for CRIB envs
as well (eventually can replace the actions + contracts dirs)
- To accomplish the lightweight tests (chainlink.Application +
simulated.Backend) we expose some test utilities in util/testutils/
- integration-tests/deployment holds product agnostic deployment
utilities including a general purpose environment structure to write
environment abstracted code against and migration output components
(address books, proposals etc)
- integration-tests/deployment/ccip holds all product specific
deployment code including
- Top level migrations and migration tests where a "migration" is
defined to be a function which operates against an environment and
outputs a MigrationOutput structure with one or more artifacts (MCMS
proposals, job specs). Notably migration tests can apply those outputs
to an ephemeral environment to ensure correctness. These migrations are
intended for export and use against real environments (testnet/mainnet).
- Re-usable product specific components of top level migrations and
associated tests

Next steps / follow up PRs:
- Port testutils export to chainlink repo
- Example solana setup
- Once cross family validated, start deeper testing and real CCIP use
cases

---------

Co-authored-by: Adam Hamrick <[email protected]>
Co-authored-by: AnieeG <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants