diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ef1c0f98913..c2d6208d0e3 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -133,10 +133,10 @@ core/scripts/gateway @smartcontractkit/dev-services # Tests /integration-tests/ @smartcontractkit/test-tooling-team @smartcontractkit/core -/integration-tests/ccip-tests @smartcontractkit/ccip-offchain @smartcontractkit/core +/integration-tests/ccip-tests @smartcontractkit/ccip-offchain @smartcontractkit/core @smartcontractkit/ccip /integration-tests/**/*keeper* @smartcontractkit/dev-services @smartcontractkit/core /integration-tests/**/*automation* @smartcontractkit/dev-services @smartcontractkit/core -/integration-tests/**/*ccip* @smartcontractkit/ccip-offchain @smartcontractkit/core +/integration-tests/**/*ccip* @smartcontractkit/ccip-offchain @smartcontractkit/core @smartcontractkit/ccip # Deployment tooling /deployment @smartcontractkit/ccip @smartcontractkit/keystone @smartcontractkit/core @smartcontractkit/deployment-automation diff --git a/.github/e2e-tests.yml b/.github/e2e-tests.yml index a671c081c1a..3b394293378 100644 --- a/.github/e2e-tests.yml +++ b/.github/e2e-tests.yml @@ -948,19 +948,6 @@ runner-test-matrix: E2E_TEST_SELECTED_NETWORK: SIMULATED_1,SIMULATED_2 E2E_JD_VERSION: 0.6.0 - - id: smoke/ccip/ccip_batching_test.go:* - path: integration-tests/smoke/ccip/ccip_batching_test.go - test_env_type: docker - runs_on: ubuntu-latest - triggers: - - PR E2E Core Tests - - Nightly E2E Tests - test_cmd: cd integration-tests/ && go test smoke/ccip/ccip_batching_test.go -timeout 12m -test.parallel=1 -count=1 -json - pyroscope_env: ci-smoke-ccipv1_6-evm-simulated - test_env_vars: - E2E_TEST_SELECTED_NETWORK: SIMULATED_1,SIMULATED_2,SIMULATED_3 - E2E_JD_VERSION: 0.6.0 - - id: smoke/ccip/ccip_token_transfer_test.go:* path: integration-tests/smoke/ccip/ccip_token_transfer_test.go test_env_type: docker diff --git a/.github/integration-in-memory-tests.yml b/.github/integration-in-memory-tests.yml index c5e0f088afe..4b4fd71258d 100644 --- a/.github/integration-in-memory-tests.yml +++ b/.github/integration-in-memory-tests.yml @@ -31,6 +31,14 @@ runner-test-matrix: triggers: - PR Integration CCIP Tests test_cmd: cd integration-tests/smoke/ccip && go test ccip_fee_boosting_test.go -timeout 12m -test.parallel=2 -count=1 -json + + - id: smoke/ccip/ccip_batching_test.go:* + path: integration-tests/smoke/ccip/ccip_batching_test.go + test_env_type: in-memory + runs_on: ubuntu-latest + triggers: + - PR Integration CCIP Tests + test_cmd: cd integration-tests/smoke/ccip && go test ccip_batching_test.go -timeout 12m -test.parallel=2 -count=1 -json - id: contracts/ccipreader_test.go:* path: integration-tests/contracts/ccipreader_test.go diff --git a/.github/workflows/changeset.yml b/.github/workflows/changeset.yml index 5d6b2deafe2..331492eb74f 100644 --- a/.github/workflows/changeset.yml +++ b/.github/workflows/changeset.yml @@ -182,7 +182,7 @@ jobs: - added|modified: 'contracts/.changeset/*.md' - name: Setup node - uses: ./.github/actions/setup-node + uses: ./.github/actions/setup-nodejs if: ${{ steps.files-changed.outputs.contracts-changeset == 'true' }} - name: Validate changeset files diff --git a/contracts/src/v0.8/ccip/capability/CCIPHome.sol b/contracts/src/v0.8/ccip/capability/CCIPHome.sol index 7a425566c33..e43e4b0d03f 100644 --- a/contracts/src/v0.8/ccip/capability/CCIPHome.sol +++ b/contracts/src/v0.8/ccip/capability/CCIPHome.sol @@ -62,7 +62,7 @@ import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts /// │ Active │ revokeCandidate │ Candidate │◄───────────┐ /// │ [1,0] │◄───────────────────┤ [1,1] │────────────┘ /// │ ├───────────────────►│ │ -/// └─────────────┘ setSecondary └─────────────┘ +/// └─────────────┘ setCandidate └─────────────┘ /// contract CCIPHome is Ownable2StepMsgSender, ITypeAndVersion, ICapabilityConfiguration, IERC165 { using EnumerableSet for EnumerableSet.UintSet; diff --git a/contracts/src/v0.8/ccip/rmn/RMNHome.sol b/contracts/src/v0.8/ccip/rmn/RMNHome.sol index 684dc3e994e..92aab25ae8e 100644 --- a/contracts/src/v0.8/ccip/rmn/RMNHome.sol +++ b/contracts/src/v0.8/ccip/rmn/RMNHome.sol @@ -54,7 +54,7 @@ import {Ownable2StepMsgSender} from "../../shared/access/Ownable2StepMsgSender.s /// │ Active │ revokeCandidate │ Candidate │◄───────────┐ /// │ [1,0] │◄───────────────────┤ [1,1] │────────────┘ /// │ ├───────────────────►│ │ -/// └─────────────┘ setSecondary └─────────────┘ +/// └─────────────┘ setCandidate └─────────────┘ /// contract RMNHome is Ownable2StepMsgSender, ITypeAndVersion { event ConfigSet(bytes32 indexed configDigest, uint32 version, StaticConfig staticConfig, DynamicConfig dynamicConfig); diff --git a/core/cmd/shell.go b/core/cmd/shell.go index 788e4da6f69..53ba5e3f82f 100644 --- a/core/cmd/shell.go +++ b/core/cmd/shell.go @@ -53,6 +53,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/cache" "github.com/smartcontractkit/chainlink/v2/core/services/versioning" "github.com/smartcontractkit/chainlink/v2/core/services/webhook" + "github.com/smartcontractkit/chainlink/v2/core/services/workflows" "github.com/smartcontractkit/chainlink/v2/core/sessions" "github.com/smartcontractkit/chainlink/v2/core/static" "github.com/smartcontractkit/chainlink/v2/core/store/migrate" @@ -111,6 +112,10 @@ func initGlobals(cfgProm config.Prometheus, cfgTracing config.Tracing, cfgTeleme AuthPublicKeyHex: csaPubKeyHex, AuthHeaders: beholderAuthHeaders, } + // note: due to the OTEL specification, all histogram buckets + // must be defined when the beholder client is created + clientCfg.MetricViews = append(clientCfg.MetricViews, workflows.MetricViews()...) + if tracingCfg.Enabled { clientCfg.TraceSpanExporter, err = tracingCfg.NewSpanExporter() if err != nil { diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 65d63f2a1df..d468907c550 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -26,7 +26,7 @@ require ( github.com/prometheus/client_golang v1.20.5 github.com/shopspring/decimal v1.4.0 github.com/smartcontractkit/chainlink-automation v0.8.1 - github.com/smartcontractkit/chainlink-common v0.3.1-0.20241204184525-29871ced7b4d + github.com/smartcontractkit/chainlink-common v0.3.1-0.20241206011233-b6684ee6508f github.com/smartcontractkit/chainlink/deployment v0.0.0-00010101000000-000000000000 github.com/smartcontractkit/chainlink/v2 v2.14.0-mercury-20240807.0.20241106193309-5560cd76211a github.com/smartcontractkit/libocr v0.0.0-20241007185508-adbe57025f12 diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 1f1374bc11d..225ae3b9191 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1142,8 +1142,8 @@ github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgB github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= github.com/smartcontractkit/chainlink-ccip v0.0.0-20241204015713-8956bb614e9e h1:GnM6ZWV6vlk2+n6c6o+v/R1LtXzBGVVx7r37nt/h6Uc= github.com/smartcontractkit/chainlink-ccip v0.0.0-20241204015713-8956bb614e9e/go.mod h1:80vGBbOfertJig0xFKsRfm+i17FkjdKkk1dAaGE45Os= -github.com/smartcontractkit/chainlink-common v0.3.1-0.20241204184525-29871ced7b4d h1:5XKarlliHXVVAhpCeOAx/TRU7eWsJ3tkqRI3I6Cc0SU= -github.com/smartcontractkit/chainlink-common v0.3.1-0.20241204184525-29871ced7b4d/go.mod h1:bQktEJf7sJ0U3SmIcXvbGUox7SmXcnSEZ4kUbT8R5Nk= +github.com/smartcontractkit/chainlink-common v0.3.1-0.20241206011233-b6684ee6508f h1:hH+cAG2zt+WK4I2m572LXAnAJg3wtGEAwzBKR8FiXo8= +github.com/smartcontractkit/chainlink-common v0.3.1-0.20241206011233-b6684ee6508f/go.mod h1:bQktEJf7sJ0U3SmIcXvbGUox7SmXcnSEZ4kUbT8R5Nk= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e h1:PRoeby6ZlTuTkv2f+7tVU4+zboTfRzI+beECynF4JQ0= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e/go.mod h1:mUh5/woemsVaHgTorA080hrYmO3syBCmPdnWc/5dOqk= github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241202141438-a90db35252db h1:N1RH1hSr2ACzOFc9hkCcjE8pRBTdcU3p8nsTJByaLes= diff --git a/core/services/workflows/monitoring.go b/core/services/workflows/monitoring.go index 205ce529c28..8457dadeb60 100644 --- a/core/services/workflows/monitoring.go +++ b/core/services/workflows/monitoring.go @@ -5,6 +5,7 @@ import ( "fmt" "go.opentelemetry.io/otel/metric" + sdkmetric "go.opentelemetry.io/otel/sdk/metric" "github.com/smartcontractkit/chainlink-common/pkg/beholder" "github.com/smartcontractkit/chainlink-common/pkg/metrics" @@ -135,6 +136,37 @@ func initMonitoringResources() (em *engineMetrics, err error) { return em, nil } +// Note: due to the OTEL specification, all histogram buckets +// Must be defined when the beholder client is created +func MetricViews() []sdkmetric.View { + return []sdkmetric.View{ + sdkmetric.NewView( + sdkmetric.Instrument{Name: "platform_engine_workflow_earlyexit_time_seconds"}, + sdkmetric.Stream{Aggregation: sdkmetric.AggregationExplicitBucketHistogram{ + Boundaries: []float64{0, 1, 10, 100}, + }}, + ), + sdkmetric.NewView( + sdkmetric.Instrument{Name: "platform_engine_workflow_completed_time_seconds"}, + sdkmetric.Stream{Aggregation: sdkmetric.AggregationExplicitBucketHistogram{ + Boundaries: []float64{0, 100, 1000, 10_000, 50_000, 100_0000, 500_000}, + }}, + ), + sdkmetric.NewView( + sdkmetric.Instrument{Name: "platform_engine_workflow_error_time_seconds"}, + sdkmetric.Stream{Aggregation: sdkmetric.AggregationExplicitBucketHistogram{ + Boundaries: []float64{0, 20, 60, 120, 240}, + }}, + ), + sdkmetric.NewView( + sdkmetric.Instrument{Name: "platform_engine_workflow_step_time_seconds"}, + sdkmetric.Stream{Aggregation: sdkmetric.AggregationExplicitBucketHistogram{ + Boundaries: []float64{0, 20, 60, 120, 240}, + }}, + ), + } +} + // workflowsMetricLabeler wraps monitoring.MetricsLabeler to provide workflow specific utilities // for monitoring resources type workflowsMetricLabeler struct { diff --git a/deployment/ccip/changeset/cs_add_chain_test.go b/deployment/ccip/changeset/cs_add_chain_test.go index c83872c0dd7..f6e1c04c469 100644 --- a/deployment/ccip/changeset/cs_add_chain_test.go +++ b/deployment/ccip/changeset/cs_add_chain_test.go @@ -227,6 +227,7 @@ func TestAddChainInbound(t *testing.T) { // verify if the configs are updated require.NoError(t, ValidateCCIPHomeConfigSetUp( + e.Env.Logger, state.Chains[e.HomeChainSel].CapabilityRegistry, state.Chains[e.HomeChainSel].CCIPHome, newChain, diff --git a/deployment/ccip/changeset/cs_deploy_chain.go b/deployment/ccip/changeset/cs_deploy_chain.go index b57c00fd796..e2762b27578 100644 --- a/deployment/ccip/changeset/cs_deploy_chain.go +++ b/deployment/ccip/changeset/cs_deploy_chain.go @@ -182,31 +182,31 @@ func deployChainContracts( } chainState, chainExists := state.Chains[chain.Selector] if !chainExists { - return fmt.Errorf("chain %d not found in existing state, deploy the prerequisites first", chain.Selector) + return fmt.Errorf("chain %s not found in existing state, deploy the prerequisites first", chain.String()) } if chainState.Weth9 == nil { - return fmt.Errorf("weth9 not found for chain %d, deploy the prerequisites first", chain.Selector) + return fmt.Errorf("weth9 not found for chain %s, deploy the prerequisites first", chain.String()) } if chainState.Timelock == nil { - return fmt.Errorf("timelock not found for chain %d, deploy the mcms contracts first", chain.Selector) + return fmt.Errorf("timelock not found for chain %s, deploy the mcms contracts first", chain.String()) } weth9Contract := chainState.Weth9 if chainState.LinkToken == nil { - return fmt.Errorf("link token not found for chain %d, deploy the prerequisites first", chain.Selector) + return fmt.Errorf("link token not found for chain %s, deploy the prerequisites first", chain.String()) } linkTokenContract := chainState.LinkToken if chainState.TokenAdminRegistry == nil { - return fmt.Errorf("token admin registry not found for chain %d, deploy the prerequisites first", chain.Selector) + return fmt.Errorf("token admin registry not found for chain %s, deploy the prerequisites first", chain.String()) } tokenAdminReg := chainState.TokenAdminRegistry if chainState.RegistryModule == nil { - return fmt.Errorf("registry module not found for chain %d, deploy the prerequisites first", chain.Selector) + return fmt.Errorf("registry module not found for chain %s, deploy the prerequisites first", chain.String()) } if chainState.Router == nil { - return fmt.Errorf("router not found for chain %d, deploy the prerequisites first", chain.Selector) + return fmt.Errorf("router not found for chain %s, deploy the prerequisites first", chain.String()) } if chainState.Receiver == nil { - ccipReceiver, err := deployment.DeployContract(e.Logger, chain, ab, + _, err := deployment.DeployContract(e.Logger, chain, ab, func(chain deployment.Chain) deployment.ContractDeploy[*maybe_revert_message_receiver.MaybeRevertMessageReceiver] { receiverAddr, tx, receiver, err2 := maybe_revert_message_receiver.DeployMaybeRevertMessageReceiver( chain.DeployerKey, @@ -221,9 +221,8 @@ func deployChainContracts( e.Logger.Errorw("Failed to deploy receiver", "err", err) return err } - e.Logger.Infow("deployed receiver", "addr", ccipReceiver.Address) } else { - e.Logger.Infow("receiver already deployed", "addr", chainState.Receiver.Address) + e.Logger.Infow("receiver already deployed", "addr", chainState.Receiver.Address, "chain", chain.String()) } rmnRemoteContract := chainState.RMNRemote if chainState.RMNRemote == nil { @@ -242,20 +241,19 @@ func deployChainContracts( } }) if err != nil { - e.Logger.Errorw("Failed to deploy RMNRemote", "err", err) + e.Logger.Errorw("Failed to deploy RMNRemote", "chain", chain.String(), "err", err) return err } - e.Logger.Infow("deployed RMNRemote", "addr", rmnRemote.Address) rmnRemoteContract = rmnRemote.Contract } else { - e.Logger.Infow("rmn remote already deployed", "addr", chainState.RMNRemote.Address) + e.Logger.Infow("rmn remote already deployed", "chain", chain.String(), "addr", chainState.RMNRemote.Address) } activeDigest, err := rmnHome.GetActiveDigest(&bind.CallOpts{}) if err != nil { - e.Logger.Errorw("Failed to get active digest", "err", err) + e.Logger.Errorw("Failed to get active digest", "chain", chain.String(), "err", err) return err } - e.Logger.Infow("setting active home digest to rmn remote", "digest", activeDigest) + e.Logger.Infow("setting active home digest to rmn remote", "chain", chain.String(), "digest", activeDigest) tx, err := rmnRemoteContract.SetConfig(chain.DeployerKey, rmn_remote.RMNRemoteConfig{ RmnHomeContractConfigDigest: activeDigest, @@ -265,7 +263,7 @@ func deployChainContracts( F: 0, // TODO: update when we have signers }) if _, err := deployment.ConfirmIfNoError(chain, tx, err); err != nil { - e.Logger.Errorw("Failed to confirm RMNRemote config", "err", err) + e.Logger.Errorw("Failed to confirm RMNRemote config", "chain", chain.String(), "err", err) return err } @@ -286,16 +284,15 @@ func deployChainContracts( } }) if err != nil { - e.Logger.Errorw("Failed to deploy RMNProxyNew", "err", err) + e.Logger.Errorw("Failed to deploy RMNProxyNew", "chain", chain.String(), "err", err) return err } - e.Logger.Infow("deployed new RMNProxyNew", "addr", rmnProxy.Address) rmnProxyContract = rmnProxy.Contract } else { - e.Logger.Infow("rmn proxy already deployed", "addr", chainState.RMNProxyNew.Address) + e.Logger.Infow("rmn proxy already deployed", "chain", chain.String(), "addr", chainState.RMNProxyNew.Address) } if chainState.TestRouter == nil { - testRouterContract, err := deployment.DeployContract(e.Logger, chain, ab, + _, err := deployment.DeployContract(e.Logger, chain, ab, func(chain deployment.Chain) deployment.ContractDeploy[*router.Router] { routerAddr, tx2, routerC, err2 := router.DeployRouter( chain.DeployerKey, @@ -308,12 +305,11 @@ func deployChainContracts( } }) if err != nil { - e.Logger.Errorw("Failed to deploy test router", "err", err) + e.Logger.Errorw("Failed to deploy test router", "chain", chain.String(), "err", err) return err } - e.Logger.Infow("deployed test router", "addr", testRouterContract.Address) } else { - e.Logger.Infow("test router already deployed", "addr", chainState.TestRouter.Address) + e.Logger.Infow("test router already deployed", "chain", chain.String(), "addr", chainState.TestRouter.Address) } nmContract := chainState.NonceManager @@ -330,13 +326,12 @@ func deployChainContracts( } }) if err != nil { - e.Logger.Errorw("Failed to deploy nonce manager", "err", err) + e.Logger.Errorw("Failed to deploy nonce manager", "chain", chain.String(), "err", err) return err } - e.Logger.Infow("Deployed nonce manager", "addr", nonceManager.Address) nmContract = nonceManager.Contract } else { - e.Logger.Infow("nonce manager already deployed", "addr", chainState.NonceManager.Address) + e.Logger.Infow("nonce manager already deployed", "chain", chain.String(), "addr", chainState.NonceManager.Address) } feeQuoterContract := chainState.FeeQuoter if chainState.FeeQuoter == nil { @@ -371,13 +366,12 @@ func deployChainContracts( } }) if err != nil { - e.Logger.Errorw("Failed to deploy fee quoter", "err", err) + e.Logger.Errorw("Failed to deploy fee quoter", "chain", chain.String(), "err", err) return err } - e.Logger.Infow("Deployed fee quoter", "addr", feeQuoter.Address) feeQuoterContract = feeQuoter.Contract } else { - e.Logger.Infow("fee quoter already deployed", "addr", chainState.FeeQuoter.Address) + e.Logger.Infow("fee quoter already deployed", "chain", chain.String(), "addr", chainState.FeeQuoter.Address) } onRampContract := chainState.OnRamp if onRampContract == nil { @@ -403,13 +397,12 @@ func deployChainContracts( } }) if err != nil { - e.Logger.Errorw("Failed to deploy onramp", "err", err) + e.Logger.Errorw("Failed to deploy onramp", "chain", chain.String(), "err", err) return err } - e.Logger.Infow("Deployed onramp", "addr", onRamp.Address) onRampContract = onRamp.Contract } else { - e.Logger.Infow("onramp already deployed", "addr", chainState.OnRamp.Address) + e.Logger.Infow("onramp already deployed", "chain", chain.String(), "addr", chainState.OnRamp.Address) } offRampContract := chainState.OffRamp if offRampContract == nil { @@ -436,13 +429,12 @@ func deployChainContracts( } }) if err != nil { - e.Logger.Errorw("Failed to deploy offramp", "err", err) + e.Logger.Errorw("Failed to deploy offramp", "chain", chain.String(), "err", err) return err } - e.Logger.Infow("Deployed offramp", "addr", offRamp.Address) offRampContract = offRamp.Contract } else { - e.Logger.Infow("offramp already deployed", "addr", chainState.OffRamp.Address) + e.Logger.Infow("offramp already deployed", "chain", chain.String(), "addr", chainState.OffRamp.Address) } // Basic wiring is always needed. tx, err = feeQuoterContract.ApplyAuthorizedCallerUpdates(chain.DeployerKey, fee_quoter.AuthorizedCallersAuthorizedCallerArgs{ @@ -451,16 +443,17 @@ func deployChainContracts( AddedCallers: []common.Address{offRampContract.Address(), chain.DeployerKey.From}, }) if _, err := deployment.ConfirmIfNoError(chain, tx, err); err != nil { - e.Logger.Errorw("Failed to confirm fee quoter authorized caller update", "err", err) + e.Logger.Errorw("Failed to confirm fee quoter authorized caller update", "chain", chain.String(), "err", err) return err } - + e.Logger.Infow("Added fee quoter authorized callers", "chain", chain.String(), "callers", []common.Address{offRampContract.Address(), chain.DeployerKey.From}) tx, err = nmContract.ApplyAuthorizedCallerUpdates(chain.DeployerKey, nonce_manager.AuthorizedCallersAuthorizedCallerArgs{ AddedCallers: []common.Address{offRampContract.Address(), onRampContract.Address()}, }) if _, err := deployment.ConfirmIfNoError(chain, tx, err); err != nil { - e.Logger.Errorw("Failed to update nonce manager with ramps", "err", err) + e.Logger.Errorw("Failed to update nonce manager with ramps", "chain", chain.String(), "err", err) return err } + e.Logger.Infow("Added nonce manager authorized callers", "chain", chain.String(), "callers", []common.Address{offRampContract.Address(), onRampContract.Address()}) return nil } diff --git a/deployment/ccip/changeset/cs_home_chain.go b/deployment/ccip/changeset/cs_home_chain.go index 0df8d87affb..750b21229aa 100644 --- a/deployment/ccip/changeset/cs_home_chain.go +++ b/deployment/ccip/changeset/cs_home_chain.go @@ -13,6 +13,7 @@ import ( "golang.org/x/exp/maps" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/internal" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_home" @@ -111,7 +112,7 @@ func deployCapReg( } }) if err != nil { - lggr.Errorw("Failed to deploy capreg", "err", err) + lggr.Errorw("Failed to deploy capreg", "chain", chain.String(), "err", err) return nil, err } return capReg, nil @@ -152,10 +153,9 @@ func deployHomeChain( } }) if err != nil { - lggr.Errorw("Failed to deploy CCIPHome", "err", err) + lggr.Errorw("Failed to deploy CCIPHome", "chain", chain.String(), "err", err) return nil, err } - lggr.Infow("deployed CCIPHome", "addr", ccipHome.Address) rmnHome, err := deployment.DeployContract( lggr, chain, ab, @@ -170,10 +170,9 @@ func deployHomeChain( }, ) if err != nil { - lggr.Errorw("Failed to deploy RMNHome", "err", err) + lggr.Errorw("Failed to deploy RMNHome", "chain", chain.String(), "err", err) return nil, err } - lggr.Infow("deployed RMNHome", "addr", rmnHome.Address) // considering the RMNHome is recently deployed, there is no digest to overwrite tx, err := rmnHome.Contract.SetCandidate(chain.DeployerKey, rmnHomeStatic, rmnHomeDynamic, [32]byte{}) @@ -184,19 +183,19 @@ func deployHomeChain( rmnCandidateDigest, err := rmnHome.Contract.GetCandidateDigest(nil) if err != nil { - lggr.Errorw("Failed to get RMNHome candidate digest", "err", err) + lggr.Errorw("Failed to get RMNHome candidate digest", "chain", chain.String(), "err", err) return nil, err } tx, err = rmnHome.Contract.PromoteCandidateAndRevokeActive(chain.DeployerKey, rmnCandidateDigest, [32]byte{}) if _, err := deployment.ConfirmIfNoError(chain, tx, err); err != nil { - lggr.Errorw("Failed to promote candidate and revoke active on RMNHome", "err", err) + lggr.Errorw("Failed to promote candidate and revoke active on RMNHome", "chain", chain.String(), "err", err) return nil, err } rmnActiveDigest, err := rmnHome.Contract.GetActiveDigest(nil) if err != nil { - lggr.Errorw("Failed to get RMNHome active digest", "err", err) + lggr.Errorw("Failed to get RMNHome active digest", "chain", chain.String(), "err", err) return nil, err } lggr.Infow("Got rmn home active digest", "digest", rmnActiveDigest) @@ -217,14 +216,14 @@ func deployHomeChain( }, }) if _, err := deployment.ConfirmIfNoError(chain, tx, err); err != nil { - lggr.Errorw("Failed to add capabilities", "err", err) + lggr.Errorw("Failed to add capabilities", "chain", chain.String(), "err", err) return nil, err } tx, err = capReg.Contract.AddNodeOperators(chain.DeployerKey, nodeOps) txBlockNum, err := deployment.ConfirmIfNoError(chain, tx, err) if err != nil { - lggr.Errorw("Failed to add node operators", "err", err) + lggr.Errorw("Failed to add node operators", "chain", chain.String(), "err", err) return nil, err } addedEvent, err := capReg.Contract.FilterNodeOperatorAdded(&bind.FilterOpts{ @@ -232,7 +231,7 @@ func deployHomeChain( Context: context.Background(), }, nil, nil) if err != nil { - lggr.Errorw("Failed to filter NodeOperatorAdded event", "err", err) + lggr.Errorw("Failed to filter NodeOperatorAdded event", "chain", chain.String(), "err", err) return capReg, err } // Need to fetch nodeoperators ids to be able to add nodes for corresponding node operators @@ -246,7 +245,7 @@ func deployHomeChain( } } if len(p2pIDsByNodeOpId) != len(nodeP2PIDsPerNodeOpAdmin) { - lggr.Errorw("Failed to add all node operators", "added", maps.Keys(p2pIDsByNodeOpId), "expected", maps.Keys(nodeP2PIDsPerNodeOpAdmin)) + lggr.Errorw("Failed to add all node operators", "added", maps.Keys(p2pIDsByNodeOpId), "expected", maps.Keys(nodeP2PIDsPerNodeOpAdmin), "chain", chain.String()) return capReg, errors.New("failed to add all node operators") } // Adds initial set of nodes to CR, who all have the CCIP capability @@ -317,7 +316,7 @@ func addNodes( } tx, err := capReg.AddNodes(chain.DeployerKey, nodeParams) if err != nil { - lggr.Errorw("Failed to add nodes", "err", deployment.MaybeDataErr(err)) + lggr.Errorw("Failed to add nodes", "chain", chain.String(), "err", deployment.MaybeDataErr(err)) return err } _, err = chain.Confirm(tx) diff --git a/deployment/ccip/changeset/cs_home_chain_test.go b/deployment/ccip/changeset/cs_home_chain_test.go index 55bc7466837..a06161f7086 100644 --- a/deployment/ccip/changeset/cs_home_chain_test.go +++ b/deployment/ccip/changeset/cs_home_chain_test.go @@ -3,7 +3,6 @@ package changeset import ( "testing" - chainsel "github.com/smartcontractkit/chain-selectors" "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" @@ -43,10 +42,7 @@ func TestDeployHomeChain(t *testing.T) { require.NotNil(t, state.Chains[homeChainSel].RMNHome) snap, err := state.View([]uint64{homeChainSel}) require.NoError(t, err) - chainid, err := chainsel.ChainIdFromSelector(homeChainSel) - require.NoError(t, err) - chainName, err := chainsel.NameFromChainId(chainid) - require.NoError(t, err) + chainName := e.Chains[homeChainSel].Name() _, ok := snap[chainName] require.True(t, ok) capRegSnap, ok := snap[chainName].CapabilityRegistry[state.Chains[homeChainSel].CapabilityRegistry.Address().String()] diff --git a/deployment/ccip/changeset/cs_initial_add_chain.go b/deployment/ccip/changeset/cs_initial_add_chain.go index 0e425aef8c7..d18116b8a74 100644 --- a/deployment/ccip/changeset/cs_initial_add_chain.go +++ b/deployment/ccip/changeset/cs_initial_add_chain.go @@ -18,6 +18,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/merklemulti" + "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/internal" "github.com/smartcontractkit/chainlink/deployment/common/types" @@ -178,19 +179,20 @@ func configureChain( e.Logger.Errorw("Failed to load existing onchain state", "err") return err } + homeChain := e.Chains[c.HomeChainSel] capReg := existingState.Chains[c.HomeChainSel].CapabilityRegistry if capReg == nil { - e.Logger.Errorw("Failed to get capability registry") + e.Logger.Errorw("Failed to get capability registry", "chain", homeChain.String()) return fmt.Errorf("capability registry not found") } ccipHome := existingState.Chains[c.HomeChainSel].CCIPHome if ccipHome == nil { - e.Logger.Errorw("Failed to get ccip home", "err", err) + e.Logger.Errorw("Failed to get ccip home", "chain", homeChain.String(), "err", err) return fmt.Errorf("ccip home not found") } rmnHome := existingState.Chains[c.HomeChainSel].RMNHome if rmnHome == nil { - e.Logger.Errorw("Failed to get rmn home", "err", err) + e.Logger.Errorw("Failed to get rmn home", "chain", homeChain.String(), "err", err) return fmt.Errorf("rmn home not found") } @@ -267,7 +269,7 @@ func addChainConfig( if _, err := deployment.ConfirmIfNoError(h, tx, err); err != nil { return ccip_home.CCIPHomeChainConfigArgs{}, err } - lggr.Infow("Applied chain config updates", "chainConfig", chainConfig) + lggr.Infow("Applied chain config updates", "homeChain", h.String(), "addedChain", chainSelector, "chainConfig", chainConfig) return chainConfig, nil } @@ -301,17 +303,17 @@ func createDON( donID := latestDon.Id + 1 - err = internal.SetupCommitDON(donID, commitConfig, capReg, home, nodes, ccipHome) + err = internal.SetupCommitDON(lggr, donID, commitConfig, capReg, home, nodes, ccipHome) if err != nil { return fmt.Errorf("setup commit don: %w", err) } // TODO: bug in contract causing this to not work as expected. - err = internal.SetupExecDON(donID, execConfig, capReg, home, nodes, ccipHome) + err = internal.SetupExecDON(lggr, donID, execConfig, capReg, home, nodes, ccipHome) if err != nil { return fmt.Errorf("setup exec don: %w", err) } - return ValidateCCIPHomeConfigSetUp(capReg, ccipHome, newChainSel) + return ValidateCCIPHomeConfigSetUp(lggr, capReg, ccipHome, newChainSel) } func addDON( @@ -369,6 +371,15 @@ func addDON( if err != nil { return err } + lggr.Debugw("Fetched OCR3 Configs", + "MultiOCR3BaseOCRConfig.F", ocrConfig.ConfigInfo.F, + "MultiOCR3BaseOCRConfig.N", ocrConfig.ConfigInfo.N, + "MultiOCR3BaseOCRConfig.IsSignatureVerificationEnabled", ocrConfig.ConfigInfo.IsSignatureVerificationEnabled, + "Signers", ocrConfig.Signers, + "Transmitters", ocrConfig.Transmitters, + "configDigest", hex.EncodeToString(ocrConfig.ConfigInfo.ConfigDigest[:]), + "chain", dest.String(), + ) // TODO: assertions to be done as part of full state // resprentation validation CCIP-3047 if mapOfframpOCR3Configs[pluginType].ConfigDigest != ocrConfig.ConfigInfo.ConfigDigest { @@ -400,6 +411,7 @@ func addDON( // ValidateCCIPHomeConfigSetUp checks that the commit and exec active and candidate configs are set up correctly func ValidateCCIPHomeConfigSetUp( + lggr logger.Logger, capReg *capabilities_registry.CapabilitiesRegistry, ccipHome *ccip_home.CCIPHome, chainSel uint64, @@ -420,10 +432,12 @@ func ValidateCCIPHomeConfigSetUp( if err != nil { return fmt.Errorf("get active commit digest: %w", err) } + lggr.Debugw("Fetched active commit digest", "commitActiveDigest", hex.EncodeToString(commitActiveDigest[:])) commitCandidateDigest, err := ccipHome.GetCandidateDigest(nil, donID, uint8(cctypes.PluginTypeCCIPCommit)) if err != nil { return fmt.Errorf("get commit candidate digest: %w", err) } + lggr.Debugw("Fetched candidate commit digest", "commitCandidateDigest", hex.EncodeToString(commitCandidateDigest[:])) if commitConfigs.ActiveConfig.ConfigDigest == [32]byte{} { return fmt.Errorf( "active config digest is empty for commit, expected nonempty, donID: %d, cfg: %+v, config digest from GetActiveDigest call: %x, config digest from GetCandidateDigest call: %x", @@ -439,6 +453,10 @@ func ValidateCCIPHomeConfigSetUp( if err != nil { return fmt.Errorf("get all exec configs: %w", err) } + lggr.Debugw("Fetched exec configs", + "ActiveConfig.ConfigDigest", hex.EncodeToString(execConfigs.ActiveConfig.ConfigDigest[:]), + "CandidateConfig.ConfigDigest", hex.EncodeToString(execConfigs.CandidateConfig.ConfigDigest[:]), + ) if execConfigs.ActiveConfig.ConfigDigest == [32]byte{} { return fmt.Errorf("active config digest is empty for exec, expected nonempty, cfg: %v", execConfigs.ActiveConfig) } diff --git a/deployment/ccip/changeset/cs_prerequisites.go b/deployment/ccip/changeset/cs_prerequisites.go index e610dfaaeeb..2386d3bb784 100644 --- a/deployment/ccip/changeset/cs_prerequisites.go +++ b/deployment/ccip/changeset/cs_prerequisites.go @@ -153,10 +153,9 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address } }) if err != nil { - lggr.Errorw("Failed to deploy mock RMN", "err", err) + lggr.Errorw("Failed to deploy mock RMN", "chain", chain.String(), "err", err) return err } - lggr.Infow("deployed mock RMN", "addr", rmn.Address) rmnProxyContract, err := deployment.DeployContract(lggr, chain, ab, func(chain deployment.Chain) deployment.ContractDeploy[*rmn_proxy_contract.RMNProxyContract] { rmnProxyAddr, tx2, rmnProxy, err2 := rmn_proxy_contract.DeployRMNProxyContract( @@ -169,10 +168,9 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address } }) if err != nil { - lggr.Errorw("Failed to deploy RMNProxyNew", "err", err) + lggr.Errorw("Failed to deploy RMNProxyExisting", "chain", chain.String(), "err", err) return err } - lggr.Infow("deployed RMNProxyNew", "addr", rmnProxyContract.Address) rmnProxy = rmnProxyContract.Contract } if tokenAdminReg == nil { @@ -186,13 +184,12 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address } }) if err != nil { - e.Logger.Errorw("Failed to deploy token admin registry", "err", err) + e.Logger.Errorw("Failed to deploy token admin registry", "chain", chain.String(), "err", err) return err } - e.Logger.Infow("deployed tokenAdminRegistry", "addr", tokenAdminRegistry) tokenAdminReg = tokenAdminRegistry.Contract } else { - e.Logger.Infow("tokenAdminRegistry already deployed", "addr", tokenAdminReg.Address) + e.Logger.Infow("tokenAdminRegistry already deployed", "chain", chain.String(), "addr", tokenAdminReg.Address) } if registryModule == nil { customRegistryModule, err := deployment.DeployContract(e.Logger, chain, ab, @@ -206,29 +203,28 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address } }) if err != nil { - e.Logger.Errorw("Failed to deploy custom registry module", "err", err) + e.Logger.Errorw("Failed to deploy custom registry module", "chain", chain.String(), "err", err) return err } - e.Logger.Infow("deployed custom registry module", "addr", customRegistryModule) registryModule = customRegistryModule.Contract } else { - e.Logger.Infow("custom registry module already deployed", "addr", registryModule.Address) + e.Logger.Infow("custom registry module already deployed", "chain", chain.String(), "addr", registryModule.Address) } isRegistryAdded, err := tokenAdminReg.IsRegistryModule(nil, registryModule.Address()) if err != nil { - e.Logger.Errorw("Failed to check if registry module is added on token admin registry", "err", err) + e.Logger.Errorw("Failed to check if registry module is added on token admin registry", "chain", chain.String(), "err", err) return fmt.Errorf("failed to check if registry module is added on token admin registry: %w", err) } if !isRegistryAdded { tx, err := tokenAdminReg.AddRegistryModule(chain.DeployerKey, registryModule.Address()) if err != nil { - e.Logger.Errorw("Failed to assign registry module on token admin registry", "err", err) + e.Logger.Errorw("Failed to assign registry module on token admin registry", "chain", chain.String(), "err", err) return fmt.Errorf("failed to assign registry module on token admin registry: %w", err) } _, err = chain.Confirm(tx) if err != nil { - e.Logger.Errorw("Failed to confirm assign registry module on token admin registry", "err", err) + e.Logger.Errorw("Failed to confirm assign registry module on token admin registry", "chain", chain.String(), "err", err) return fmt.Errorf("failed to confirm assign registry module on token admin registry: %w", err) } e.Logger.Infow("assigned registry module on token admin registry") @@ -245,10 +241,9 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address } }) if err != nil { - lggr.Errorw("Failed to deploy weth9", "err", err) + lggr.Errorw("Failed to deploy weth9", "chain", chain.String(), "err", err) return err } - lggr.Infow("deployed weth9", "addr", weth.Address) weth9Contract = weth.Contract } else { lggr.Infow("weth9 already deployed", "addr", weth9Contract.Address) @@ -268,16 +263,16 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address } }) if err != nil { - e.Logger.Errorw("Failed to deploy router", "err", err) + e.Logger.Errorw("Failed to deploy router", "chain", chain.String(), "err", err) return err } - e.Logger.Infow("deployed router", "addr", routerContract.Address) + r = routerContract.Contract } else { - e.Logger.Infow("router already deployed", "addr", chainState.Router.Address) + e.Logger.Infow("router already deployed", "chain", chain.String(), "addr", chainState.Router.Address) } if deployOpts.Multicall3Enabled && mc3 == nil { - multicall3Contract, err := deployment.DeployContract(e.Logger, chain, ab, + _, err := deployment.DeployContract(e.Logger, chain, ab, func(chain deployment.Chain) deployment.ContractDeploy[*multicall3.Multicall3] { multicall3Addr, tx2, multicall3Wrapper, err2 := multicall3.DeployMulticall3( chain.DeployerKey, @@ -288,12 +283,13 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address } }) if err != nil { - e.Logger.Errorw("Failed to deploy ccip multicall", "err", err) + e.Logger.Errorw("Failed to deploy ccip multicall", "chain", chain.String(), "err", err) return err } - e.Logger.Infow("deployed ccip multicall", "addr", multicall3Contract.Address) } else { - e.Logger.Info("ccip multicall already deployed", "addr", mc3.Address) + if mc3 != nil { + e.Logger.Info("ccip multicall already deployed", "chain", chain.String(), "addr", mc3.Address) + } } if isUSDC { token, pool, messenger, transmitter, err1 := DeployUSDC(e.Logger, chain, ab, rmnProxy.Address(), r.Address()) @@ -301,7 +297,7 @@ func deployPrerequisiteContracts(e deployment.Environment, ab deployment.Address return err1 } e.Logger.Infow("Deployed USDC contracts", - "chainSelector", chain.Selector, + "chain", chain.String(), "token", token.Address(), "pool", pool.Address(), "transmitter", transmitter.Address(), diff --git a/deployment/ccip/changeset/internal/deploy_home_chain.go b/deployment/ccip/changeset/internal/deploy_home_chain.go index 27052b04d07..6328c329b9a 100644 --- a/deployment/ccip/changeset/internal/deploy_home_chain.go +++ b/deployment/ccip/changeset/internal/deploy_home_chain.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" @@ -173,6 +174,7 @@ func BuildSetOCR3ConfigArgs( } func SetupExecDON( + lggr logger.Logger, donID uint32, execConfig ccip_home.CCIPHomeOCR3Config, capReg *capabilities_registry.CapabilitiesRegistry, @@ -212,6 +214,7 @@ func SetupExecDON( if _, err := deployment.ConfirmIfNoError(home, tx, err); err != nil { return fmt.Errorf("confirm update don w/ exec config: %w", err) } + lggr.Infow("Updated DON with exec config", "chain", home.String(), "donID", donID, "txHash", tx.Hash().Hex(), "setCandidateCall", encodedSetCandidateCall) execCandidateDigest, err := ccipHome.GetCandidateDigest(nil, donID, execConfig.PluginType) if err != nil { @@ -221,7 +224,7 @@ func SetupExecDON( if execCandidateDigest == [32]byte{} { return fmt.Errorf("candidate digest is empty, expected nonempty") } - + lggr.Infow("Got exec candidate digest", "chain", home.String(), "donID", donID, "execCandidateDigest", execCandidateDigest) // promote candidate call encodedPromotionCall, err := CCIPHomeABI.Pack( "promoteCandidateAndRevokeActive", @@ -257,6 +260,7 @@ func SetupExecDON( if bn == 0 { return fmt.Errorf("UpdateDON tx not confirmed") } + lggr.Infow("Promoted exec candidate", "chain", home.String(), "donID", donID, "txHash", tx.Hash().Hex(), "promotionCall", encodedPromotionCall) // check if candidate digest is promoted pEvent, err := ccipHome.FilterConfigPromoted(&bind.FilterOpts{ Context: context.Background(), @@ -293,14 +297,20 @@ func SetupExecDON( return fmt.Errorf("get all exec configs 2nd time: %w", err) } - // print the above info - fmt.Printf("completed exec DON creation and promotion: donID: %d execCandidateDigest: %x, execActiveDigest: %x, execCandidateDigestFromGetAllConfigs: %x, execActiveDigestFromGetAllConfigs: %x\n", - donID, execCandidateDigest, execActiveDigest, execConfigs.CandidateConfig.ConfigDigest, execConfigs.ActiveConfig.ConfigDigest) + // log the above info + lggr.Infow("completed exec DON creation and promotion", + "donID", donID, + "execCandidateDigest", execCandidateDigest, + "execActiveDigest", execActiveDigest, + "execCandidateDigestFromGetAllConfigs", execConfigs.CandidateConfig.ConfigDigest, + "execActiveDigestFromGetAllConfigs", execConfigs.ActiveConfig.ConfigDigest, + ) return nil } func SetupCommitDON( + lggr logger.Logger, donID uint32, commitConfig ccip_home.CCIPHomeOCR3Config, capReg *capabilities_registry.CapabilitiesRegistry, @@ -331,7 +341,7 @@ func SetupCommitDON( if _, err := deployment.ConfirmIfNoError(home, tx, err); err != nil { return fmt.Errorf("confirm add don w/ commit config: %w", err) } - + lggr.Debugw("Added DON with commit config", "chain", home.String(), "donID", donID, "txHash", tx.Hash().Hex(), "setCandidateCall", encodedSetCandidateCall) commitCandidateDigest, err := ccipHome.GetCandidateDigest(nil, donID, commitConfig.PluginType) if err != nil { return fmt.Errorf("get commit candidate digest: %w", err) @@ -340,7 +350,7 @@ func SetupCommitDON( if commitCandidateDigest == [32]byte{} { return fmt.Errorf("candidate digest is empty, expected nonempty") } - fmt.Printf("commit candidate digest after setCandidate: %x\n", commitCandidateDigest) + lggr.Debugw("Got commit candidate digest", "chain", home.String(), "donID", donID, "commitCandidateDigest", commitCandidateDigest) encodedPromotionCall, err := CCIPHomeABI.Pack( "promoteCandidateAndRevokeActive", @@ -373,6 +383,7 @@ func SetupCommitDON( if _, err := deployment.ConfirmIfNoError(home, tx, err); err != nil { return fmt.Errorf("confirm update don w/ commit config: %w", err) } + lggr.Debugw("Promoted commit candidate", "chain", home.String(), "donID", donID, "txHash", tx.Hash().Hex(), "promotionCall", encodedPromotionCall) // check that candidate digest is empty. commitCandidateDigest, err = ccipHome.GetCandidateDigest(nil, donID, commitConfig.PluginType) @@ -399,9 +410,14 @@ func SetupCommitDON( return fmt.Errorf("get all commit configs 2nd time: %w", err) } - // print the above information - fmt.Printf("completed commit DON creation and promotion: donID: %d, commitCandidateDigest: %x, commitActiveDigest: %x, commitCandidateDigestFromGetAllConfigs: %x, commitActiveDigestFromGetAllConfigs: %x\n", - donID, commitCandidateDigest, commitActiveDigest, commitConfigs.CandidateConfig.ConfigDigest, commitConfigs.ActiveConfig.ConfigDigest) + // log the above information + lggr.Infow("completed commit DON creation and promotion", + "donID", donID, + "commitCandidateDigest", commitCandidateDigest, + "commitActiveDigest", commitActiveDigest, + "commitCandidateDigestFromGetAllConfigs", commitConfigs.CandidateConfig.ConfigDigest, + "commitActiveDigestFromGetAllConfigs", commitConfigs.ActiveConfig.ConfigDigest, + ) return nil } diff --git a/deployment/ccip/changeset/state.go b/deployment/ccip/changeset/state.go index 20763523141..0820f42d0c7 100644 --- a/deployment/ccip/changeset/state.go +++ b/deployment/ccip/changeset/state.go @@ -13,7 +13,6 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" - chainsel "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/view" @@ -134,35 +133,35 @@ func (c CCIPChainState) GenerateView() (view.ChainView, error) { if c.Router != nil { routerView, err := v1_2.GenerateRouterView(c.Router) if err != nil { - return chainView, err + return chainView, errors.Wrapf(err, "failed to generate router view for router %s", c.Router.Address().String()) } chainView.Router[c.Router.Address().Hex()] = routerView } if c.TokenAdminRegistry != nil { taView, err := v1_5.GenerateTokenAdminRegistryView(c.TokenAdminRegistry) if err != nil { - return chainView, err + return chainView, errors.Wrapf(err, "failed to generate token admin registry view for token admin registry %s", c.TokenAdminRegistry.Address().String()) } chainView.TokenAdminRegistry[c.TokenAdminRegistry.Address().Hex()] = taView } if c.NonceManager != nil { nmView, err := v1_6.GenerateNonceManagerView(c.NonceManager) if err != nil { - return chainView, err + return chainView, errors.Wrapf(err, "failed to generate nonce manager view for nonce manager %s", c.NonceManager.Address().String()) } chainView.NonceManager[c.NonceManager.Address().Hex()] = nmView } if c.RMNRemote != nil { rmnView, err := v1_6.GenerateRMNRemoteView(c.RMNRemote) if err != nil { - return chainView, err + return chainView, errors.Wrapf(err, "failed to generate rmn remote view for rmn remote %s", c.RMNRemote.Address().String()) } chainView.RMN[c.RMNRemote.Address().Hex()] = rmnView } if c.FeeQuoter != nil && c.Router != nil && c.TokenAdminRegistry != nil { fqView, err := v1_6.GenerateFeeQuoterView(c.FeeQuoter, c.Router, c.TokenAdminRegistry) if err != nil { - return chainView, err + return chainView, errors.Wrapf(err, "failed to generate fee quoter view for fee quoter %s", c.FeeQuoter.Address().String()) } chainView.FeeQuoter[c.FeeQuoter.Address().Hex()] = fqView } @@ -174,7 +173,7 @@ func (c CCIPChainState) GenerateView() (view.ChainView, error) { c.TokenAdminRegistry, ) if err != nil { - return chainView, err + return chainView, errors.Wrapf(err, "failed to generate on ramp view for on ramp %s", c.OnRamp.Address().String()) } chainView.OnRamp[c.OnRamp.Address().Hex()] = onRampView } @@ -185,7 +184,7 @@ func (c CCIPChainState) GenerateView() (view.ChainView, error) { c.Router, ) if err != nil { - return chainView, err + return chainView, errors.Wrapf(err, "failed to generate off ramp view for off ramp %s", c.OffRamp.Address().String()) } chainView.OffRamp[c.OffRamp.Address().Hex()] = offRampView } @@ -193,7 +192,7 @@ func (c CCIPChainState) GenerateView() (view.ChainView, error) { if c.CommitStore != nil { commitStoreView, err := v1_5.GenerateCommitStoreView(c.CommitStore) if err != nil { - return chainView, err + return chainView, errors.Wrapf(err, "failed to generate commit store view for commit store %s", c.CommitStore.Address().String()) } chainView.CommitStore[c.CommitStore.Address().Hex()] = commitStoreView } @@ -201,28 +200,28 @@ func (c CCIPChainState) GenerateView() (view.ChainView, error) { if c.RMNProxyNew != nil { rmnProxyView, err := v1_0.GenerateRMNProxyView(c.RMNProxyNew) if err != nil { - return chainView, err + return chainView, errors.Wrapf(err, "failed to generate rmn proxy view for rmn proxy %s", c.RMNProxyNew.Address().String()) } chainView.RMNProxy[c.RMNProxyNew.Address().Hex()] = rmnProxyView } if c.CapabilityRegistry != nil { capRegView, err := common_v1_0.GenerateCapabilityRegistryView(c.CapabilityRegistry) if err != nil { - return chainView, err + return chainView, errors.Wrapf(err, "failed to generate capability registry view for capability registry %s", c.CapabilityRegistry.Address().String()) } chainView.CapabilityRegistry[c.CapabilityRegistry.Address().Hex()] = capRegView } if c.MCMSWithTimelockState.Timelock != nil { mcmsView, err := c.MCMSWithTimelockState.GenerateMCMSWithTimelockView() if err != nil { - return chainView, err + return chainView, errors.Wrapf(err, "failed to generate MCMS with timelock view for MCMS with timelock %s", c.MCMSWithTimelockState.Timelock.Address().String()) } chainView.MCMSWithTimelock = mcmsView } if c.LinkToken != nil { linkTokenView, err := common_v1_0.GenerateLinkTokenView(c.LinkToken) if err != nil { - return chainView, err + return chainView, errors.Wrapf(err, "failed to generate link token view for link token %s", c.LinkToken.Address().String()) } chainView.LinkToken = linkTokenView } @@ -242,12 +241,7 @@ type CCIPOnChainState struct { func (s CCIPOnChainState) View(chains []uint64) (map[string]view.ChainView, error) { m := make(map[string]view.ChainView) for _, chainSelector := range chains { - // TODO: Need a utility for this - chainid, err := chainsel.ChainIdFromSelector(chainSelector) - if err != nil { - return m, err - } - chainName, err := chainsel.NameFromChainId(chainid) + chainInfo, err := deployment.ChainInfo(chainSelector) if err != nil { return m, err } @@ -259,7 +253,7 @@ func (s CCIPOnChainState) View(chains []uint64) (map[string]view.ChainView, erro if err != nil { return m, err } - m[chainName] = chainView + m[chainInfo.ChainName] = chainView } return m, nil } diff --git a/deployment/ccip/changeset/test_assertions.go b/deployment/ccip/changeset/test_assertions.go index ad2ea4257ea..a7d3ecf61f8 100644 --- a/deployment/ccip/changeset/test_assertions.go +++ b/deployment/ccip/changeset/test_assertions.go @@ -259,6 +259,40 @@ func (c *commitReportTracker) allCommited(sourceChainSelector uint64) bool { return true } +// ConfirmMultipleCommits waits for multiple ccipocr3.SeqNumRange to be committed by the Offramp. +// Waiting is done in parallel per every sourceChain/destChain (lane) passed as argument. +func ConfirmMultipleCommits( + t *testing.T, + chains map[uint64]deployment.Chain, + state map[uint64]CCIPChainState, + startBlocks map[uint64]*uint64, + enforceSingleCommit bool, + expectedSeqNums map[SourceDestPair]ccipocr3.SeqNumRange, +) error { + errGrp := &errgroup.Group{} + + for sourceDest, seqRange := range expectedSeqNums { + seqRange := seqRange + srcChain := sourceDest.SourceChainSelector + destChain := sourceDest.DestChainSelector + + errGrp.Go(func() error { + _, err := ConfirmCommitWithExpectedSeqNumRange( + t, + chains[srcChain], + chains[destChain], + state[destChain].OffRamp, + startBlocks[destChain], + seqRange, + enforceSingleCommit, + ) + return err + }) + } + + return errGrp.Wait() +} + // ConfirmCommitWithExpectedSeqNumRange waits for a commit report on the destination chain with the expected sequence number range. // startBlock is the block number to start watching from. // If startBlock is nil, it will start watching from the latest block. @@ -449,7 +483,7 @@ func ConfirmExecWithSeqNrs( return nil, fmt.Errorf("no expected sequence numbers provided") } - timer := time.NewTimer(3 * time.Minute) + timer := time.NewTimer(8 * time.Minute) defer timer.Stop() tick := time.NewTicker(3 * time.Second) defer tick.Stop() @@ -564,6 +598,22 @@ func RequireConsistently(t *testing.T, condition func() bool, duration time.Dura } } +func SeqNumberRageToSlice(seqRanges map[SourceDestPair]ccipocr3.SeqNumRange) map[SourceDestPair][]uint64 { + flatten := make(map[SourceDestPair][]uint64) + + for srcDst, seqRange := range seqRanges { + if _, ok := flatten[srcDst]; !ok { + flatten[srcDst] = make([]uint64, 0, seqRange.End()-seqRange.Start()+1) + } + + for i := seqRange.Start(); i <= seqRange.End(); i++ { + flatten[srcDst] = append(flatten[srcDst], uint64(i)) + } + } + + return flatten +} + const ( EXECUTION_STATE_UNTOUCHED = 0 EXECUTION_STATE_INPROGRESS = 1 diff --git a/deployment/ccip/changeset/test_helpers.go b/deployment/ccip/changeset/test_helpers.go index 742fe39200a..1ee8b0d0e42 100644 --- a/deployment/ccip/changeset/test_helpers.go +++ b/deployment/ccip/changeset/test_helpers.go @@ -286,9 +286,15 @@ func NewMemoryEnvironmentWithJobsAndContracts(t *testing.T, lggr logger.Logger, TimelockMinDelay: big.NewInt(0), } } - var usdcChains []uint64 - if tCfg != nil && tCfg.IsUSDC { - usdcChains = allChains + var ( + usdcChains []uint64 + isMulticall3 bool + ) + if tCfg != nil { + if tCfg.IsUSDC { + usdcChains = allChains + } + isMulticall3 = tCfg.IsMultiCall3 } // Need to deploy prerequisites first so that we can form the USDC config // no proposals to be made, timelock can be passed as nil here @@ -303,6 +309,7 @@ func NewMemoryEnvironmentWithJobsAndContracts(t *testing.T, lggr logger.Logger, ChainSelectors: allChains, Opts: []PrerequisiteOpt{ WithUSDCChains(usdcChains), + WithMulticall3(isMulticall3), }, }, }, @@ -1225,6 +1232,78 @@ func Transfer( return msgSentEvent, startBlocks } +type TestTransferRequest struct { + Name string + SourceChain, DestChain uint64 + Receiver common.Address + ExpectedStatus int + // optional + Tokens []router.ClientEVMTokenAmount + Data []byte + ExtraArgs []byte + ExpectedTokenBalances map[common.Address]*big.Int +} + +// TransferMultiple sends multiple CCIPMessages (represented as TestTransferRequest) sequentially. +// It verifies whether message is not reverted on the source and proper event is emitted by OnRamp. +// However, it doesn't wait for message to be committed or executed. Therefore, you can send multiple messages very fast, +// but you need to make sure they are committed/executed on your own (if that's the intention). +// It saves some time during test execution, because we let plugins batch instead of executing one by one +// If you want to wait for execution in a "batch" manner you will need to pass maps returned by TransferMultiple to +// either ConfirmMultipleCommits (for commit) or ConfirmExecWithSeqNrsForAll (for exec). Check example usage in the tests. +func TransferMultiple( + ctx context.Context, + t *testing.T, + env deployment.Environment, + state CCIPOnChainState, + requests []TestTransferRequest, +) ( + map[uint64]*uint64, + map[SourceDestPair]cciptypes.SeqNumRange, + map[SourceDestPair]map[uint64]int, + map[uint64]map[TokenReceiverIdentifier]*big.Int, +) { + startBlocks := make(map[uint64]*uint64) + expectedSeqNums := make(map[SourceDestPair]cciptypes.SeqNumRange) + expectedExecutionStates := make(map[SourceDestPair]map[uint64]int) + expectedTokenBalances := make(TokenBalanceAccumulator) + + for _, tt := range requests { + t.Run(tt.Name, func(t *testing.T) { + expectedTokenBalances.add(tt.DestChain, tt.Receiver, tt.ExpectedTokenBalances) + + pairId := SourceDestPair{ + SourceChainSelector: tt.SourceChain, + DestChainSelector: tt.DestChain, + } + + msg, blocks := Transfer( + ctx, t, env, state, tt.SourceChain, tt.DestChain, tt.Tokens, tt.Receiver, tt.Data, tt.ExtraArgs) + if _, ok := expectedExecutionStates[pairId]; !ok { + expectedExecutionStates[pairId] = make(map[uint64]int) + } + expectedExecutionStates[pairId][msg.SequenceNumber] = tt.ExpectedStatus + + if _, ok := startBlocks[tt.DestChain]; !ok { + startBlocks[tt.DestChain] = blocks[tt.DestChain] + } + + seqNr, ok := expectedSeqNums[pairId] + if ok { + expectedSeqNums[pairId] = cciptypes.NewSeqNumRange( + seqNr.Start(), cciptypes.SeqNum(msg.SequenceNumber), + ) + } else { + expectedSeqNums[pairId] = cciptypes.NewSeqNumRange( + cciptypes.SeqNum(msg.SequenceNumber), cciptypes.SeqNum(msg.SequenceNumber), + ) + } + }) + } + + return startBlocks, expectedSeqNums, expectedExecutionStates, expectedTokenBalances +} + // TransferAndWaitForSuccess sends a message from sourceChain to destChain and waits for it to be executed func TransferAndWaitForSuccess( ctx context.Context, @@ -1258,6 +1337,60 @@ func TransferAndWaitForSuccess( require.Equal(t, expectedStatus, states[identifier][msgSentEvent.SequenceNumber]) } +// TokenBalanceAccumulator is a convenient accumulator to aggregate expected balances of different tokens +// used across the tests. You can iterate over your test cases and build the final "expected" balances for tokens (per chain, per sender) +// For instance, if your test runs multiple transfers for the same token, and you want to verify the balance of tokens at +// the end of the execution, you can simply use that struct for aggregating expected tokens +// Please also see WaitForTokenBalances to better understand how you can assert token balances +type TokenBalanceAccumulator map[uint64]map[TokenReceiverIdentifier]*big.Int + +func (t TokenBalanceAccumulator) add( + destChain uint64, + receiver common.Address, + expectedBalance map[common.Address]*big.Int) { + for token, balance := range expectedBalance { + tkIdentifier := TokenReceiverIdentifier{token, receiver} + + if _, ok := t[destChain]; !ok { + t[destChain] = make(map[TokenReceiverIdentifier]*big.Int) + } + actual, ok := t[destChain][tkIdentifier] + if !ok { + actual = big.NewInt(0) + } + t[destChain][tkIdentifier] = new(big.Int).Add(actual, balance) + } +} + +type TokenReceiverIdentifier struct { + token common.Address + receiver common.Address +} + +// WaitForTokenBalances waits for multiple ERC20 tokens to reach a particular balance +// It works in a batch manner, so you can pass and exhaustive list of different tokens (per senders and chains) +// and it would work concurrently for the balance to be met. Check WaitForTheTokenBalance to see how balance +// checking is made for a token/receiver pair +func WaitForTokenBalances( + ctx context.Context, + t *testing.T, + chains map[uint64]deployment.Chain, + expectedBalances map[uint64]map[TokenReceiverIdentifier]*big.Int, +) { + errGrp := &errgroup.Group{} + for chainID, tokens := range expectedBalances { + for id, balance := range tokens { + id := id + balance := balance + errGrp.Go(func() error { + WaitForTheTokenBalance(ctx, t, id.token, id.receiver, chains[chainID], balance) + return nil + }) + } + } + require.NoError(t, errGrp.Wait()) +} + func WaitForTheTokenBalance( ctx context.Context, t *testing.T, diff --git a/deployment/ccip/changeset/test_usdc_helpers.go b/deployment/ccip/changeset/test_usdc_helpers.go index 75994ec9356..55f1bd25a36 100644 --- a/deployment/ccip/changeset/test_usdc_helpers.go +++ b/deployment/ccip/changeset/test_usdc_helpers.go @@ -175,13 +175,13 @@ func DeployUSDC( } }) if err != nil { - lggr.Errorw("Failed to deploy USDC token", "err", err) + lggr.Errorw("Failed to deploy USDC token", "chain", chain.String(), "err", err) return nil, nil, nil, nil, err } tx, err := token.Contract.GrantMintRole(chain.DeployerKey, chain.DeployerKey.From) if err != nil { - lggr.Errorw("Failed to grant mint role", "token", token.Contract.Address(), "err", err) + lggr.Errorw("Failed to grant mint role", "chain", chain.String(), "token", token.Contract.Address(), "err", err) return nil, nil, nil, nil, err } _, err = chain.Confirm(tx) @@ -207,12 +207,10 @@ func DeployUSDC( } }) if err != nil { - lggr.Errorw("Failed to deploy mock USDC transmitter", "err", err) + lggr.Errorw("Failed to deploy mock USDC transmitter", "chain", chain.String(), "err", err) return nil, nil, nil, nil, err } - lggr.Infow("deployed mock USDC transmitter", "addr", transmitter.Address) - messenger, err := deployment.DeployContract(lggr, chain, addresses, func(chain deployment.Chain) deployment.ContractDeploy[*mock_usdc_token_messenger.MockE2EUSDCTokenMessenger] { messengerAddress, tx, messengerContract, err2 := mock_usdc_token_messenger.DeployMockE2EUSDCTokenMessenger( @@ -230,10 +228,9 @@ func DeployUSDC( } }) if err != nil { - lggr.Errorw("Failed to deploy USDC token messenger", "err", err) + lggr.Errorw("Failed to deploy USDC token messenger", "chain", chain.String(), "err", err) return nil, nil, nil, nil, err } - lggr.Infow("deployed mock USDC token messenger", "addr", messenger.Address) tokenPool, err := deployment.DeployContract(lggr, chain, addresses, func(chain deployment.Chain) deployment.ContractDeploy[*usdc_token_pool.USDCTokenPool] { @@ -255,10 +252,9 @@ func DeployUSDC( } }) if err != nil { - lggr.Errorw("Failed to deploy USDC token pool", "err", err) + lggr.Errorw("Failed to deploy USDC token pool", "chain", chain.String(), "err", err) return nil, nil, nil, nil, err } - lggr.Infow("deployed USDC token pool", "addr", tokenPool.Address) return token.Contract, tokenPool.Contract, messenger.Contract, transmitter.Contract, nil } diff --git a/deployment/common/changeset/deploy_link_token.go b/deployment/common/changeset/deploy_link_token.go index 5728e977c47..292c07c93df 100644 --- a/deployment/common/changeset/deploy_link_token.go +++ b/deployment/common/changeset/deploy_link_token.go @@ -52,7 +52,7 @@ func deployLinkTokenContract( } }) if err != nil { - lggr.Errorw("Failed to deploy link token", "err", err) + lggr.Errorw("Failed to deploy link token", "chain", chain.String(), "err", err) return linkToken, err } return linkToken, nil diff --git a/deployment/common/changeset/internal/mcms.go b/deployment/common/changeset/internal/mcms.go index 5694f83786b..281f43924f4 100644 --- a/deployment/common/changeset/internal/mcms.go +++ b/deployment/common/changeset/internal/mcms.go @@ -6,6 +6,7 @@ import ( owner_helpers "github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/smartcontractkit/chainlink/deployment/common/view/v1_0" @@ -30,7 +31,7 @@ func DeployMCMSWithConfig( } }) if err != nil { - lggr.Errorw("Failed to deploy mcm", "err", err) + lggr.Errorw("Failed to deploy mcm", "chain", chain.String(), "err", err) return mcm, err } mcmsTx, err := mcm.Contract.SetConfig(chain.DeployerKey, @@ -42,7 +43,7 @@ func DeployMCMSWithConfig( false, ) if _, err := deployment.ConfirmIfNoError(chain, mcmsTx, err); err != nil { - lggr.Errorw("Failed to confirm mcm config", "err", err) + lggr.Errorw("Failed to confirm mcm config", "chain", chain.String(), "err", err) return mcm, err } return mcm, nil @@ -115,15 +116,14 @@ func DeployMCMSWithTimelockContracts( } }) if err != nil { - lggr.Errorw("Failed to deploy timelock", "err", err) + lggr.Errorw("Failed to deploy timelock", "chain", chain.String(), "err", err) return nil, err } - lggr.Infow("deployed timelock", "addr", timelock.Address) // 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) if _, err := deployment.ConfirmIfNoError(chain, tx, err); err != nil { - lggr.Errorw("Failed to grant timelock admin role", "err", err) + lggr.Errorw("Failed to grant timelock admin role", "chain", chain.String(), "err", err) return nil, err } // After the proposer cycle is validated, diff --git a/deployment/common/view/nops.go b/deployment/common/view/nops.go index 7d705f694d3..61e16d59145 100644 --- a/deployment/common/view/nops.go +++ b/deployment/common/view/nops.go @@ -4,7 +4,9 @@ import ( "context" "fmt" + "github.com/pkg/errors" nodev1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/node" + "github.com/smartcontractkit/chainlink/deployment" ) @@ -39,7 +41,7 @@ func GenerateNopsView(nodeIds []string, oc deployment.OffchainClient) (map[strin // get node info nodeDetails, err := oc.GetNode(context.Background(), &nodev1.GetNodeRequest{Id: node.NodeID}) if err != nil { - return nv, err + return nv, errors.Wrapf(err, "failed to get node details from offchain client for node %s", node.NodeID) } if nodeDetails == nil || nodeDetails.Node == nil { return nv, fmt.Errorf("failed to get node details from offchain client for node %s", node.NodeID) diff --git a/deployment/common/view/types/contract_state.go b/deployment/common/view/types/contract_state.go index f65c510af53..9b63d1f4db0 100644 --- a/deployment/common/view/types/contract_state.go +++ b/deployment/common/view/types/contract_state.go @@ -1,6 +1,8 @@ package types import ( + "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" ) @@ -14,11 +16,11 @@ type ContractMetaData struct { func NewContractMetaData(tv Meta, addr common.Address) (ContractMetaData, error) { tvStr, err := tv.TypeAndVersion(nil) if err != nil { - return ContractMetaData{}, err + return ContractMetaData{}, fmt.Errorf("failed to get type and version addr %s: %w", addr.String(), err) } owner, err := tv.Owner(nil) if err != nil { - return ContractMetaData{}, err + return ContractMetaData{}, fmt.Errorf("failed to get owner addr %s: %w", addr.String(), err) } return ContractMetaData{ TypeAndVersion: tvStr, diff --git a/deployment/common/view/v1_0/link_token.go b/deployment/common/view/v1_0/link_token.go index 38649037592..19b1b43aa02 100644 --- a/deployment/common/view/v1_0/link_token.go +++ b/deployment/common/view/v1_0/link_token.go @@ -1,6 +1,7 @@ package v1_0 import ( + "fmt" "math/big" "github.com/smartcontractkit/chainlink/deployment" @@ -18,15 +19,15 @@ type LinkTokenView struct { func GenerateLinkTokenView(lt *link_token.LinkToken) (LinkTokenView, error) { owner, err := lt.Owner(nil) if err != nil { - return LinkTokenView{}, err + return LinkTokenView{}, fmt.Errorf("view error to get link token owner addr %s: %w", lt.Address().String(), err) } decimals, err := lt.Decimals(nil) if err != nil { - return LinkTokenView{}, err + return LinkTokenView{}, fmt.Errorf("view error to get link token decimals addr %s: %w", lt.Address().String(), err) } totalSupply, err := lt.TotalSupply(nil) if err != nil { - return LinkTokenView{}, err + return LinkTokenView{}, fmt.Errorf("view error to get link token total supply addr %s: %w", lt.Address().String(), err) } return LinkTokenView{ ContractMetaData: types.ContractMetaData{ diff --git a/deployment/environment.go b/deployment/environment.go index bdf9fe6d5de..b4ac3a4f3f0 100644 --- a/deployment/environment.go +++ b/deployment/environment.go @@ -54,6 +54,27 @@ type Chain struct { // Note the Sign function can be abstract supporting a variety of key storage mechanisms (e.g. KMS etc). DeployerKey *bind.TransactOpts Confirm func(tx *types.Transaction) (uint64, error) + // Users are a set of keys that can be used to interact with the chain. + // These are distinct from the deployer key. + Users []*bind.TransactOpts +} + +func (c Chain) String() string { + chainInfo, err := ChainInfo(c.Selector) + if err != nil { + // we should never get here, if the selector is invalid it should not be in the environment + panic(err) + } + return fmt.Sprintf("%s (%d)", chainInfo.ChainName, chainInfo.ChainSelector) +} + +func (c Chain) Name() string { + chainInfo, err := ChainInfo(c.Selector) + if err != nil { + // we should never get here, if the selector is invalid it should not be in the environment + panic(err) + } + return chainInfo.ChainName } // Environment represents an instance of a deployed product @@ -144,7 +165,7 @@ func ConfirmIfNoError(chain Chain, tx *types.Transaction, err error) (uint64, er var d rpc.DataError ok := errors.As(err, &d) if ok { - return 0, fmt.Errorf("transaction reverted: Error %s ErrorData %v", d.Error(), d.ErrorData()) + return 0, fmt.Errorf("transaction reverted on chain %s: Error %s ErrorData %v", chain.String(), d.Error(), d.ErrorData()) } return 0, err } diff --git a/deployment/environment/clo/models/models_gen.go b/deployment/environment/clo/models/models_gen.go index baea1dbcbed..8d8f57c3b56 100644 --- a/deployment/environment/clo/models/models_gen.go +++ b/deployment/environment/clo/models/models_gen.go @@ -58,7 +58,7 @@ type AddAggregatorInput struct { type AddChainInput struct { NetworkID string `json:"networkID,omitempty"` Template string `json:"template,omitempty"` - // The Display Name lets a user differentiate multiple CCIP chains on the same network. It is not unique and used for display purposes only. + // The Display String lets a user differentiate multiple CCIP chains on the same network. It is not unique and used for display purposes only. DisplayName *string `json:"displayName,omitempty"` } @@ -102,7 +102,7 @@ type AddLaneInput struct { ChainAid string `json:"chainAID,omitempty"` ChainBid string `json:"chainBID,omitempty"` Template string `json:"template,omitempty"` - // The Display Name lets a user differentiate multiple CCIP chains on the same network. It is not unique and used for display purposes only. + // The Display String lets a user differentiate multiple CCIP chains on the same network. It is not unique and used for display purposes only. DisplayName *string `json:"displayName,omitempty"` } @@ -353,7 +353,7 @@ type CCIPChain struct { FeeTokens []string `json:"feeTokens,omitempty"` WrappedNativeToken string `json:"wrappedNativeToken,omitempty"` ArchivedAt *Time `json:"archivedAt,omitempty"` - // The Display Name lets a user differentiate multiple CCIP chains on the same network. It is not unique and used for display purposes only. + // The Display String lets a user differentiate multiple CCIP chains on the same network. It is not unique and used for display purposes only. DisplayName *string `json:"displayName,omitempty"` DeployedTemplate map[string]interface{} `json:"deployedTemplate,omitempty"` Labels map[string]interface{} `json:"labels,omitempty"` @@ -1001,7 +1001,7 @@ type ImportAggregatorInput struct { type ImportChainInput struct { NetworkID string `json:"networkID,omitempty"` Template string `json:"template,omitempty"` - // The Display Name lets a user differentiate multiple CCIP chains on the same network. It is not unique and used for display purposes only. + // The Display String lets a user differentiate multiple CCIP chains on the same network. It is not unique and used for display purposes only. DisplayName *string `json:"displayName,omitempty"` } @@ -1047,7 +1047,7 @@ type ImportLaneInput struct { ChainAid string `json:"chainAID,omitempty"` ChainBid string `json:"chainBID,omitempty"` Template string `json:"template,omitempty"` - // The Display Name lets a user differentiate multiple CCIP chains on the same network. It is not unique and used for display purposes only. + // The Display String lets a user differentiate multiple CCIP chains on the same network. It is not unique and used for display purposes only. DisplayName *string `json:"displayName,omitempty"` } diff --git a/deployment/environment/devenv/chain.go b/deployment/environment/devenv/chain.go index 407b898cb04..5c6c4336ed7 100644 --- a/deployment/environment/devenv/chain.go +++ b/deployment/environment/devenv/chain.go @@ -108,6 +108,10 @@ func NewChains(logger logger.Logger, configs []ChainConfig) (map[uint64]deployme if ec == nil { return nil, fmt.Errorf("failed to connect to chain %s", chainCfg.ChainName) } + chainInfo, err := deployment.ChainInfo(selector) + if err != nil { + return nil, fmt.Errorf("failed to get chain info for chain %s: %w", chainCfg.ChainName, err) + } chains[selector] = deployment.Chain{ Selector: selector, Client: ec, @@ -115,28 +119,24 @@ func NewChains(logger logger.Logger, configs []ChainConfig) (map[uint64]deployme Confirm: func(tx *types.Transaction) (uint64, error) { var blockNumber uint64 if tx == nil { - return 0, fmt.Errorf("tx was nil, nothing to confirm") + return 0, fmt.Errorf("tx was nil, nothing to confirm chain %s", chainInfo.ChainName) } ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute) defer cancel() - chainId, err := ec.ChainID(ctx) - if err != nil { - return blockNumber, fmt.Errorf("failed to get chain id: %w", err) - } receipt, err := bind.WaitMined(ctx, ec, tx) if err != nil { - return blockNumber, fmt.Errorf("failed to get confirmed receipt for chain %d: %w", chainId, err) + return blockNumber, fmt.Errorf("failed to get confirmed receipt for chain %s: %w", chainInfo.ChainName, err) } if receipt == nil { - return blockNumber, fmt.Errorf("receipt was nil for tx %s", tx.Hash().Hex()) + return blockNumber, fmt.Errorf("receipt was nil for tx %s chain %s", tx.Hash().Hex(), chainInfo.ChainName) } blockNumber = receipt.BlockNumber.Uint64() if receipt.Status == 0 { errReason, err := deployment.GetErrorReasonFromTx(ec, chainCfg.DeployerKey.From, tx, receipt) if err == nil && errReason != "" { - return blockNumber, fmt.Errorf("tx %s reverted,error reason: %s", tx.Hash().Hex(), errReason) + return blockNumber, fmt.Errorf("tx %s reverted,error reason: %s chain %s", tx.Hash().Hex(), errReason, chainInfo.ChainName) } - return blockNumber, fmt.Errorf("tx %s reverted, could not decode error reason", tx.Hash().Hex()) + return blockNumber, fmt.Errorf("tx %s reverted, could not decode error reason chain %s", tx.Hash().Hex(), chainInfo.ChainName) } return blockNumber, nil }, diff --git a/deployment/environment/memory/chain.go b/deployment/environment/memory/chain.go index 58f71a83a8c..40a20a02416 100644 --- a/deployment/environment/memory/chain.go +++ b/deployment/environment/memory/chain.go @@ -9,12 +9,12 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient/simulated" - "github.com/ethereum/go-ethereum/params" "github.com/stretchr/testify/require" chainsel "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" ) type EVMChain struct { @@ -66,7 +66,7 @@ func evmChain(t *testing.T, numUsers int) EVMChain { owner, err := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) require.NoError(t, err) genesis := types.GenesisAlloc{ - owner.From: {Balance: big.NewInt(0).Mul(big.NewInt(700000), big.NewInt(params.Ether))}} + owner.From: {Balance: assets.Ether(1_000_000).ToInt()}} // create a set of user keys var users []*bind.TransactOpts for j := 0; j < numUsers; j++ { @@ -75,7 +75,7 @@ func evmChain(t *testing.T, numUsers int) EVMChain { user, err := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) require.NoError(t, err) users = append(users, user) - genesis[user.From] = types.Account{Balance: big.NewInt(0).Mul(big.NewInt(700000), big.NewInt(params.Ether))} + genesis[user.From] = types.Account{Balance: assets.Ether(1_000_000).ToInt()} } // there have to be enough initial funds on each chain to allocate for all the nodes that share the given chain in the test backend := simulated.NewBackend(genesis, simulated.WithBlockGasLimit(50000000)) diff --git a/deployment/environment/memory/environment.go b/deployment/environment/memory/environment.go index f4692998d34..1693834c572 100644 --- a/deployment/environment/memory/environment.go +++ b/deployment/environment/memory/environment.go @@ -3,6 +3,7 @@ package memory import ( "context" "fmt" + "strconv" "testing" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -66,34 +67,35 @@ func generateMemoryChain(t *testing.T, inputs map[uint64]EVMChain) map[uint64]de chains := make(map[uint64]deployment.Chain) for cid, chain := range inputs { chain := chain - sel, err := chainsel.SelectorFromChainId(cid) + chainInfo, err := chainsel.GetChainDetailsByChainIDAndFamily(strconv.FormatUint(cid, 10), chainsel.FamilyEVM) require.NoError(t, err) backend := NewBackend(chain.Backend) - chains[sel] = deployment.Chain{ - Selector: sel, + chains[chainInfo.ChainSelector] = deployment.Chain{ + Selector: chainInfo.ChainSelector, Client: backend, DeployerKey: chain.DeployerKey, Confirm: func(tx *types.Transaction) (uint64, error) { if tx == nil { - return 0, fmt.Errorf("tx was nil, nothing to confirm") + return 0, fmt.Errorf("tx was nil, nothing to confirm, chain %s", chainInfo.ChainName) } for { backend.Commit() receipt, err := backend.TransactionReceipt(context.Background(), tx.Hash()) if err != nil { - t.Log("failed to get receipt", err) + t.Log("failed to get receipt", "chain", chainInfo.ChainName, err) continue } if receipt.Status == 0 { errReason, err := deployment.GetErrorReasonFromTx(chain.Backend.Client(), chain.DeployerKey.From, tx, receipt) if err == nil && errReason != "" { - return 0, fmt.Errorf("tx %s reverted,error reason: %s", tx.Hash().Hex(), errReason) + return 0, fmt.Errorf("tx %s reverted,error reason: %s chain %s", tx.Hash().Hex(), errReason, chainInfo.ChainName) } - return 0, fmt.Errorf("tx %s reverted, could not decode error reason", tx.Hash().Hex()) + return 0, fmt.Errorf("tx %s reverted, could not decode error reason chain %s", tx.Hash().Hex(), chainInfo.ChainName) } return receipt.BlockNumber.Uint64(), nil } }, + Users: chain.Users, } } return chains diff --git a/deployment/go.mod b/deployment/go.mod index b914f76d59b..7908b6a8048 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -25,7 +25,7 @@ require ( github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86 github.com/smartcontractkit/chain-selectors v1.0.31 github.com/smartcontractkit/chainlink-ccip v0.0.0-20241204015713-8956bb614e9e - github.com/smartcontractkit/chainlink-common v0.3.1-0.20241204184525-29871ced7b4d + github.com/smartcontractkit/chainlink-common v0.3.1-0.20241206011233-b6684ee6508f github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0 github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.13 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 diff --git a/deployment/go.sum b/deployment/go.sum index d740931e9a4..fec529e51c6 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -1411,8 +1411,8 @@ github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgB github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= github.com/smartcontractkit/chainlink-ccip v0.0.0-20241204015713-8956bb614e9e h1:GnM6ZWV6vlk2+n6c6o+v/R1LtXzBGVVx7r37nt/h6Uc= github.com/smartcontractkit/chainlink-ccip v0.0.0-20241204015713-8956bb614e9e/go.mod h1:80vGBbOfertJig0xFKsRfm+i17FkjdKkk1dAaGE45Os= -github.com/smartcontractkit/chainlink-common v0.3.1-0.20241204184525-29871ced7b4d h1:5XKarlliHXVVAhpCeOAx/TRU7eWsJ3tkqRI3I6Cc0SU= -github.com/smartcontractkit/chainlink-common v0.3.1-0.20241204184525-29871ced7b4d/go.mod h1:bQktEJf7sJ0U3SmIcXvbGUox7SmXcnSEZ4kUbT8R5Nk= +github.com/smartcontractkit/chainlink-common v0.3.1-0.20241206011233-b6684ee6508f h1:hH+cAG2zt+WK4I2m572LXAnAJg3wtGEAwzBKR8FiXo8= +github.com/smartcontractkit/chainlink-common v0.3.1-0.20241206011233-b6684ee6508f/go.mod h1:bQktEJf7sJ0U3SmIcXvbGUox7SmXcnSEZ4kUbT8R5Nk= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e h1:PRoeby6ZlTuTkv2f+7tVU4+zboTfRzI+beECynF4JQ0= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e/go.mod h1:mUh5/woemsVaHgTorA080hrYmO3syBCmPdnWc/5dOqk= github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241202141438-a90db35252db h1:N1RH1hSr2ACzOFc9hkCcjE8pRBTdcU3p8nsTJByaLes= diff --git a/deployment/helpers.go b/deployment/helpers.go index e8d2d8c8d59..50f4c404b09 100644 --- a/deployment/helpers.go +++ b/deployment/helpers.go @@ -138,17 +138,18 @@ func DeployContract[C any]( ) (*ContractDeploy[C], error) { contractDeploy := deploy(chain) if contractDeploy.Err != nil { - lggr.Errorw("Failed to deploy contract", "err", contractDeploy.Err) + lggr.Errorw("Failed to deploy contract", "chain", chain.String(), "err", contractDeploy.Err) return nil, contractDeploy.Err } _, err := chain.Confirm(contractDeploy.Tx) if err != nil { - lggr.Errorw("Failed to confirm deployment", "err", err) + lggr.Errorw("Failed to confirm deployment", "chain", chain.String(), "Contract", contractDeploy.Tv.String(), "err", err) return nil, err } + lggr.Infow("Deployed contract", "Contract", contractDeploy.Tv.String(), "addr", contractDeploy.Address, "chain", chain.Selector) err = addressBook.Save(chain.Selector, contractDeploy.Address.String(), contractDeploy.Tv) if err != nil { - lggr.Errorw("Failed to save contract address", "err", err) + lggr.Errorw("Failed to save contract address", "Contract", contractDeploy.Tv.String(), "addr", contractDeploy.Address, "chain", chain.String(), "err", err) return nil, err } return &contractDeploy, nil @@ -158,9 +159,25 @@ func IsValidChainSelector(cs uint64) error { if cs == 0 { return fmt.Errorf("chain selector must be set") } - _, err := chain_selectors.ChainIdFromSelector(cs) + _, err := chain_selectors.GetSelectorFamily(cs) if err != nil { - return fmt.Errorf("invalid chain selector: %d - %w", cs, err) + return err } return nil } + +func ChainInfo(cs uint64) (chain_selectors.ChainDetails, error) { + id, err := chain_selectors.GetChainIDFromSelector(cs) + if err != nil { + return chain_selectors.ChainDetails{}, err + } + family, err := chain_selectors.GetSelectorFamily(cs) + if err != nil { + return chain_selectors.ChainDetails{}, err + } + info, err := chain_selectors.GetChainDetailsByChainIDAndFamily(id, family) + if err != nil { + return chain_selectors.ChainDetails{}, err + } + return info, nil +} diff --git a/deployment/keystone/ocr3config_test.go b/deployment/keystone/ocr3config_test.go index 4046787724a..daf869d77e0 100644 --- a/deployment/keystone/ocr3config_test.go +++ b/deployment/keystone/ocr3config_test.go @@ -11,13 +11,14 @@ import ( "github.com/ethereum/go-ethereum/common" chain_selectors "github.com/smartcontractkit/chain-selectors" - "github.com/smartcontractkit/chainlink/deployment" - "github.com/smartcontractkit/chainlink/deployment/common/view" - "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" types2 "github.com/smartcontractkit/libocr/offchainreporting2/types" "github.com/smartcontractkit/libocr/offchainreporting2plus/types" types3 "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/test-go/testify/require" + + "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/deployment/common/view" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" ) var wantOCR3Config = `{ @@ -83,7 +84,6 @@ func Test_configureOCR3Request_generateOCR3Config(t *testing.T) { var cfg OracleConfig err := json.Unmarshal([]byte(ocr3Cfg), &cfg) require.NoError(t, err) - r := configureOCR3Request{ cfg: &OracleConfigWithSecrets{OracleConfig: cfg, OCRSecrets: deployment.XXXGenerateTestOCRSecrets()}, nodes: nodes, diff --git a/deployment/multiclient.go b/deployment/multiclient.go index dcda07ebb0b..914c1fbd9e3 100644 --- a/deployment/multiclient.go +++ b/deployment/multiclient.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" "github.com/pkg/errors" + chainselectors "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/chainlink-common/pkg/logger" ) @@ -46,6 +47,7 @@ type MultiClient struct { Backups []*ethclient.Client RetryConfig RetryConfig lggr logger.Logger + chainName string } func NewMultiClient(lggr logger.Logger, rpcs []RPC, opts ...func(client *MultiClient)) (*MultiClient, error) { @@ -59,6 +61,17 @@ func NewMultiClient(lggr logger.Logger, rpcs []RPC, opts ...func(client *MultiCl if err != nil { return nil, fmt.Errorf("failed to dial ws url '%s': %w", rpc.WSURL, err) } + // fetch chain name if not set + if mc.chainName == "" { + id, err := client.ChainID(context.Background()) + if err == nil { + details, err := chainselectors.GetChainDetailsByChainIDAndFamily(id.String(), chainselectors.FamilyEVM) + if err == nil { + return nil, err + } + mc.chainName = details.ChainName + } + } clients = append(clients, client) } mc.Client = clients[0] @@ -134,11 +147,12 @@ func (mc *MultiClient) WaitMined(ctx context.Context, tx *types.Transaction) (*t func (mc *MultiClient) retryWithBackups(opName string, op func(*ethclient.Client) error) error { var err error - for _, client := range append([]*ethclient.Client{mc.Client}, mc.Backups...) { + for i, client := range append([]*ethclient.Client{mc.Client}, mc.Backups...) { err2 := retry.Do(func() error { + mc.lggr.Debugf("Trying op %s with chain %s client index %d", opName, mc.chainName, i) err = op(client) if err != nil { - mc.lggr.Warnf("retryable error '%s' for op %s with client %v", err.Error(), opName, client) + mc.lggr.Warnf("retryable error '%s' for op %s with chain %s client index %d", err.Error(), opName, mc.chainName, i) return err } return nil @@ -146,7 +160,7 @@ func (mc *MultiClient) retryWithBackups(opName string, op func(*ethclient.Client if err2 == nil { return nil } - mc.lggr.Infof("Client %v failed, trying next client", client) + mc.lggr.Infof("Client at index %d failed, trying next client chain %s", i, mc.chainName) } - return errors.Wrapf(err, "All backup clients %v failed", mc.Backups) + return errors.Wrapf(err, "All backup clients %v failed for chain %s", mc.Backups, mc.chainName) } diff --git a/go.mod b/go.mod index f1d3b1b71e5..ad71fa75be3 100644 --- a/go.mod +++ b/go.mod @@ -79,7 +79,7 @@ require ( github.com/smartcontractkit/chain-selectors v1.0.31 github.com/smartcontractkit/chainlink-automation v0.8.1 github.com/smartcontractkit/chainlink-ccip v0.0.0-20241204015713-8956bb614e9e - github.com/smartcontractkit/chainlink-common v0.3.1-0.20241204184525-29871ced7b4d + github.com/smartcontractkit/chainlink-common v0.3.1-0.20241206011233-b6684ee6508f github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241202141438-a90db35252db github.com/smartcontractkit/chainlink-feeds v0.1.1 @@ -106,6 +106,7 @@ require ( go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.49.0 go.opentelemetry.io/otel v1.31.0 go.opentelemetry.io/otel/metric v1.31.0 + go.opentelemetry.io/otel/sdk/metric v1.31.0 go.opentelemetry.io/otel/trace v1.31.0 go.uber.org/multierr v1.11.0 go.uber.org/zap v1.27.0 @@ -364,7 +365,6 @@ require ( go.opentelemetry.io/otel/log v0.6.0 // indirect go.opentelemetry.io/otel/sdk v1.31.0 // indirect go.opentelemetry.io/otel/sdk/log v0.6.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.31.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/ratelimit v0.3.1 // indirect golang.org/x/arch v0.11.0 // indirect diff --git a/go.sum b/go.sum index cdf7fb3805d..42de8ab4a72 100644 --- a/go.sum +++ b/go.sum @@ -1125,8 +1125,8 @@ github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgB github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= github.com/smartcontractkit/chainlink-ccip v0.0.0-20241204015713-8956bb614e9e h1:GnM6ZWV6vlk2+n6c6o+v/R1LtXzBGVVx7r37nt/h6Uc= github.com/smartcontractkit/chainlink-ccip v0.0.0-20241204015713-8956bb614e9e/go.mod h1:80vGBbOfertJig0xFKsRfm+i17FkjdKkk1dAaGE45Os= -github.com/smartcontractkit/chainlink-common v0.3.1-0.20241204184525-29871ced7b4d h1:5XKarlliHXVVAhpCeOAx/TRU7eWsJ3tkqRI3I6Cc0SU= -github.com/smartcontractkit/chainlink-common v0.3.1-0.20241204184525-29871ced7b4d/go.mod h1:bQktEJf7sJ0U3SmIcXvbGUox7SmXcnSEZ4kUbT8R5Nk= +github.com/smartcontractkit/chainlink-common v0.3.1-0.20241206011233-b6684ee6508f h1:hH+cAG2zt+WK4I2m572LXAnAJg3wtGEAwzBKR8FiXo8= +github.com/smartcontractkit/chainlink-common v0.3.1-0.20241206011233-b6684ee6508f/go.mod h1:bQktEJf7sJ0U3SmIcXvbGUox7SmXcnSEZ4kUbT8R5Nk= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e h1:PRoeby6ZlTuTkv2f+7tVU4+zboTfRzI+beECynF4JQ0= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e/go.mod h1:mUh5/woemsVaHgTorA080hrYmO3syBCmPdnWc/5dOqk= github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241202141438-a90db35252db h1:N1RH1hSr2ACzOFc9hkCcjE8pRBTdcU3p8nsTJByaLes= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 6544f9cffae..aeef16e90d8 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -41,7 +41,7 @@ require ( github.com/smartcontractkit/chain-selectors v1.0.31 github.com/smartcontractkit/chainlink-automation v0.8.1 github.com/smartcontractkit/chainlink-ccip v0.0.0-20241204015713-8956bb614e9e - github.com/smartcontractkit/chainlink-common v0.3.1-0.20241204184525-29871ced7b4d + github.com/smartcontractkit/chainlink-common v0.3.1-0.20241206011233-b6684ee6508f github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0 github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.2 github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.19 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 264d60607f0..b9611bc473b 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1432,8 +1432,8 @@ github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgB github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= github.com/smartcontractkit/chainlink-ccip v0.0.0-20241204015713-8956bb614e9e h1:GnM6ZWV6vlk2+n6c6o+v/R1LtXzBGVVx7r37nt/h6Uc= github.com/smartcontractkit/chainlink-ccip v0.0.0-20241204015713-8956bb614e9e/go.mod h1:80vGBbOfertJig0xFKsRfm+i17FkjdKkk1dAaGE45Os= -github.com/smartcontractkit/chainlink-common v0.3.1-0.20241204184525-29871ced7b4d h1:5XKarlliHXVVAhpCeOAx/TRU7eWsJ3tkqRI3I6Cc0SU= -github.com/smartcontractkit/chainlink-common v0.3.1-0.20241204184525-29871ced7b4d/go.mod h1:bQktEJf7sJ0U3SmIcXvbGUox7SmXcnSEZ4kUbT8R5Nk= +github.com/smartcontractkit/chainlink-common v0.3.1-0.20241206011233-b6684ee6508f h1:hH+cAG2zt+WK4I2m572LXAnAJg3wtGEAwzBKR8FiXo8= +github.com/smartcontractkit/chainlink-common v0.3.1-0.20241206011233-b6684ee6508f/go.mod h1:bQktEJf7sJ0U3SmIcXvbGUox7SmXcnSEZ4kUbT8R5Nk= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e h1:PRoeby6ZlTuTkv2f+7tVU4+zboTfRzI+beECynF4JQ0= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e/go.mod h1:mUh5/woemsVaHgTorA080hrYmO3syBCmPdnWc/5dOqk= github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241202141438-a90db35252db h1:N1RH1hSr2ACzOFc9hkCcjE8pRBTdcU3p8nsTJByaLes= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index 3a29e6ce419..646ec7a5c1a 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -19,7 +19,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/rs/zerolog v1.33.0 github.com/slack-go/slack v0.15.0 - github.com/smartcontractkit/chainlink-common v0.3.1-0.20241204184525-29871ced7b4d + github.com/smartcontractkit/chainlink-common v0.3.1-0.20241206011233-b6684ee6508f github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.19 github.com/smartcontractkit/chainlink-testing-framework/seth v1.50.9 github.com/smartcontractkit/chainlink-testing-framework/wasp v1.50.2 diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index 70e4d6bce84..7bc63d18fb5 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1423,8 +1423,8 @@ github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgB github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= github.com/smartcontractkit/chainlink-ccip v0.0.0-20241204015713-8956bb614e9e h1:GnM6ZWV6vlk2+n6c6o+v/R1LtXzBGVVx7r37nt/h6Uc= github.com/smartcontractkit/chainlink-ccip v0.0.0-20241204015713-8956bb614e9e/go.mod h1:80vGBbOfertJig0xFKsRfm+i17FkjdKkk1dAaGE45Os= -github.com/smartcontractkit/chainlink-common v0.3.1-0.20241204184525-29871ced7b4d h1:5XKarlliHXVVAhpCeOAx/TRU7eWsJ3tkqRI3I6Cc0SU= -github.com/smartcontractkit/chainlink-common v0.3.1-0.20241204184525-29871ced7b4d/go.mod h1:bQktEJf7sJ0U3SmIcXvbGUox7SmXcnSEZ4kUbT8R5Nk= +github.com/smartcontractkit/chainlink-common v0.3.1-0.20241206011233-b6684ee6508f h1:hH+cAG2zt+WK4I2m572LXAnAJg3wtGEAwzBKR8FiXo8= +github.com/smartcontractkit/chainlink-common v0.3.1-0.20241206011233-b6684ee6508f/go.mod h1:bQktEJf7sJ0U3SmIcXvbGUox7SmXcnSEZ4kUbT8R5Nk= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e h1:PRoeby6ZlTuTkv2f+7tVU4+zboTfRzI+beECynF4JQ0= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e/go.mod h1:mUh5/woemsVaHgTorA080hrYmO3syBCmPdnWc/5dOqk= github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241202141438-a90db35252db h1:N1RH1hSr2ACzOFc9hkCcjE8pRBTdcU3p8nsTJByaLes= diff --git a/integration-tests/smoke/ccip/ccip_batching_test.go b/integration-tests/smoke/ccip/ccip_batching_test.go index c801e899585..8c3615fbb20 100644 --- a/integration-tests/smoke/ccip/ccip_batching_test.go +++ b/integration-tests/smoke/ccip/ccip_batching_test.go @@ -9,20 +9,15 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - gethtypes "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/require" "golang.org/x/exp/maps" - chainsel "github.com/smartcontractkit/chain-selectors" - "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" "github.com/smartcontractkit/chainlink-common/pkg/merklemulti" + "github.com/smartcontractkit/chainlink/deployment/environment/memory" "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" - testsetups "github.com/smartcontractkit/chainlink/integration-tests/testsetups/ccip" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/offramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/onramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" @@ -30,16 +25,33 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" ) -func Test_CCIPBatching(t *testing.T) { +const ( + numMessages = 40 +) + +type batchTestSetup struct { + e changeset.DeployedEnv + state changeset.CCIPOnChainState + sourceChain1 uint64 + sourceChain2 uint64 + destChain uint64 +} + +func newBatchTestSetup(t *testing.T) batchTestSetup { // Setup 3 chains, with 2 lanes going to the dest. - lggr := logger.TestLogger(t) - ctx := changeset.Context(t) - // Will load 3 chains when specified by the overrides.toml or env vars (E2E_TEST_SELECTED_NETWORK). - // See e2e-tests.yml. - e, _, _ := testsetups.NewLocalDevEnvironmentWithDefaultPrice(t, lggr, &changeset.TestConfigs{ - IsUSDC: false, - IsMultiCall3: true, // needed for this test - }) + e := changeset.NewMemoryEnvironmentWithJobsAndContracts( + t, + logger.TestLogger(t), + memory.MemoryEnvironmentConfig{ + Chains: 3, + Nodes: 4, + Bootstraps: 1, + NumOfUsersPerChain: 2, + }, + &changeset.TestConfigs{ + IsMultiCall3: true, + }, + ) state, err := changeset.LoadOnchainState(e.Env) require.NoError(t, err) @@ -61,251 +73,260 @@ func Test_CCIPBatching(t *testing.T) { require.NoError(t, changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(e.Env, state, sourceChain1, destChain, false)) require.NoError(t, changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(e.Env, state, sourceChain2, destChain, false)) - const ( - numMessages = 5 - ) + return batchTestSetup{e, state, sourceChain1, sourceChain2, destChain} +} + +func Test_CCIPBatching_MaxBatchSizeEVM(t *testing.T) { + t.Parallel() + + ctx := changeset.Context(t) + setup := newBatchTestSetup(t) + sourceChain1, sourceChain2, destChain, e, state := setup.sourceChain1, setup.sourceChain2, setup.destChain, setup.e, setup.state + var ( startSeqNum = map[uint64]ccipocr3.SeqNum{ sourceChain1: 1, sourceChain2: 1, } - ) - - t.Run("batch data only messages from single source", func(t *testing.T) { - var ( - sourceChain = sourceChain1 - ) - err := sendMessages( - ctx, - t, - e.Env.Chains[sourceChain], + sourceChain = sourceChain1 + transactors = []*bind.TransactOpts{ e.Env.Chains[sourceChain].DeployerKey, - state.Chains[sourceChain].OnRamp, - state.Chains[sourceChain].Router, - state.Chains[sourceChain].Multicall3, - destChain, - numMessages, - common.LeftPadBytes(state.Chains[destChain].Receiver.Address().Bytes(), 32), - ) - require.NoError(t, err) - - _, err = changeset.ConfirmCommitWithExpectedSeqNumRange( - t, - e.Env.Chains[sourceChain], - e.Env.Chains[destChain], - state.Chains[destChain].OffRamp, - nil, - ccipocr3.NewSeqNumRange(startSeqNum[sourceChain], startSeqNum[sourceChain]+numMessages-1), - true, - ) - require.NoErrorf(t, err, "failed to confirm commit from chain %d", sourceChain) - - states, err := changeset.ConfirmExecWithSeqNrs( - t, - e.Env.Chains[sourceChain], - e.Env.Chains[destChain], - state.Chains[destChain].OffRamp, - nil, - genSeqNrRange(startSeqNum[sourceChain], startSeqNum[sourceChain]+numMessages-1), - ) - require.NoError(t, err) - // assert that all states are successful - for _, state := range states { - require.Equal(t, changeset.EXECUTION_STATE_SUCCESS, state) + e.Env.Chains[sourceChain].Users[0], } + errs = make(chan error, len(transactors)) + ) - startSeqNum[sourceChain] = startSeqNum[sourceChain] + numMessages - }) - - t.Run("batch data only messages from multiple sources", func(t *testing.T) { - t.Skipf("skipping - failing consistently in CI") - var ( - wg sync.WaitGroup - sourceChains = []uint64{sourceChain1, sourceChain2} - errs = make(chan error, len(sourceChains)) - ) - - for _, srcChain := range sourceChains { - wg.Add(1) - go sendMessagesAsync( + for _, transactor := range transactors { + go func() { + err := sendMessages( ctx, t, - e, - state, - srcChain, + e.Env.Chains[sourceChain], + transactor, + state.Chains[sourceChain].OnRamp, + state.Chains[sourceChain].Router, + state.Chains[sourceChain].Multicall3, destChain, - numMessages, - &wg, - errs, + merklemulti.MaxNumberTreeLeaves/2, + common.LeftPadBytes(state.Chains[destChain].Receiver.Address().Bytes(), 32), ) - } - - wg.Wait() - - var i int - for i < len(sourceChains) { - select { - case err := <-errs: - require.NoError(t, err) - i++ - case <-ctx.Done(): - require.FailNow(t, "didn't get all errors before test context was done") - } - } + t.Log("sendMessages error:", err, ", writing to channel") + errs <- err + t.Log("sent error to channel") + }() + } - // confirm the commit reports - outputErrs := make(chan outputErr[*offramp.OffRampCommitReportAccepted], len(sourceChains)) - for _, srcChain := range sourceChains { - wg.Add(1) - go assertCommitReportsAsync( - t, - e, - state, - srcChain, - destChain, - startSeqNum[srcChain], - startSeqNum[srcChain]+ccipocr3.SeqNum(numMessages)-1, - &wg, - outputErrs, - ) + var i = 0 + for i < len(transactors) { + select { + case err := <-errs: + require.NoError(t, err) + i++ + case <-ctx.Done(): + require.FailNow(t, "didn't get all errors before test context was done") } + } - t.Log("waiting for commit report") - wg.Wait() - - i = 0 - var reports []*offramp.OffRampCommitReportAccepted - for i < len(sourceChains) { - select { - case outputErr := <-outputErrs: - require.NoError(t, outputErr.err) - reports = append(reports, outputErr.output) - i++ - case <-ctx.Done(): - require.FailNow(t, "didn't get all commit reports before test context was done") - } - } + _, err := changeset.ConfirmCommitWithExpectedSeqNumRange( + t, + e.Env.Chains[sourceChain], + e.Env.Chains[destChain], + state.Chains[destChain].OffRamp, + nil, // startBlock + ccipocr3.NewSeqNumRange( + startSeqNum[sourceChain], + startSeqNum[sourceChain]+ccipocr3.SeqNum(merklemulti.MaxNumberTreeLeaves)-1, + ), + true, + ) + require.NoErrorf(t, err, "failed to confirm commit from chain %d", sourceChain) +} - // the reports should be the same for both, since both roots should be batched within - // that one report. - require.Lenf(t, reports, len(sourceChains), "expected %d commit reports", len(sourceChains)) - require.NotNil(t, reports[0], "commit report should not be nil") - require.NotNil(t, reports[1], "commit report should not be nil") - // TODO: this assertion is failing, despite messages being sent at the same time. - // require.Equal(t, reports[0], reports[1], "commit reports should be the same") - - // confirm execution - execErrs := make(chan outputErr[map[uint64]int], len(sourceChains)) - for _, srcChain := range sourceChains { - wg.Add(1) - go assertExecAsync( - t, - e, - state, - srcChain, - destChain, - genSeqNrRange(startSeqNum[srcChain], startSeqNum[srcChain]+ccipocr3.SeqNum(numMessages)-1), - &wg, - execErrs, - ) - } +func Test_CCIPBatching_MultiSource(t *testing.T) { + // t.Skip("Exec not working, boosting not working correctly") - t.Log("waiting for exec reports") - wg.Wait() - - i = 0 - var execStates []map[uint64]int - for i < len(sourceChains) { - select { - case outputErr := <-execErrs: - require.NoError(t, outputErr.err) - execStates = append(execStates, outputErr.output) - i++ - case <-ctx.Done(): - require.FailNow(t, "didn't get all exec reports before test context was done") - } - } + t.Parallel() - // assert that all states are successful - for _, states := range execStates { - for _, state := range states { - require.Equal(t, changeset.EXECUTION_STATE_SUCCESS, state) - } - } + // Setup 3 chains, with 2 lanes going to the dest. + ctx := changeset.Context(t) + setup := newBatchTestSetup(t) + sourceChain1, sourceChain2, destChain, e, state := setup.sourceChain1, setup.sourceChain2, setup.destChain, setup.e, setup.state - // update the start and end seq nums - for _, srcChain := range sourceChains { - startSeqNum[srcChain] = startSeqNum[srcChain] + numMessages + var ( + wg sync.WaitGroup + sourceChains = []uint64{sourceChain1, sourceChain2} + errs = make(chan error, len(sourceChains)) + startSeqNum = map[uint64]ccipocr3.SeqNum{ + sourceChain1: 1, + sourceChain2: 1, } - }) - - t.Run("max evm batch size", func(t *testing.T) { - t.Skipf("This test is flaky, skipping until the issue related to fee calculation is resolved") - var ( - sourceChain = sourceChain1 - otherSender = mustNewTransactor(t, e.Env.Chains[sourceChain]) - transactors = []*bind.TransactOpts{ - e.Env.Chains[sourceChain].DeployerKey, - otherSender, - } - errs = make(chan error, len(transactors)) - ) + ) - // transfer some eth to the other sender from the DeployerKey - sendEth( + for _, srcChain := range sourceChains { + wg.Add(1) + go sendMessagesAsync( ctx, t, - e.Env.Chains[sourceChain], - e.Env.Chains[sourceChain].DeployerKey, - otherSender.From, - assets.Ether(20).ToInt(), + e, + state, + srcChain, + destChain, + numMessages, + &wg, + errs, ) + } - for _, transactor := range transactors { - go func() { - err := sendMessages( - ctx, - t, - e.Env.Chains[sourceChain], - transactor, - state.Chains[sourceChain].OnRamp, - state.Chains[sourceChain].Router, - state.Chains[sourceChain].Multicall3, - destChain, - merklemulti.MaxNumberTreeLeaves/2, - common.LeftPadBytes(state.Chains[destChain].Receiver.Address().Bytes(), 32), - ) - t.Log("sendMessages error:", err, ", writing to channel") - errs <- err - t.Log("sent error to channel") - }() + wg.Wait() + + var i int + for i < len(sourceChains) { + select { + case err := <-errs: + require.NoError(t, err) + i++ + case <-ctx.Done(): + require.FailNow(t, "didn't get all errors before test context was done") } + } - var i = 0 - for i < len(transactors) { - select { - case err := <-errs: - require.NoError(t, err) - i++ - case <-ctx.Done(): - require.FailNow(t, "didn't get all errors before test context was done") - } + // confirm the commit reports + outputErrs := make(chan outputErr[*offramp.OffRampCommitReportAccepted], len(sourceChains)) + for _, srcChain := range sourceChains { + wg.Add(1) + go assertCommitReportsAsync( + t, + e, + state, + srcChain, + destChain, + startSeqNum[srcChain], + startSeqNum[srcChain]+ccipocr3.SeqNum(numMessages)-1, + &wg, + outputErrs, + ) + } + + t.Log("waiting for commit report") + wg.Wait() + + i = 0 + var reports []*offramp.OffRampCommitReportAccepted + for i < len(sourceChains) { + select { + case outputErr := <-outputErrs: + require.NoError(t, outputErr.err) + reports = append(reports, outputErr.output) + i++ + case <-ctx.Done(): + require.FailNow(t, "didn't get all commit reports before test context was done") } + } - _, err = changeset.ConfirmCommitWithExpectedSeqNumRange( + // the reports should be the same for both, since both roots should be batched within + // that one report. + require.Lenf(t, reports, len(sourceChains), "expected %d commit reports", len(sourceChains)) + require.NotNil(t, reports[0], "commit report should not be nil") + require.NotNil(t, reports[1], "commit report should not be nil") + // TODO: this assertion is failing, despite messages being sent at the same time. + // require.Equal(t, reports[0], reports[1], "commit reports should be the same") + + // confirm execution + execErrs := make(chan outputErr[map[uint64]int], len(sourceChains)) + for _, srcChain := range sourceChains { + wg.Add(1) + go assertExecAsync( t, - e.Env.Chains[sourceChain], - e.Env.Chains[destChain], - state.Chains[destChain].OffRamp, - nil, // startBlock - ccipocr3.NewSeqNumRange( - startSeqNum[sourceChain], - startSeqNum[sourceChain]+ccipocr3.SeqNum(merklemulti.MaxNumberTreeLeaves)-1, - ), - true, + e, + state, + srcChain, + destChain, + genSeqNrRange(startSeqNum[srcChain], startSeqNum[srcChain]+ccipocr3.SeqNum(numMessages)-1), + &wg, + execErrs, ) - require.NoErrorf(t, err, "failed to confirm commit from chain %d", sourceChain) - }) + } + + t.Log("waiting for exec reports") + wg.Wait() + + i = 0 + var execStates []map[uint64]int + for i < len(sourceChains) { + select { + case outputErr := <-execErrs: + require.NoError(t, outputErr.err) + execStates = append(execStates, outputErr.output) + i++ + case <-ctx.Done(): + require.FailNow(t, "didn't get all exec reports before test context was done") + } + } + + // assert that all states are successful + for _, states := range execStates { + for _, state := range states { + require.Equal(t, changeset.EXECUTION_STATE_SUCCESS, state) + } + } +} + +func Test_CCIPBatching_SingleSource(t *testing.T) { + t.Parallel() + + // Setup 3 chains, with 2 lanes going to the dest. + ctx := changeset.Context(t) + setup := newBatchTestSetup(t) + sourceChain1, sourceChain2, destChain, e, state := setup.sourceChain1, setup.sourceChain2, setup.destChain, setup.e, setup.state + + var ( + startSeqNum = map[uint64]ccipocr3.SeqNum{ + sourceChain1: 1, + sourceChain2: 1, + } + ) + + var ( + sourceChain = sourceChain1 + ) + err := sendMessages( + ctx, + t, + e.Env.Chains[sourceChain], + e.Env.Chains[sourceChain].DeployerKey, + state.Chains[sourceChain].OnRamp, + state.Chains[sourceChain].Router, + state.Chains[sourceChain].Multicall3, + destChain, + numMessages, + common.LeftPadBytes(state.Chains[destChain].Receiver.Address().Bytes(), 32), + ) + require.NoError(t, err) + + _, err = changeset.ConfirmCommitWithExpectedSeqNumRange( + t, + e.Env.Chains[sourceChain], + e.Env.Chains[destChain], + state.Chains[destChain].OffRamp, + nil, + ccipocr3.NewSeqNumRange(startSeqNum[sourceChain], startSeqNum[sourceChain]+numMessages-1), + true, + ) + require.NoErrorf(t, err, "failed to confirm commit from chain %d", sourceChain) + + states, err := changeset.ConfirmExecWithSeqNrs( + t, + e.Env.Chains[sourceChain], + e.Env.Chains[destChain], + state.Chains[destChain].OffRamp, + nil, + genSeqNrRange(startSeqNum[sourceChain], startSeqNum[sourceChain]+numMessages-1), + ) + require.NoError(t, err) + // assert that all states are successful + for _, state := range states { + require.Equal(t, changeset.EXECUTION_STATE_SUCCESS, state) + } } type outputErr[T any] struct { @@ -373,18 +394,34 @@ func sendMessagesAsync( out chan<- error, ) { defer wg.Done() - err := sendMessages( - ctx, - t, - e.Env.Chains[sourceChainSelector], - e.Env.Chains[sourceChainSelector].DeployerKey, - state.Chains[sourceChainSelector].OnRamp, - state.Chains[sourceChainSelector].Router, - state.Chains[sourceChainSelector].Multicall3, - destChainSelector, - numMessages, - common.LeftPadBytes(state.Chains[destChainSelector].Receiver.Address().Bytes(), 32), + var err error + + const ( + numRetries = 3 ) + + // we retry a bunch of times just in case there is a race b/w the prices being + // posted and the messages being sent. + for i := 0; i < numRetries; i++ { + err = sendMessages( + ctx, + t, + e.Env.Chains[sourceChainSelector], + e.Env.Chains[sourceChainSelector].DeployerKey, + state.Chains[sourceChainSelector].OnRamp, + state.Chains[sourceChainSelector].Router, + state.Chains[sourceChainSelector].Multicall3, + destChainSelector, + numMessages, + common.LeftPadBytes(state.Chains[destChainSelector].Receiver.Address().Bytes(), 32), + ) + if err == nil { + break + } + + t.Log("sendMessagesAsync error is non-nil:", err, ", retrying") + } + t.Log("sendMessagesAsync error:", err, ", writing to channel") out <- err } @@ -412,8 +449,13 @@ func sendMessages( return fmt.Errorf("generate messages: %w", err) } + currBalance, err := sourceChain.Client.BalanceAt(ctx, sourceTransactOpts.From, nil) + if err != nil { + return fmt.Errorf("get balance: %w", err) + } + // Send the tx with the messages through the multicall - t.Logf("Sending %d messages with total value %s", numMessages, totalValue.String()) + t.Logf("Sending %d messages with total value %s, current balance: %s", numMessages, totalValue.String(), currBalance.String()) tx, err := sourceMulticall3.Aggregate3Value( &bind.TransactOpts{ From: sourceTransactOpts.From, @@ -434,7 +476,9 @@ func sendMessages( if err != nil { return fmt.Errorf("get message sent event: %w", err) } - defer iter.Close() + defer func() { + require.NoError(t, iter.Close()) + }() // there should be numMessages messages emitted for i := 0; i < numMessages; i++ { @@ -461,7 +505,7 @@ func genMessages( Data: []byte(fmt.Sprintf("hello world %d", i)), TokenAmounts: nil, FeeToken: common.HexToAddress("0x0"), - ExtraArgs: nil, + ExtraArgs: changeset.MakeEVMExtraArgsV2(50_000, false), } fee, err := sourceRouter.GetFee(&bind.CallOpts{Context: ctx}, destChainSelector, msg) @@ -495,51 +539,3 @@ func genSeqNrRange(start, end ccipocr3.SeqNum) []uint64 { } return seqNrs } - -func mustNewTransactor(t *testing.T, chain deployment.Chain) *bind.TransactOpts { - chainID, err := chainsel.GetChainIDFromSelector(chain.Selector) - require.NoError(t, err) - chainIDBig, ok := new(big.Int).SetString(chainID, 10) - require.True(t, ok, "evm chainID must be integral") - key, err := crypto.GenerateKey() - require.NoError(t, err) - transactor, err := bind.NewKeyedTransactorWithChainID(key, chainIDBig) - require.NoError(t, err) - return transactor -} - -func sendEth( - ctx context.Context, - t *testing.T, - chain deployment.Chain, - from *bind.TransactOpts, - to common.Address, - value *big.Int, -) { - balance, err := chain.Client.BalanceAt(ctx, from.From, nil) - require.NoError(t, err) - if balance.Cmp(value) < 0 { - t.Fatalf("insufficient balance: %s < %s", balance.String(), value.String()) - } - t.Logf("balance of from account %s: %s", from.From.String(), balance.String()) - - nonce, err := chain.Client.PendingNonceAt(ctx, from.From) - require.NoError(t, err) - gp, err := chain.Client.SuggestGasPrice(ctx) - require.NoError(t, err) - tx := gethtypes.NewTx(&gethtypes.LegacyTx{ - Nonce: nonce, - GasPrice: gp, - Gas: 21_000, - To: &to, - Value: value, - Data: nil, - }) - signedTx, err := from.Signer(from.From, tx) - require.NoError(t, err) - err = chain.Client.SendTransaction(ctx, signedTx) - require.NoError(t, err) - t.Log("sent funding tx:", signedTx.Hash().Hex()) - _, err = deployment.ConfirmIfNoError(chain, signedTx, err) - require.NoError(t, err) -} diff --git a/integration-tests/smoke/ccip/ccip_token_transfer_test.go b/integration-tests/smoke/ccip/ccip_token_transfer_test.go index 06ee06297ae..81920246bed 100644 --- a/integration-tests/smoke/ccip/ccip_token_transfer_test.go +++ b/integration-tests/smoke/ccip/ccip_token_transfer_test.go @@ -96,54 +96,44 @@ func TestTokenTransfer(t *testing.T) { }, ) - tcs := []struct { - name string - srcChain uint64 - dstChain uint64 - tokenAmounts []router.ClientEVMTokenAmount - receiver common.Address - data []byte - extraData []byte - expectedTokenBalances map[common.Address]*big.Int - expectedExecutionState int - }{ + tcs := []changeset.TestTransferRequest{ { - name: "Send token to EOA", - srcChain: sourceChain, - dstChain: destChain, - tokenAmounts: []router.ClientEVMTokenAmount{ + Name: "Send token to EOA", + SourceChain: sourceChain, + DestChain: destChain, + Tokens: []router.ClientEVMTokenAmount{ { Token: srcToken.Address(), Amount: oneE18, }, }, - receiver: utils.RandomAddress(), - expectedTokenBalances: map[common.Address]*big.Int{ + Receiver: utils.RandomAddress(), + ExpectedTokenBalances: map[common.Address]*big.Int{ destToken.Address(): oneE18, }, - expectedExecutionState: changeset.EXECUTION_STATE_SUCCESS, + ExpectedStatus: changeset.EXECUTION_STATE_SUCCESS, }, { - name: "Send token to contract", - srcChain: sourceChain, - dstChain: destChain, - tokenAmounts: []router.ClientEVMTokenAmount{ + Name: "Send token to contract", + SourceChain: sourceChain, + DestChain: destChain, + Tokens: []router.ClientEVMTokenAmount{ { Token: srcToken.Address(), Amount: oneE18, }, }, - receiver: state.Chains[destChain].Receiver.Address(), - expectedTokenBalances: map[common.Address]*big.Int{ + Receiver: state.Chains[destChain].Receiver.Address(), + ExpectedTokenBalances: map[common.Address]*big.Int{ destToken.Address(): oneE18, }, - expectedExecutionState: changeset.EXECUTION_STATE_SUCCESS, + ExpectedStatus: changeset.EXECUTION_STATE_SUCCESS, }, { - name: "Send N tokens to contract", - srcChain: destChain, - dstChain: sourceChain, - tokenAmounts: []router.ClientEVMTokenAmount{ + Name: "Send N tokens to contract", + SourceChain: destChain, + DestChain: sourceChain, + Tokens: []router.ClientEVMTokenAmount{ { Token: selfServeDestToken.Address(), Amount: oneE18, @@ -157,19 +147,19 @@ func TestTokenTransfer(t *testing.T) { Amount: oneE18, }, }, - receiver: state.Chains[sourceChain].Receiver.Address(), - extraData: changeset.MakeEVMExtraArgsV2(300_000, false), - expectedTokenBalances: map[common.Address]*big.Int{ + Receiver: state.Chains[sourceChain].Receiver.Address(), + ExtraArgs: changeset.MakeEVMExtraArgsV2(300_000, false), + ExpectedTokenBalances: map[common.Address]*big.Int{ selfServeSrcToken.Address(): new(big.Int).Add(oneE18, oneE18), srcToken.Address(): oneE18, }, - expectedExecutionState: changeset.EXECUTION_STATE_SUCCESS, + ExpectedStatus: changeset.EXECUTION_STATE_SUCCESS, }, { - name: "Sending token transfer with custom gasLimits to the EOA is successful", - srcChain: destChain, - dstChain: sourceChain, - tokenAmounts: []router.ClientEVMTokenAmount{ + Name: "Sending token transfer with custom gasLimits to the EOA is successful", + SourceChain: destChain, + DestChain: sourceChain, + Tokens: []router.ClientEVMTokenAmount{ { Token: selfServeDestToken.Address(), Amount: oneE18, @@ -179,19 +169,19 @@ func TestTokenTransfer(t *testing.T) { Amount: new(big.Int).Add(oneE18, oneE18), }, }, - receiver: utils.RandomAddress(), - extraData: changeset.MakeEVMExtraArgsV2(1, false), - expectedTokenBalances: map[common.Address]*big.Int{ + Receiver: utils.RandomAddress(), + ExtraArgs: changeset.MakeEVMExtraArgsV2(1, false), + ExpectedTokenBalances: map[common.Address]*big.Int{ selfServeSrcToken.Address(): oneE18, srcToken.Address(): new(big.Int).Add(oneE18, oneE18), }, - expectedExecutionState: changeset.EXECUTION_STATE_SUCCESS, + ExpectedStatus: changeset.EXECUTION_STATE_SUCCESS, }, { - name: "Sending PTT with too low gas limit leads to the revert when receiver is a contract", - srcChain: destChain, - dstChain: sourceChain, - tokenAmounts: []router.ClientEVMTokenAmount{ + Name: "Sending PTT with too low gas limit leads to the revert when receiver is a contract", + SourceChain: destChain, + DestChain: sourceChain, + Tokens: []router.ClientEVMTokenAmount{ { Token: selfServeDestToken.Address(), Amount: oneE18, @@ -201,45 +191,40 @@ func TestTokenTransfer(t *testing.T) { Amount: oneE18, }, }, - receiver: state.Chains[sourceChain].Receiver.Address(), - data: []byte("this should be reverted because gasLimit is too low, no tokens are transferred as well"), - extraData: changeset.MakeEVMExtraArgsV2(1, false), - expectedTokenBalances: map[common.Address]*big.Int{ + Receiver: state.Chains[sourceChain].Receiver.Address(), + Data: []byte("this should be reverted because gasLimit is too low, no tokens are transferred as well"), + ExtraArgs: changeset.MakeEVMExtraArgsV2(1, false), + ExpectedTokenBalances: map[common.Address]*big.Int{ selfServeSrcToken.Address(): big.NewInt(0), srcToken.Address(): big.NewInt(0), }, - expectedExecutionState: changeset.EXECUTION_STATE_FAILURE, + ExpectedStatus: changeset.EXECUTION_STATE_FAILURE, }, } - for _, tt := range tcs { - t.Run(tt.name, func(t *testing.T) { - initialBalances := map[common.Address]*big.Int{} - for token := range tt.expectedTokenBalances { - initialBalance := changeset.GetTokenBalance(ctx, t, token, tt.receiver, e.Chains[tt.dstChain]) - initialBalances[token] = initialBalance - } + startBlocks, expectedSeqNums, expectedExecutionStates, expectedTokenBalances := + changeset.TransferMultiple(ctx, t, e, state, tcs) - changeset.TransferAndWaitForSuccess( - ctx, - t, - e, - state, - tt.srcChain, - tt.dstChain, - tt.tokenAmounts, - tt.receiver, - tt.data, - tt.expectedExecutionState, - tt.extraData, - ) + err = changeset.ConfirmMultipleCommits( + t, + e.Chains, + state.Chains, + startBlocks, + false, + expectedSeqNums, + ) + require.NoError(t, err) - for token, balance := range tt.expectedTokenBalances { - expected := new(big.Int).Add(initialBalances[token], balance) - changeset.WaitForTheTokenBalance(ctx, t, token, tt.receiver, e.Chains[tt.dstChain], expected) - } - }) - } + execStates := changeset.ConfirmExecWithSeqNrsForAll( + t, + e, + state, + changeset.SeqNumberRageToSlice(expectedSeqNums), + startBlocks, + ) + require.Equal(t, expectedExecutionStates, execStates) + + changeset.WaitForTokenBalances(ctx, t, e.Chains, expectedTokenBalances) } func createAndFundSelfServeActor( diff --git a/integration-tests/smoke/ccip/ccip_usdc_test.go b/integration-tests/smoke/ccip/ccip_usdc_test.go index d1df2a6da92..eacf03df926 100644 --- a/integration-tests/smoke/ccip/ccip_usdc_test.go +++ b/integration-tests/smoke/ccip/ccip_usdc_test.go @@ -11,12 +11,10 @@ import ( "golang.org/x/sync/errgroup" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" testsetups "github.com/smartcontractkit/chainlink/integration-tests/testsetups/ccip" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/onramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/burn_mint_erc677" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -37,7 +35,12 @@ func TestUSDCTokenTransfer(t *testing.T) { IsUSDC: true, } tenv, _, _ := testsetups.NewLocalDevEnvironmentWithDefaultPrice(t, lggr, config) - //tenv := changeset.NewMemoryEnvironmentWithJobsAndContracts(t, lggr, 3, 4, config) + //tenv := changeset.NewMemoryEnvironmentWithJobsAndContracts(t, lggr, memory.MemoryEnvironmentConfig{ + // Chains: 3, + // NumOfUsersPerChain: 3, + // Nodes: 5, + // Bootstraps: 1, + //}, config) e := tenv.Env state, err := changeset.LoadOnchainState(e) @@ -97,37 +100,28 @@ func TestUSDCTokenTransfer(t *testing.T) { // MockE2EUSDCTransmitter always mint 1, see MockE2EUSDCTransmitter.sol for more details tinyOneCoin := new(big.Int).SetUint64(1) - tcs := []struct { - name string - receiver common.Address - sourceChain uint64 - destChain uint64 - tokens []router.ClientEVMTokenAmount - data []byte - expectedTokenBalances map[common.Address]*big.Int - expectedExecutionState int - }{ + tcs := []changeset.TestTransferRequest{ { - name: "single USDC token transfer to EOA", - receiver: utils.RandomAddress(), - sourceChain: chainC, - destChain: chainA, - tokens: []router.ClientEVMTokenAmount{ + Name: "single USDC token transfer to EOA", + Receiver: utils.RandomAddress(), + SourceChain: chainC, + DestChain: chainA, + Tokens: []router.ClientEVMTokenAmount{ { Token: cChainUSDC.Address(), Amount: tinyOneCoin, }}, - expectedTokenBalances: map[common.Address]*big.Int{ + ExpectedTokenBalances: map[common.Address]*big.Int{ aChainUSDC.Address(): tinyOneCoin, }, - expectedExecutionState: changeset.EXECUTION_STATE_SUCCESS, + ExpectedStatus: changeset.EXECUTION_STATE_SUCCESS, }, { - name: "multiple USDC tokens within the same message", - receiver: utils.RandomAddress(), - sourceChain: chainC, - destChain: chainA, - tokens: []router.ClientEVMTokenAmount{ + Name: "multiple USDC tokens within the same message", + Receiver: utils.RandomAddress(), + SourceChain: chainC, + DestChain: chainA, + Tokens: []router.ClientEVMTokenAmount{ { Token: cChainUSDC.Address(), Amount: tinyOneCoin, @@ -137,18 +131,18 @@ func TestUSDCTokenTransfer(t *testing.T) { Amount: tinyOneCoin, }, }, - expectedTokenBalances: map[common.Address]*big.Int{ - // 2 coins because of the same receiver + ExpectedTokenBalances: map[common.Address]*big.Int{ + // 2 coins because of the same Receiver aChainUSDC.Address(): new(big.Int).Add(tinyOneCoin, tinyOneCoin), }, - expectedExecutionState: changeset.EXECUTION_STATE_SUCCESS, + ExpectedStatus: changeset.EXECUTION_STATE_SUCCESS, }, { - name: "USDC token together with another token transferred to EOA", - receiver: utils.RandomAddress(), - sourceChain: chainA, - destChain: chainC, - tokens: []router.ClientEVMTokenAmount{ + Name: "USDC token together with another token transferred to EOA", + Receiver: utils.RandomAddress(), + SourceChain: chainA, + DestChain: chainC, + Tokens: []router.ClientEVMTokenAmount{ { Token: aChainUSDC.Address(), Amount: tinyOneCoin, @@ -158,105 +152,89 @@ func TestUSDCTokenTransfer(t *testing.T) { Amount: new(big.Int).Mul(tinyOneCoin, big.NewInt(10)), }, }, - expectedTokenBalances: map[common.Address]*big.Int{ + ExpectedTokenBalances: map[common.Address]*big.Int{ cChainUSDC.Address(): tinyOneCoin, cChainToken.Address(): new(big.Int).Mul(tinyOneCoin, big.NewInt(10)), }, - expectedExecutionState: changeset.EXECUTION_STATE_SUCCESS, + ExpectedStatus: changeset.EXECUTION_STATE_SUCCESS, }, { - name: "programmable token transfer to valid contract receiver", - receiver: state.Chains[chainC].Receiver.Address(), - sourceChain: chainA, - destChain: chainC, - tokens: []router.ClientEVMTokenAmount{ + Name: "USDC programmable token transfer to valid contract receiver", + Receiver: state.Chains[chainC].Receiver.Address(), + SourceChain: chainA, + DestChain: chainC, + Tokens: []router.ClientEVMTokenAmount{ { Token: aChainUSDC.Address(), Amount: tinyOneCoin, }, }, - data: []byte("hello world"), - expectedTokenBalances: map[common.Address]*big.Int{ + Data: []byte("hello world"), + ExpectedTokenBalances: map[common.Address]*big.Int{ cChainUSDC.Address(): tinyOneCoin, }, - expectedExecutionState: changeset.EXECUTION_STATE_SUCCESS, + ExpectedStatus: changeset.EXECUTION_STATE_SUCCESS, + }, + { + Name: "USDC programmable token transfer with too little gas", + Receiver: state.Chains[chainB].Receiver.Address(), + SourceChain: chainC, + DestChain: chainB, + Tokens: []router.ClientEVMTokenAmount{ + { + Token: cChainUSDC.Address(), + Amount: tinyOneCoin, + }, + }, + Data: []byte("gimme more gas to execute that!"), + ExpectedTokenBalances: map[common.Address]*big.Int{ + bChainUSDC.Address(): new(big.Int).SetUint64(0), + }, + ExtraArgs: changeset.MakeEVMExtraArgsV2(1, false), + ExpectedStatus: changeset.EXECUTION_STATE_FAILURE, + }, + { + Name: "USDC token transfer from a different source chain", + Receiver: utils.RandomAddress(), + SourceChain: chainB, + DestChain: chainC, + Tokens: []router.ClientEVMTokenAmount{ + { + Token: bChainUSDC.Address(), + Amount: tinyOneCoin, + }, + }, + Data: nil, + ExpectedTokenBalances: map[common.Address]*big.Int{ + cChainUSDC.Address(): tinyOneCoin, + }, + ExpectedStatus: changeset.EXECUTION_STATE_SUCCESS, }, } - for _, tt := range tcs { - t.Run(tt.name, func(t *testing.T) { - initialBalances := map[common.Address]*big.Int{} - for token := range tt.expectedTokenBalances { - initialBalance := changeset.GetTokenBalance(ctx, t, token, tt.receiver, e.Chains[tt.destChain]) - initialBalances[token] = initialBalance - } - - changeset.TransferAndWaitForSuccess( - ctx, - t, - e, - state, - tt.sourceChain, - tt.destChain, - tt.tokens, - tt.receiver, - tt.data, - tt.expectedExecutionState, - nil, - ) - - for token, balance := range tt.expectedTokenBalances { - expected := new(big.Int).Add(initialBalances[token], balance) - changeset.WaitForTheTokenBalance(ctx, t, token, tt.receiver, e.Chains[tt.destChain], expected) - } - }) - } - - t.Run("multi-source USDC transfer targeting the same dest receiver", func(t *testing.T) { - sendSingleTokenTransfer := func(source, dest uint64, token common.Address, receiver common.Address) (*onramp.OnRampCCIPMessageSent, changeset.SourceDestPair) { - msg := changeset.TestSendRequest(t, e, state, source, dest, false, router.ClientEVM2AnyMessage{ - Receiver: common.LeftPadBytes(receiver.Bytes(), 32), - Data: []byte{}, - TokenAmounts: []router.ClientEVMTokenAmount{{Token: token, Amount: tinyOneCoin}}, - FeeToken: common.HexToAddress("0x0"), - ExtraArgs: nil, - }) - return msg, changeset.SourceDestPair{ - SourceChainSelector: source, - DestChainSelector: dest, - } - } - - receiver := utils.RandomAddress() - - startBlocks := make(map[uint64]*uint64) - expectedSeqNum := make(map[changeset.SourceDestPair]uint64) - expectedSeqNumExec := make(map[changeset.SourceDestPair][]uint64) + startBlocks, expectedSeqNums, expectedExecutionStates, expectedTokenBalances := + changeset.TransferMultiple(ctx, t, e, state, tcs) - latesthdr, err := e.Chains[chainC].Client.HeaderByNumber(testcontext.Get(t), nil) - require.NoError(t, err) - block := latesthdr.Number.Uint64() - startBlocks[chainC] = &block - - message1, message1ID := sendSingleTokenTransfer(chainA, chainC, aChainUSDC.Address(), receiver) - expectedSeqNum[message1ID] = message1.SequenceNumber - expectedSeqNumExec[message1ID] = []uint64{message1.SequenceNumber} - - message2, message2ID := sendSingleTokenTransfer(chainB, chainC, bChainUSDC.Address(), receiver) - expectedSeqNum[message2ID] = message2.SequenceNumber - expectedSeqNumExec[message2ID] = []uint64{message2.SequenceNumber} - - changeset.ConfirmCommitForAllWithExpectedSeqNums(t, e, state, expectedSeqNum, startBlocks) - states := changeset.ConfirmExecWithSeqNrsForAll(t, e, state, expectedSeqNumExec, startBlocks) + err = changeset.ConfirmMultipleCommits( + t, + e.Chains, + state.Chains, + startBlocks, + false, + expectedSeqNums, + ) + require.NoError(t, err) - require.Equal(t, changeset.EXECUTION_STATE_SUCCESS, states[message1ID][message1.SequenceNumber]) - require.Equal(t, changeset.EXECUTION_STATE_SUCCESS, states[message2ID][message2.SequenceNumber]) + execStates := changeset.ConfirmExecWithSeqNrsForAll( + t, + e, + state, + changeset.SeqNumberRageToSlice(expectedSeqNums), + startBlocks, + ) + require.Equal(t, expectedExecutionStates, execStates) - // We sent 1 coin from each source chain, so we should have 2 coins on the destination chain - // Receiver is randomly generated so we don't need to get the initial balance first - expectedBalance := new(big.Int).Add(tinyOneCoin, tinyOneCoin) - changeset.WaitForTheTokenBalance(ctx, t, cChainUSDC.Address(), receiver, e.Chains[chainC], expectedBalance) - }) + changeset.WaitForTokenBalances(ctx, t, e.Chains, expectedTokenBalances) } func updateFeeQuoters( @@ -274,7 +252,11 @@ func updateFeeQuoters( return changeset.UpdateFeeQuoterForUSDC(lggr, e.Chains[chainB], state.Chains[chainB], chainC, bChainUSDC) }) updateFeeQtrGrp.Go(func() error { - return changeset.UpdateFeeQuoterForUSDC(lggr, e.Chains[chainC], state.Chains[chainC], chainA, cChainUSDC) + err1 := changeset.UpdateFeeQuoterForUSDC(lggr, e.Chains[chainC], state.Chains[chainC], chainA, cChainUSDC) + if err1 != nil { + return err1 + } + return changeset.UpdateFeeQuoterForUSDC(lggr, e.Chains[chainC], state.Chains[chainC], chainB, cChainUSDC) }) return updateFeeQtrGrp.Wait() }