diff --git a/.changeset/clean-files-beg.md b/.changeset/clean-files-beg.md new file mode 100644 index 00000000000..1357a044cb0 --- /dev/null +++ b/.changeset/clean-files-beg.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +Adding OCR3 promwrapper to LLO #internal diff --git a/.changeset/dull-readers-rush.md b/.changeset/dull-readers-rush.md new file mode 100644 index 00000000000..3b6f2ae8758 --- /dev/null +++ b/.changeset/dull-readers-rush.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +Reporting number of OCR3 instances running using promwrapper #internal diff --git a/.changeset/hot-islands-dress.md b/.changeset/hot-islands-dress.md new file mode 100644 index 00000000000..82e34ecf42b --- /dev/null +++ b/.changeset/hot-islands-dress.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +#internal refactor: inject ocr secrets via env instead of config diff --git a/.changeset/late-doors-battle.md b/.changeset/late-doors-battle.md new file mode 100644 index 00000000000..8ec64b9048e --- /dev/null +++ b/.changeset/late-doors-battle.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +Fix TransactionSender go routine leak. #bugfix 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 3b394293378..d40d375f860 100644 --- a/.github/e2e-tests.yml +++ b/.github/e2e-tests.yml @@ -948,45 +948,6 @@ runner-test-matrix: E2E_TEST_SELECTED_NETWORK: SIMULATED_1,SIMULATED_2 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 - runs_on: ubuntu-latest - triggers: - - PR E2E Core Tests - - Nightly E2E Tests - test_cmd: cd integration-tests/ && go test smoke/ccip/ccip_token_transfer_test.go -timeout 16m -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 - E2E_JD_VERSION: 0.6.0 - - - id: smoke/ccip/ccip_ooo_execution_test.go:* - path: integration-tests/smoke/ccip/ccip_ooo_execution_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_ooo_execution_test.go -timeout 16m -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 - E2E_JD_VERSION: 0.6.0 - - - id: smoke/ccip/ccip_usdc_test.go:* - path: integration-tests/smoke/ccip/ccip_usdc_test.go - test_env_type: docker - runs_on: ubuntu-latest - triggers: - - PR E2E Core Tests - - Nightly E2E Tests - test_cmd: cd integration-tests/smoke/ccip && go test ccip_usdc_test.go -timeout 18m -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_message_limitations_test.go:* path: integration-tests/smoke/ccip/ccip_message_limitations_test.go test_env_type: docker diff --git a/.github/integration-in-memory-tests.yml b/.github/integration-in-memory-tests.yml index 4b4fd71258d..b7522274d85 100644 --- a/.github/integration-in-memory-tests.yml +++ b/.github/integration-in-memory-tests.yml @@ -48,4 +48,28 @@ runner-test-matrix: - PR Integration CCIP Tests test_cmd: cd integration-tests/contracts && go test ccipreader_test.go -timeout 5m -test.parallel=1 -count=1 -json + - id: smoke/ccip/ccip_usdc_test.go:* + path: integration-tests/smoke/ccip/ccip_usdc_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_usdc_test.go -timeout 18m -test.parallel=1 -count=1 -json + + - id: smoke/ccip/ccip_ooo_execution_test.go:* + path: integration-tests/smoke/ccip/ccip_ooo_execution_test.go + test_env_type: in-memory + runs_on: ubuntu-latest + triggers: + - PR Integration CCIP Tests + test_cmd: cd integration-tests/ && go test smoke/ccip/ccip_ooo_execution_test.go -timeout 16m -test.parallel=1 -count=1 -json + + - id: smoke/ccip/ccip_token_transfer_test.go:* + path: integration-tests/smoke/ccip/ccip_token_transfer_test.go + test_env_type: in-memory + runs_on: ubuntu-latest + triggers: + - PR Integration CCIP Tests + test_cmd: cd integration-tests/ && go test smoke/ccip/ccip_token_transfer_test.go -timeout 16m -test.parallel=1 -count=1 -json + # END: CCIP tests diff --git a/.github/workflows/changeset.yml b/.github/workflows/changeset.yml index 331492eb74f..d6d269326c7 100644 --- a/.github/workflows/changeset.yml +++ b/.github/workflows/changeset.yml @@ -184,10 +184,16 @@ jobs: - name: Setup node uses: ./.github/actions/setup-nodejs if: ${{ steps.files-changed.outputs.contracts-changeset == 'true' }} - + + - name: Install base dependencies + if: ${{ steps.files-changed.outputs.contracts-changeset == 'true' }} + run: pnpm i + - name: Validate changeset files if: ${{ steps.files-changed.outputs.contracts-changeset == 'true' }} working-directory: contracts + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | pnpm changeset version diff --git a/common/client/transaction_sender.go b/common/client/transaction_sender.go index cd2ce96c5b2..5f58682142f 100644 --- a/common/client/transaction_sender.go +++ b/common/client/transaction_sender.go @@ -93,6 +93,8 @@ type TransactionSender[TX any, RESULT SendTxResult, CHAIN_ID types.ID, RPC SendT // * Otherwise, returns any (effectively random) of the errors. func (txSender *TransactionSender[TX, RESULT, CHAIN_ID, RPC]) SendTransaction(ctx context.Context, tx TX) RESULT { var result RESULT + ctx, cancel := txSender.chStop.Ctx(ctx) + defer cancel() if !txSender.IfStarted(func() { txResults := make(chan RESULT) txResultsToReport := make(chan RESULT) @@ -103,8 +105,6 @@ func (txSender *TransactionSender[TX, RESULT, CHAIN_ID, RPC]) SendTransaction(ct if isSendOnly { txSender.wg.Add(1) go func(ctx context.Context) { - ctx, cancel := txSender.chStop.Ctx(context.WithoutCancel(ctx)) - defer cancel() defer txSender.wg.Done() // Send-only nodes' results are ignored as they tend to return false-positive responses. // Broadcast to them is necessary to speed up the propagation of TX in the network. @@ -117,8 +117,9 @@ func (txSender *TransactionSender[TX, RESULT, CHAIN_ID, RPC]) SendTransaction(ct healthyNodesNum++ primaryNodeWg.Add(1) go func(ctx context.Context) { - ctx, cancel := txSender.chStop.Ctx(context.WithoutCancel(ctx)) - defer cancel() + // Broadcasting transaction and results reporting for invariant detection are background jobs that must be detached from + // callers cancellation. + // Results reporting to SendTransaction caller must respect caller's context to avoid goroutine leak. defer primaryNodeWg.Done() r := txSender.broadcastTxAsync(ctx, rpc, tx) select { @@ -128,6 +129,8 @@ func (txSender *TransactionSender[TX, RESULT, CHAIN_ID, RPC]) SendTransaction(ct case txResults <- r: } + ctx, cancel := txSender.chStop.Ctx(context.WithoutCancel(ctx)) + defer cancel() select { case <-ctx.Done(): txSender.lggr.Debugw("Failed to send tx results to report", "err", ctx.Err()) @@ -151,8 +154,13 @@ func (txSender *TransactionSender[TX, RESULT, CHAIN_ID, RPC]) SendTransaction(ct return } + if healthyNodesNum == 0 { + result = txSender.newResult(ErroringNodeError) + return + } + txSender.wg.Add(1) - go txSender.reportSendTxAnomalies(ctx, tx, txResultsToReport) + go txSender.reportSendTxAnomalies(tx, txResultsToReport) result = txSender.collectTxResults(ctx, tx, healthyNodesNum, txResults) }) { @@ -163,6 +171,9 @@ func (txSender *TransactionSender[TX, RESULT, CHAIN_ID, RPC]) SendTransaction(ct } func (txSender *TransactionSender[TX, RESULT, CHAIN_ID, RPC]) broadcastTxAsync(ctx context.Context, rpc RPC, tx TX) RESULT { + // broadcast is a background job, so always detach from caller's cancellation + ctx, cancel := txSender.chStop.Ctx(context.WithoutCancel(ctx)) + defer cancel() result := rpc.SendTransaction(ctx, tx) txSender.lggr.Debugw("Node sent transaction", "tx", tx, "err", result.Error()) if !slices.Contains(sendTxSuccessfulCodes, result.Code()) && ctx.Err() == nil { @@ -171,7 +182,7 @@ func (txSender *TransactionSender[TX, RESULT, CHAIN_ID, RPC]) broadcastTxAsync(c return result } -func (txSender *TransactionSender[TX, RESULT, CHAIN_ID, RPC]) reportSendTxAnomalies(ctx context.Context, tx TX, txResults <-chan RESULT) { +func (txSender *TransactionSender[TX, RESULT, CHAIN_ID, RPC]) reportSendTxAnomalies(tx TX, txResults <-chan RESULT) { defer txSender.wg.Done() resultsByCode := sendTxResults[RESULT]{} // txResults eventually will be closed @@ -179,8 +190,17 @@ func (txSender *TransactionSender[TX, RESULT, CHAIN_ID, RPC]) reportSendTxAnomal resultsByCode[txResult.Code()] = append(resultsByCode[txResult.Code()], txResult) } + select { + case <-txSender.chStop: + // it's ok to receive no results if txSender is closing. Return early to prevent false reporting of invariant violation. + if len(resultsByCode) == 0 { + return + } + default: + } + _, criticalErr := aggregateTxResults[RESULT](resultsByCode) - if criticalErr != nil && ctx.Err() == nil { + if criticalErr != nil { txSender.lggr.Criticalw("observed invariant violation on SendTransaction", "tx", tx, "resultsByCode", resultsByCode, "err", criticalErr) PromMultiNodeInvariantViolations.WithLabelValues(txSender.chainFamily, txSender.chainID.String(), criticalErr.Error()).Inc() } @@ -218,9 +238,6 @@ func aggregateTxResults[RESULT any](resultsByCode sendTxResults[RESULT]) (result } func (txSender *TransactionSender[TX, RESULT, CHAIN_ID, RPC]) collectTxResults(ctx context.Context, tx TX, healthyNodesNum int, txResults <-chan RESULT) RESULT { - if healthyNodesNum == 0 { - return txSender.newResult(ErroringNodeError) - } requiredResults := int(math.Ceil(float64(healthyNodesNum) * sendTxQuorum)) errorsByCode := sendTxResults[RESULT]{} var softTimeoutChan <-chan time.Time diff --git a/common/client/transaction_sender_test.go b/common/client/transaction_sender_test.go index e9869610828..656791b7e86 100644 --- a/common/client/transaction_sender_test.go +++ b/common/client/transaction_sender_test.go @@ -14,6 +14,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink/v2/common/types" ) @@ -293,6 +294,27 @@ func TestTransactionSender_SendTransaction(t *testing.T) { require.NoError(t, result.Error()) require.Equal(t, Successful, result.Code()) }) + t.Run("All background jobs stop even if RPC returns result after soft timeout", func(t *testing.T) { + chainID := types.RandomID() + expectedError := errors.New("transaction failed") + fastNode := newNode(t, expectedError, nil) + + // hold reply from the node till SendTransaction returns result + sendTxContext, sendTxCancel := context.WithCancel(tests.Context(t)) + slowNode := newNode(t, errors.New("transaction failed"), func(_ mock.Arguments) { + <-sendTxContext.Done() + }) + + lggr := logger.Test(t) + + _, txSender := newTestTransactionSender(t, chainID, lggr, []Node[types.ID, TestSendTxRPCClient]{fastNode, slowNode}, nil) + result := txSender.SendTransaction(sendTxContext, nil) + sendTxCancel() + require.EqualError(t, result.Error(), expectedError.Error()) + // TxSender should stop all background go routines after SendTransaction is done and before test is done. + // Otherwise, it signals that we have a goroutine leak. + txSender.wg.Wait() + }) } func TestTransactionSender_SendTransaction_aggregateTxResults(t *testing.T) { diff --git a/core/capabilities/ccip/oraclecreator/plugin.go b/core/capabilities/ccip/oraclecreator/plugin.go index 1b8c6344349..a716d96418a 100644 --- a/core/capabilities/ccip/oraclecreator/plugin.go +++ b/core/capabilities/ccip/oraclecreator/plugin.go @@ -270,7 +270,7 @@ func (i *pluginOracleCreator) createFactoryAndTransmitter( rmnPeerClient, rmnCrypto, ) - factory = promwrapper.NewReportingPluginFactory[[]byte](factory, chainID, "CCIPCommit") + factory = promwrapper.NewReportingPluginFactory[[]byte](factory, i.lggr, chainID, "CCIPCommit") transmitter = ocrimpls.NewCommitContractTransmitter[[]byte](destChainWriter, ocrtypes.Account(destFromAccounts[0]), hexutil.Encode(config.Config.OfframpAddress), // TODO: this works for evm only, how about non-evm? @@ -291,7 +291,7 @@ func (i *pluginOracleCreator) createFactoryAndTransmitter( contractReaders, chainWriters, ) - factory = promwrapper.NewReportingPluginFactory[[]byte](factory, chainID, "CCIPExec") + factory = promwrapper.NewReportingPluginFactory[[]byte](factory, i.lggr, chainID, "CCIPExec") transmitter = ocrimpls.NewExecContractTransmitter[[]byte](destChainWriter, ocrtypes.Account(destFromAccounts[0]), hexutil.Encode(config.Config.OfframpAddress), // TODO: this works for evm only, how about non-evm? diff --git a/core/scripts/go.mod b/core/scripts/go.mod index d468907c550..8f9e458b8e2 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -297,7 +297,7 @@ require ( github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/shirou/gopsutil/v3 v3.24.3 // indirect github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86 // indirect - github.com/smartcontractkit/chain-selectors v1.0.31 // indirect + github.com/smartcontractkit/chain-selectors v1.0.34 // indirect github.com/smartcontractkit/chainlink-ccip v0.0.0-20241204015713-8956bb614e9e // indirect github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e // indirect github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241202141438-a90db35252db // indirect @@ -310,7 +310,7 @@ require ( github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20241009055228-33d0c0bf38de // indirect github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20241009055228-33d0c0bf38de // indirect - github.com/smartcontractkit/wsrpc v0.8.2 // indirect + github.com/smartcontractkit/wsrpc v0.8.3 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 225ae3b9191..3b8c5987c00 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1136,8 +1136,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86 h1:qQH6fZZe31nBAG6INHph3z5ysDTPptyu0TR9uoJ1+ok= github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86/go.mod h1:WtWOoVQQEHxRHL2hNmuRrvDfYfQG/CioFNoa9Rr2mBE= -github.com/smartcontractkit/chain-selectors v1.0.31 h1:oRHyK88KnsCh4OdU2hr0u70pm3KUgyMDyK0v0aOtUk4= -github.com/smartcontractkit/chain-selectors v1.0.31/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= +github.com/smartcontractkit/chain-selectors v1.0.34 h1:MJ17OGu8+jjl426pcKrJkCf3fePb3eCreuAnUA3RBj4= +github.com/smartcontractkit/chain-selectors v1.0.34/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= 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= @@ -1168,8 +1168,8 @@ github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20241009055228- github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20241009055228-33d0c0bf38de/go.mod h1:Sl2MF/Fp3fgJIVzhdGhmZZX2BlnM0oUUyBP4s4xYb6o= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20241009055228-33d0c0bf38de h1:66VQxXx3lvTaAZrMBkIcdH9VEjujUEvmBQdnyOJnkOc= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20241009055228-33d0c0bf38de/go.mod h1:NSc7hgOQbXG3DAwkOdWnZzLTZENXSwDJ7Va1nBp0YU0= -github.com/smartcontractkit/wsrpc v0.8.2 h1:XB/xcn/MMseHW+8JE8+a/rceA86ck7Ur6cEa9LiUC8M= -github.com/smartcontractkit/wsrpc v0.8.2/go.mod h1:2u/wfnhl5R4RlSXseN4n6HHIWk8w1Am3AT6gWftQbNg= +github.com/smartcontractkit/wsrpc v0.8.3 h1:9tDf7Ut61g36RJIyxV9iI73SqoOMasKPfURV9oMLrPg= +github.com/smartcontractkit/wsrpc v0.8.3/go.mod h1:2u/wfnhl5R4RlSXseN4n6HHIWk8w1Am3AT6gWftQbNg= github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= diff --git a/core/scripts/keystone/src/88_gen_ocr3_config.go b/core/scripts/keystone/src/88_gen_ocr3_config.go index f4292e9a1d4..707616b833b 100644 --- a/core/scripts/keystone/src/88_gen_ocr3_config.go +++ b/core/scripts/keystone/src/88_gen_ocr3_config.go @@ -13,9 +13,8 @@ func mustReadConfig(fileName string) (output ksdeploy.TopLevelConfigSource) { func generateOCR3Config(nodeList string, configFile string, chainID int64, pubKeysPath string) ksdeploy.OCR2OracleConfig { topLevelCfg := mustReadConfig(configFile) cfg := topLevelCfg.OracleConfig - cfg.OCRSecrets = deployment.XXXGenerateTestOCRSecrets() nca := downloadNodePubKeys(nodeList, chainID, pubKeysPath) - c, err := ksdeploy.GenerateOCR3Config(cfg, nca) + c, err := ksdeploy.GenerateOCR3Config(cfg, nca, deployment.XXXGenerateTestOCRSecrets()) helpers.PanicErr(err) return c } diff --git a/core/services/llo/delegate.go b/core/services/llo/delegate.go index d305fe0e948..ba4ddbb8fb0 100644 --- a/core/services/llo/delegate.go +++ b/core/services/llo/delegate.go @@ -21,6 +21,7 @@ import ( corelogger "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr3/promwrapper" "github.com/smartcontractkit/chainlink/v2/core/services/streams" ) @@ -59,6 +60,7 @@ type DelegateConfig struct { ShouldRetireCache datastreamsllo.ShouldRetireCache EAMonitoringEndpoint ocrcommontypes.MonitoringEndpoint DonID uint32 + ChainID string // OCR3 TraceLogging bool @@ -151,8 +153,21 @@ func (d *delegate) Start(ctx context.Context) error { OffchainConfigDigester: d.cfg.OffchainConfigDigester, OffchainKeyring: d.cfg.OffchainKeyring, OnchainKeyring: d.cfg.OnchainKeyring, - ReportingPluginFactory: datastreamsllo.NewPluginFactory( - d.cfg.ReportingPluginConfig, psrrc, d.src, d.cfg.RetirementReportCodec, d.cfg.ChannelDefinitionCache, d.ds, logger.Named(lggr, "ReportingPlugin"), llo.EVMOnchainConfigCodec{}, d.reportCodecs, + ReportingPluginFactory: promwrapper.NewReportingPluginFactory( + datastreamsllo.NewPluginFactory( + d.cfg.ReportingPluginConfig, + psrrc, + d.src, + d.cfg.RetirementReportCodec, + d.cfg.ChannelDefinitionCache, + d.ds, + logger.Named(lggr, "ReportingPlugin"), + llo.EVMOnchainConfigCodec{}, + d.reportCodecs, + ), + lggr, + d.cfg.ChainID, + "llo", ), MetricsRegisterer: prometheus.WrapRegistererWith(map[string]string{"job_name": d.cfg.JobName.ValueOrZero()}, prometheus.DefaultRegisterer), }) diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index e7a5a1c3a92..edcc816bf04 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -1048,6 +1048,7 @@ func (d *Delegate) newServicesLLO( RetirementReportCodec: datastreamsllo.StandardRetirementReportCodec{}, EAMonitoringEndpoint: d.monitoringEndpointGen.GenMonitoringEndpoint(rid.Network, rid.ChainID, telemetryContractID, synchronization.EnhancedEAMercury), DonID: pluginCfg.DonID, + ChainID: rid.ChainID, TraceLogging: d.cfg.OCR2().TraceLogging(), BinaryNetworkEndpointFactory: d.peerWrapper.Peer2, diff --git a/core/services/ocr3/promwrapper/factory.go b/core/services/ocr3/promwrapper/factory.go index 0dabd346112..6518cea3c0d 100644 --- a/core/services/ocr3/promwrapper/factory.go +++ b/core/services/ocr3/promwrapper/factory.go @@ -3,6 +3,8 @@ package promwrapper import ( "context" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" ) @@ -10,17 +12,20 @@ var _ ocr3types.ReportingPluginFactory[any] = &ReportingPluginFactory[any]{} type ReportingPluginFactory[RI any] struct { origin ocr3types.ReportingPluginFactory[RI] + lggr logger.Logger chainID string plugin string } func NewReportingPluginFactory[RI any]( origin ocr3types.ReportingPluginFactory[RI], + lggr logger.Logger, chainID string, plugin string, ) *ReportingPluginFactory[RI] { return &ReportingPluginFactory[RI]{ origin: origin, + lggr: lggr, chainID: chainID, plugin: plugin, } @@ -31,12 +36,18 @@ func (r ReportingPluginFactory[RI]) NewReportingPlugin(ctx context.Context, conf if err != nil { return nil, ocr3types.ReportingPluginInfo{}, err } + r.lggr.Infow("Wrapping ReportingPlugin with prometheus metrics reporter", + "configDigest", config.ConfigDigest, + "oracleID", config.OracleID, + ) wrapped := newReportingPlugin( plugin, r.chainID, r.plugin, + config.ConfigDigest.String(), promOCR3ReportsGenerated, promOCR3Durations, + promOCR3PluginStatus, ) return wrapped, info, err } diff --git a/core/services/ocr3/promwrapper/factory_test.go b/core/services/ocr3/promwrapper/factory_test.go index 72f35aad172..fb68c039b78 100644 --- a/core/services/ocr3/promwrapper/factory_test.go +++ b/core/services/ocr3/promwrapper/factory_test.go @@ -10,11 +10,13 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + + "github.com/smartcontractkit/chainlink/v2/core/logger" ) func Test_WrapperFactory(t *testing.T) { - validFactory := NewReportingPluginFactory(fakeFactory[uint]{}, "solana", "plugin") - failingFactory := NewReportingPluginFactory(fakeFactory[uint]{err: errors.New("error")}, "123", "plugin") + validFactory := NewReportingPluginFactory(fakeFactory[uint]{}, logger.TestLogger(t), "solana", "plugin") + failingFactory := NewReportingPluginFactory(fakeFactory[uint]{err: errors.New("error")}, logger.TestLogger(t), "123", "plugin") plugin, _, err := validFactory.NewReportingPlugin(tests.Context(t), ocr3types.ReportingPluginConfig{}) require.NoError(t, err) diff --git a/core/services/ocr3/promwrapper/plugin.go b/core/services/ocr3/promwrapper/plugin.go index e4e0c3d35d5..dcee5050d1e 100644 --- a/core/services/ocr3/promwrapper/plugin.go +++ b/core/services/ocr3/promwrapper/plugin.go @@ -14,27 +14,33 @@ var _ ocr3types.ReportingPlugin[any] = &reportingPlugin[any]{} type reportingPlugin[RI any] struct { ocr3types.ReportingPlugin[RI] - chainID string - plugin string + chainID string + plugin string + configDigest string // Prometheus components for tracking metrics reportsGenerated *prometheus.CounterVec durations *prometheus.HistogramVec + status *prometheus.GaugeVec } func newReportingPlugin[RI any]( origin ocr3types.ReportingPlugin[RI], chainID string, plugin string, + configDigest string, reportsGenerated *prometheus.CounterVec, durations *prometheus.HistogramVec, + status *prometheus.GaugeVec, ) *reportingPlugin[RI] { return &reportingPlugin[RI]{ ReportingPlugin: origin, chainID: chainID, plugin: plugin, + configDigest: configDigest, reportsGenerated: reportsGenerated, durations: durations, + status: status, } } @@ -88,15 +94,23 @@ func (p *reportingPlugin[RI]) ShouldTransmitAcceptedReport(ctx context.Context, return result, err } -func (p *reportingPlugin[RI]) trackReports( - function functionType, - count int, -) { +func (p *reportingPlugin[RI]) Close() error { + p.updateStatus(false) + return p.ReportingPlugin.Close() +} + +func (p *reportingPlugin[RI]) trackReports(function functionType, count int) { p.reportsGenerated. WithLabelValues(p.chainID, p.plugin, string(function)). Add(float64(count)) } +func (p *reportingPlugin[RI]) updateStatus(status bool) { + p.status. + WithLabelValues(p.chainID, p.plugin, p.configDigest). + Set(float64(boolToInt(status))) +} + func boolToInt(arg bool) int { if arg { return 1 @@ -118,5 +132,7 @@ func withObservedExecution[RI, R any]( WithLabelValues(p.chainID, p.plugin, string(function), strconv.FormatBool(success)). Observe(float64(time.Since(start))) + p.updateStatus(true) + return result, err } diff --git a/core/services/ocr3/promwrapper/plugin_test.go b/core/services/ocr3/promwrapper/plugin_test.go index 35a97d109aa..9a7b6f2e648 100644 --- a/core/services/ocr3/promwrapper/plugin_test.go +++ b/core/services/ocr3/promwrapper/plugin_test.go @@ -19,15 +19,15 @@ import ( func Test_ReportsGeneratedGauge(t *testing.T) { plugin1 := newReportingPlugin( fakePlugin[uint]{reports: make([]ocr3types.ReportPlus[uint], 2)}, - "123", "empty", promOCR3ReportsGenerated, promOCR3Durations, + "123", "empty", "abc", promOCR3ReportsGenerated, promOCR3Durations, promOCR3PluginStatus, ) plugin2 := newReportingPlugin( fakePlugin[bool]{reports: make([]ocr3types.ReportPlus[bool], 10)}, - "solana", "different_plugin", promOCR3ReportsGenerated, promOCR3Durations, + "solana", "different_plugin", "abc", promOCR3ReportsGenerated, promOCR3Durations, promOCR3PluginStatus, ) plugin3 := newReportingPlugin( fakePlugin[string]{err: errors.New("error")}, - "1234", "empty", promOCR3ReportsGenerated, promOCR3Durations, + "1234", "empty", "abc", promOCR3ReportsGenerated, promOCR3Durations, promOCR3PluginStatus, ) r1, err := plugin1.Reports(tests.Context(t), 1, nil) @@ -57,20 +57,27 @@ func Test_ReportsGeneratedGauge(t *testing.T) { g4 := testutil.ToFloat64(promOCR3ReportsGenerated.WithLabelValues("1234", "empty", "reports")) require.Equal(t, 0, int(g4)) + + pluginHealth := testutil.ToFloat64(promOCR3PluginStatus.WithLabelValues("123", "empty", "abc")) + require.Equal(t, 1, int(pluginHealth)) + + require.NoError(t, plugin1.Close()) + pluginHealth = testutil.ToFloat64(promOCR3PluginStatus.WithLabelValues("123", "empty", "abc")) + require.Equal(t, 0, int(pluginHealth)) } func Test_DurationHistograms(t *testing.T) { plugin1 := newReportingPlugin( fakePlugin[uint]{}, - "123", "empty", promOCR3ReportsGenerated, promOCR3Durations, + "123", "empty", "abc", promOCR3ReportsGenerated, promOCR3Durations, promOCR3PluginStatus, ) plugin2 := newReportingPlugin( fakePlugin[uint]{err: errors.New("error")}, - "123", "empty", promOCR3ReportsGenerated, promOCR3Durations, + "123", "empty", "abc", promOCR3ReportsGenerated, promOCR3Durations, promOCR3PluginStatus, ) plugin3 := newReportingPlugin( fakePlugin[uint]{}, - "solana", "commit", promOCR3ReportsGenerated, promOCR3Durations, + "solana", "commit", "abc", promOCR3ReportsGenerated, promOCR3Durations, promOCR3PluginStatus, ) for _, p := range []*reportingPlugin[uint]{plugin1, plugin2, plugin3} { diff --git a/core/services/ocr3/promwrapper/types.go b/core/services/ocr3/promwrapper/types.go index bf6a1b2a39c..2fa29dcdf20 100644 --- a/core/services/ocr3/promwrapper/types.go +++ b/core/services/ocr3/promwrapper/types.go @@ -48,4 +48,11 @@ var ( }, []string{"chainID", "plugin", "function", "success"}, ) + promOCR3PluginStatus = promauto.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "ocr3_reporting_plugin_status", + Help: "Gauge indicating whether plugin is up and running or not", + }, + []string{"chainID", "plugin", "configDigest"}, + ) ) diff --git a/deployment/address_book.go b/deployment/address_book.go index 28d728bf6c7..6f605013011 100644 --- a/deployment/address_book.go +++ b/deployment/address_book.go @@ -271,3 +271,26 @@ func AddressBookContains(ab AddressBook, chain uint64, addrToFind string) (bool, return false, nil } + +// AddressesContainBundle checks if the addresses +// contains a single instance of all the addresses in the bundle. +// It returns an error if there are more than one instance of a contract. +func AddressesContainBundle(addrs map[string]TypeAndVersion, wantTypes map[TypeAndVersion]struct{}) (bool, error) { + counts := make(map[TypeAndVersion]int) + for wantType := range wantTypes { + for _, haveType := range addrs { + if wantType == haveType { + counts[wantType]++ + if counts[wantType] > 1 { + return false, fmt.Errorf("found more than one instance of contract %s", wantType) + } + } + } + } + // Either 0 or 1, so we can just check the sum. + sum := 0 + for _, count := range counts { + sum += count + } + return sum == len(wantTypes), nil +} diff --git a/deployment/address_book_test.go b/deployment/address_book_test.go index 35efdbf8546..e022e89a9ab 100644 --- a/deployment/address_book_test.go +++ b/deployment/address_book_test.go @@ -243,3 +243,36 @@ func TestAddressBook_ConcurrencyAndDeadlock(t *testing.T) { wg.Wait() } + +func TestAddressesContainsBundle(t *testing.T) { + onRamp100 := NewTypeAndVersion("OnRamp", Version1_0_0) + onRamp110 := NewTypeAndVersion("OnRamp", Version1_1_0) + onRamp120 := NewTypeAndVersion("OnRamp", Version1_2_0) + addr1 := common.HexToAddress("0x1").String() + addr2 := common.HexToAddress("0x2").String() + addr3 := common.HexToAddress("0x3").String() + + // More than one instance should error + _, err := AddressesContainBundle(map[string]TypeAndVersion{ + addr1: onRamp100, + addr2: onRamp100, + }, map[TypeAndVersion]struct{}{onRamp100: {}}) + require.Error(t, err) + + // No such instances should be false + exists, err := AddressesContainBundle(map[string]TypeAndVersion{ + addr2: onRamp110, + addr1: onRamp110, + }, map[TypeAndVersion]struct{}{onRamp100: {}}) + require.NoError(t, err) + assert.Equal(t, exists, false) + + // 2 elements + exists, err = AddressesContainBundle(map[string]TypeAndVersion{ + addr1: onRamp100, + addr2: onRamp110, + addr3: onRamp120, + }, map[TypeAndVersion]struct{}{onRamp100: {}, onRamp110: {}}) + require.NoError(t, err) + assert.Equal(t, exists, true) +} diff --git a/deployment/ccip/changeset/cs_active_candidate.go b/deployment/ccip/changeset/cs_active_candidate.go index b2aad3889ec..ecd87e2d399 100644 --- a/deployment/ccip/changeset/cs_active_candidate.go +++ b/deployment/ccip/changeset/cs_active_candidate.go @@ -137,7 +137,7 @@ func SetCandidatePluginChangeset( } newDONArgs, err := internal.BuildOCR3ConfigForCCIPHome( - cfg.OCRSecrets, + e.OCRSecrets, state.Chains[cfg.NewChainSelector].OffRamp, e.Chains[cfg.NewChainSelector], nodes.NonBootstraps(), diff --git a/deployment/ccip/changeset/cs_active_candidate_test.go b/deployment/ccip/changeset/cs_active_candidate_test.go index 4bd0c9fd7a4..0fb29242794 100644 --- a/deployment/ccip/changeset/cs_active_candidate_test.go +++ b/deployment/ccip/changeset/cs_active_candidate_test.go @@ -144,7 +144,7 @@ func TestActiveCandidate(t *testing.T) { nil, ) ocr3ConfigMap, err := internal.BuildOCR3ConfigForCCIPHome( - deployment.XXXGenerateTestOCRSecrets(), + e.OCRSecrets, state.Chains[tenv.FeedChainSel].OffRamp, e.Chains[tenv.FeedChainSel], nodes.NonBootstraps(), diff --git a/deployment/ccip/changeset/cs_add_chain.go b/deployment/ccip/changeset/cs_add_chain.go index 9c19517260f..d589d16b779 100644 --- a/deployment/ccip/changeset/cs_add_chain.go +++ b/deployment/ccip/changeset/cs_add_chain.go @@ -8,6 +8,7 @@ import ( "github.com/smartcontractkit/chainlink-ccip/chainconfig" "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/internal" "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" @@ -142,7 +143,6 @@ type AddDonAndSetCandidateChangesetConfig struct { PluginType types.PluginType NodeIDs []string CCIPOCRParams CCIPOCRParams - OCRSecrets deployment.OCRSecrets } func (a AddDonAndSetCandidateChangesetConfig) Validate(e deployment.Environment, state CCIPOnChainState) (deployment.Nodes, error) { @@ -191,7 +191,7 @@ func (a AddDonAndSetCandidateChangesetConfig) Validate(e deployment.Environment, return nil, fmt.Errorf("invalid ccip ocr params: %w", err) } - if a.OCRSecrets.IsEmpty() { + if e.OCRSecrets.IsEmpty() { return nil, fmt.Errorf("OCR secrets must be set") } @@ -215,7 +215,7 @@ func AddDonAndSetCandidateChangeset( } newDONArgs, err := internal.BuildOCR3ConfigForCCIPHome( - cfg.OCRSecrets, + e.OCRSecrets, state.Chains[cfg.NewChainSelector].OffRamp, e.Chains[cfg.NewChainSelector], nodes.NonBootstraps(), diff --git a/deployment/ccip/changeset/cs_add_chain_test.go b/deployment/ccip/changeset/cs_add_chain_test.go index c83872c0dd7..c8d872236c2 100644 --- a/deployment/ccip/changeset/cs_add_chain_test.go +++ b/deployment/ccip/changeset/cs_add_chain_test.go @@ -84,7 +84,6 @@ func TestAddChainInbound(t *testing.T) { HomeChainSel: e.HomeChainSel, FeedChainSel: e.FeedChainSel, ChainConfigByChain: chainConfig, - OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), }) require.NoError(t, err) @@ -191,7 +190,6 @@ func TestAddChainInbound(t *testing.T) { NewChainSelector: newChain, PluginType: types.PluginTypeCCIPCommit, NodeIDs: nodeIDs, - OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), CCIPOCRParams: DefaultOCRParams( e.FeedChainSel, tokenConfig.GetTokenInfo(logger.TestLogger(t), state.Chains[newChain].LinkToken, state.Chains[newChain].Weth9), @@ -207,7 +205,6 @@ func TestAddChainInbound(t *testing.T) { NewChainSelector: newChain, PluginType: types.PluginTypeCCIPExec, NodeIDs: nodeIDs, - OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), CCIPOCRParams: DefaultOCRParams( e.FeedChainSel, tokenConfig.GetTokenInfo(logger.TestLogger(t), state.Chains[newChain].LinkToken, state.Chains[newChain].Weth9), @@ -227,6 +224,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..13aee106e5a 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" @@ -75,7 +76,6 @@ type NewChainsConfig struct { // Common to all chains HomeChainSel uint64 FeedChainSel uint64 - OCRSecrets deployment.OCRSecrets // Per chain config ChainConfigByChain map[uint64]CCIPOCRParams } @@ -95,9 +95,6 @@ func (c NewChainsConfig) Validate() error { if err := deployment.IsValidChainSelector(c.FeedChainSel); err != nil { return fmt.Errorf("invalid feed chain selector: %d - %w", c.FeedChainSel, err) } - if c.OCRSecrets.IsEmpty() { - return fmt.Errorf("no OCR secrets provided") - } // Validate chain config for chain, cfg := range c.ChainConfigByChain { if err := cfg.Validate(); err != nil { @@ -165,7 +162,7 @@ func configureChain( e deployment.Environment, c NewChainsConfig, ) error { - if c.OCRSecrets.IsEmpty() { + if e.OCRSecrets.IsEmpty() { return fmt.Errorf("OCR secrets are empty") } nodes, err := deployment.NodeInfo(e.NodeIDs, e.Offchain) @@ -178,19 +175,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") } @@ -215,7 +213,7 @@ func configureChain( // For each chain, we create a DON on the home chain (2 OCR instances) if err := addDON( e.Logger, - c.OCRSecrets, + e.OCRSecrets, capReg, ccipHome, rmnHome.Address(), @@ -267,7 +265,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 +299,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 +367,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 +407,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 +428,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 +449,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..22ae59fc360 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" @@ -83,6 +82,7 @@ var ( type CCIPChainState struct { commoncs.MCMSWithTimelockState commoncs.LinkTokenState + commoncs.StaticLinkTokenState OnRamp *onramp.OnRamp OffRamp *offramp.OffRamp FeeQuoter *fee_quoter.FeeQuoter @@ -134,35 +134,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 +174,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 +185,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 +193,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,35 +201,42 @@ 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) + linkTokenView, err := c.GenerateLinkView() 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 } + if c.StaticLinkToken != nil { + staticLinkTokenView, err := c.GenerateStaticLinkView() + if err != nil { + return chainView, err + } + chainView.StaticLinkToken = staticLinkTokenView + } return chainView, nil } -// Onchain state always derivable from an address book. +// CCIPOnChainState state always derivable from an address book. // Offchain state always derivable from a list of nodeIds. // Note can translate this into Go struct needed for MCMS/Docs/UI. type CCIPOnChainState struct { @@ -242,12 +249,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 +261,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 } @@ -290,24 +292,31 @@ func LoadOnchainState(e deployment.Environment) (CCIPOnChainState, error) { // LoadChainState Loads all state for a chain into state func LoadChainState(chain deployment.Chain, addresses map[string]deployment.TypeAndVersion) (CCIPChainState, error) { var state CCIPChainState - mcmsWithTimelock, err := commoncs.LoadMCMSWithTimelockState(chain, addresses) + mcmsWithTimelock, err := commoncs.MaybeLoadMCMSWithTimelockState(chain, addresses) if err != nil { return state, err } state.MCMSWithTimelockState = *mcmsWithTimelock - linkState, err := commoncs.LoadLinkTokenState(chain, addresses) + linkState, err := commoncs.MaybeLoadLinkTokenState(chain, addresses) if err != nil { return state, err } state.LinkTokenState = *linkState + staticLinkState, err := commoncs.MaybeLoadStaticLinkTokenState(chain, addresses) + if err != nil { + return state, err + } + state.StaticLinkTokenState = *staticLinkState for address, tvStr := range addresses { switch tvStr.String() { case deployment.NewTypeAndVersion(commontypes.RBACTimelock, deployment.Version1_0_0).String(), deployment.NewTypeAndVersion(commontypes.ProposerManyChainMultisig, deployment.Version1_0_0).String(), deployment.NewTypeAndVersion(commontypes.CancellerManyChainMultisig, deployment.Version1_0_0).String(), deployment.NewTypeAndVersion(commontypes.BypasserManyChainMultisig, deployment.Version1_0_0).String(), - deployment.NewTypeAndVersion(commontypes.LinkToken, deployment.Version1_0_0).String(): + deployment.NewTypeAndVersion(commontypes.LinkToken, deployment.Version1_0_0).String(), + deployment.NewTypeAndVersion(commontypes.StaticLinkToken, deployment.Version1_0_0).String(): + // Skip common contracts, they are already loaded. continue case deployment.NewTypeAndVersion(CapabilitiesRegistry, deployment.Version1_0_0).String(): cr, err := capabilities_registry.NewCapabilitiesRegistry(common.HexToAddress(address), chain.Client) diff --git a/deployment/ccip/changeset/state_test.go b/deployment/ccip/changeset/state_test.go new file mode 100644 index 00000000000..6e679c265dc --- /dev/null +++ b/deployment/ccip/changeset/state_test.go @@ -0,0 +1,24 @@ +package changeset + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/deployment/environment/memory" + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +func TestSmokeState(t *testing.T) { + lggr := logger.TestLogger(t) + tenv := NewMemoryEnvironmentWithJobsAndContracts(t, lggr, memory.MemoryEnvironmentConfig{ + Chains: 3, + Nodes: 4, + Bootstraps: 1, + NumOfUsersPerChain: 1, + }, nil) + state, err := LoadOnchainState(tenv.Env) + require.NoError(t, err) + _, err = state.View(tenv.Env.AllChainSelectors()) + require.NoError(t, err) +} diff --git a/deployment/ccip/changeset/test_helpers.go b/deployment/ccip/changeset/test_helpers.go index 1ee8b0d0e42..49edb275526 100644 --- a/deployment/ccip/changeset/test_helpers.go +++ b/deployment/ccip/changeset/test_helpers.go @@ -379,7 +379,6 @@ func NewMemoryEnvironmentWithJobsAndContracts(t *testing.T, lggr logger.Logger, Config: NewChainsConfig{ HomeChainSel: e.HomeChainSel, FeedChainSel: e.FeedChainSel, - OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), ChainConfigByChain: chainConfigs, }, }, 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/ccip/changeset/view_test.go b/deployment/ccip/changeset/view_test.go new file mode 100644 index 00000000000..934b937f7b5 --- /dev/null +++ b/deployment/ccip/changeset/view_test.go @@ -0,0 +1,22 @@ +package changeset + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/deployment/environment/memory" + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +func TestSmokeView(t *testing.T) { + lggr := logger.TestLogger(t) + tenv := NewMemoryEnvironmentWithJobsAndContracts(t, lggr, memory.MemoryEnvironmentConfig{ + Chains: 3, + Nodes: 4, + Bootstraps: 1, + NumOfUsersPerChain: 1, + }, nil) + _, err := ViewCCIP(tenv.Env) + require.NoError(t, err) +} diff --git a/deployment/ccip/view/view.go b/deployment/ccip/view/view.go index 836dc9c65dd..1cacd58cc2b 100644 --- a/deployment/ccip/view/view.go +++ b/deployment/ccip/view/view.go @@ -28,6 +28,7 @@ type ChainView struct { CapabilityRegistry map[string]common_v1_0.CapabilityRegistryView `json:"capabilityRegistry,omitempty"` MCMSWithTimelock common_v1_0.MCMSWithTimelockView `json:"mcmsWithTimelock,omitempty"` LinkToken common_v1_0.LinkTokenView `json:"linkToken,omitempty"` + StaticLinkToken common_v1_0.StaticLinkTokenView `json:"staticLinkToken,omitempty"` } func NewChain() ChainView { 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/deploy_link_token_test.go b/deployment/common/changeset/deploy_link_token_test.go index 29a9ece1478..a61743e9bf4 100644 --- a/deployment/common/changeset/deploy_link_token_test.go +++ b/deployment/common/changeset/deploy_link_token_test.go @@ -3,7 +3,6 @@ package changeset_test import ( "testing" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" @@ -28,12 +27,9 @@ func TestDeployLinkToken(t *testing.T) { require.NoError(t, err) addrs, err := e.ExistingAddresses.AddressesForChain(chain1) require.NoError(t, err) - state, err := changeset.LoadLinkTokenState(e.Chains[chain1], addrs) + state, err := changeset.MaybeLoadLinkTokenState(e.Chains[chain1], addrs) require.NoError(t, err) - view, err := state.GenerateLinkView() + // View itself already unit tested + _, err = state.GenerateLinkView() require.NoError(t, err) - assert.Equal(t, view.Owner, e.Chains[chain1].DeployerKey.From) - assert.Equal(t, view.TypeAndVersion, "LinkToken 1.0.0") - // Initially nothing minted. - assert.Equal(t, view.Supply.String(), "0") } 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/changeset/internal/mcms_test.go b/deployment/common/changeset/internal/mcms_test.go index 9969a0e5bc9..2269911f4cd 100644 --- a/deployment/common/changeset/internal/mcms_test.go +++ b/deployment/common/changeset/internal/mcms_test.go @@ -49,7 +49,7 @@ func TestDeployMCMSWithTimelockContracts(t *testing.T) { addresses, err := ab.AddressesForChain(chainsel.TEST_90000001.Selector) require.NoError(t, err) require.Len(t, addresses, 4) - mcmsState, err := changeset.LoadMCMSWithTimelockState(chains[chainsel.TEST_90000001.Selector], addresses) + mcmsState, err := changeset.MaybeLoadMCMSWithTimelockState(chains[chainsel.TEST_90000001.Selector], addresses) require.NoError(t, err) v, err := mcmsState.GenerateMCMSWithTimelockView() b, err := json.MarshalIndent(v, "", " ") diff --git a/deployment/common/changeset/state.go b/deployment/common/changeset/state.go index f553e124a38..0055c908f8d 100644 --- a/deployment/common/changeset/state.go +++ b/deployment/common/changeset/state.go @@ -2,6 +2,7 @@ package changeset import ( "errors" + "fmt" "github.com/ethereum/go-ethereum/common" owner_helpers "github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers" @@ -9,12 +10,14 @@ import ( "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/smartcontractkit/chainlink/deployment/common/view/v1_0" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/link_token" ) // MCMSWithTimelockState holds the Go bindings // for a MCMSWithTimelock contract deployment. // It is public for use in product specific packages. +// Either all fields are nil or all fields are non-nil. type MCMSWithTimelockState struct { CancellerMcm *owner_helpers.ManyChainMultiSig BypasserMcm *owner_helpers.ManyChainMultiSig @@ -22,6 +25,8 @@ type MCMSWithTimelockState struct { Timelock *owner_helpers.RBACTimelock } +// Validate checks that all fields are non-nil, ensuring it's ready +// for use generating views or interactions. func (state MCMSWithTimelockState) Validate() error { if state.Timelock == nil { return errors.New("timelock not found") @@ -66,29 +71,51 @@ func (state MCMSWithTimelockState) GenerateMCMSWithTimelockView() (v1_0.MCMSWith }, nil } -func LoadMCMSWithTimelockState(chain deployment.Chain, addresses map[string]deployment.TypeAndVersion) (*MCMSWithTimelockState, error) { +// MaybeLoadMCMSWithTimelockState looks for the addresses corresponding to +// contracts deployed with DeployMCMSWithTimelock and loads them into a +// MCMSWithTimelockState struct. If none of the contracts are found, the state struct will be nil. +// An error indicates: +// - Found but was unable to load a contract +// - It only found part of the bundle of contracts +// - If found more than one instance of a contract (we expect one bundle in the given addresses) +func MaybeLoadMCMSWithTimelockState(chain deployment.Chain, addresses map[string]deployment.TypeAndVersion) (*MCMSWithTimelockState, error) { state := MCMSWithTimelockState{} + // We expect one of each contract on the chain. + timelock := deployment.NewTypeAndVersion(types.RBACTimelock, deployment.Version1_0_0) + proposer := deployment.NewTypeAndVersion(types.ProposerManyChainMultisig, deployment.Version1_0_0) + canceller := deployment.NewTypeAndVersion(types.CancellerManyChainMultisig, deployment.Version1_0_0) + bypasser := deployment.NewTypeAndVersion(types.BypasserManyChainMultisig, deployment.Version1_0_0) + + // Ensure we either have the bundle or not. + _, err := deployment.AddressesContainBundle(addresses, + map[deployment.TypeAndVersion]struct{}{ + timelock: {}, proposer: {}, canceller: {}, bypasser: {}, + }) + if err != nil { + return nil, fmt.Errorf("unable to check MCMS contracts on chain %s error: %w", chain.Name(), err) + } + for address, tvStr := range addresses { - switch tvStr.String() { - case deployment.NewTypeAndVersion(types.RBACTimelock, deployment.Version1_0_0).String(): + switch tvStr { + case timelock: tl, err := owner_helpers.NewRBACTimelock(common.HexToAddress(address), chain.Client) if err != nil { return nil, err } state.Timelock = tl - case deployment.NewTypeAndVersion(types.ProposerManyChainMultisig, deployment.Version1_0_0).String(): + case proposer: mcms, err := owner_helpers.NewManyChainMultiSig(common.HexToAddress(address), chain.Client) if err != nil { return nil, err } state.ProposerMcm = mcms - case deployment.NewTypeAndVersion(types.BypasserManyChainMultisig, deployment.Version1_0_0).String(): + case bypasser: mcms, err := owner_helpers.NewManyChainMultiSig(common.HexToAddress(address), chain.Client) if err != nil { return nil, err } state.BypasserMcm = mcms - case deployment.NewTypeAndVersion(types.CancellerManyChainMultisig, deployment.Version1_0_0).String(): + case canceller: mcms, err := owner_helpers.NewManyChainMultiSig(common.HexToAddress(address), chain.Client) if err != nil { return nil, err @@ -110,10 +137,17 @@ func (s LinkTokenState) GenerateLinkView() (v1_0.LinkTokenView, error) { return v1_0.GenerateLinkTokenView(s.LinkToken) } -func LoadLinkTokenState(chain deployment.Chain, addresses map[string]deployment.TypeAndVersion) (*LinkTokenState, error) { +func MaybeLoadLinkTokenState(chain deployment.Chain, addresses map[string]deployment.TypeAndVersion) (*LinkTokenState, error) { state := LinkTokenState{} + linkToken := deployment.NewTypeAndVersion(types.LinkToken, deployment.Version1_0_0) + // Perhaps revisit if we have a use case for multiple. + _, err := deployment.AddressesContainBundle(addresses, map[deployment.TypeAndVersion]struct{}{linkToken: {}}) + if err != nil { + return nil, fmt.Errorf("unable to check link token on chain %s error: %w", chain.Name(), err) + } for address, tvStr := range addresses { - if tvStr.String() == deployment.NewTypeAndVersion(types.LinkToken, deployment.Version1_0_0).String() { + switch tvStr { + case linkToken: lt, err := link_token.NewLinkToken(common.HexToAddress(address), chain.Client) if err != nil { return nil, err @@ -123,3 +157,35 @@ func LoadLinkTokenState(chain deployment.Chain, addresses map[string]deployment. } return &state, nil } + +type StaticLinkTokenState struct { + StaticLinkToken *link_token_interface.LinkToken +} + +func (s StaticLinkTokenState) GenerateStaticLinkView() (v1_0.StaticLinkTokenView, error) { + if s.StaticLinkToken == nil { + return v1_0.StaticLinkTokenView{}, errors.New("static link token not found") + } + return v1_0.GenerateStaticLinkTokenView(s.StaticLinkToken) +} + +func MaybeLoadStaticLinkTokenState(chain deployment.Chain, addresses map[string]deployment.TypeAndVersion) (*StaticLinkTokenState, error) { + state := StaticLinkTokenState{} + staticLinkToken := deployment.NewTypeAndVersion(types.StaticLinkToken, deployment.Version1_0_0) + // Perhaps revisit if we have a use case for multiple. + _, err := deployment.AddressesContainBundle(addresses, map[deployment.TypeAndVersion]struct{}{staticLinkToken: {}}) + if err != nil { + return nil, fmt.Errorf("unable to check static link token on chain %s error: %w", chain.Name(), err) + } + for address, tvStr := range addresses { + switch tvStr { + case staticLinkToken: + lt, err := link_token_interface.NewLinkToken(common.HexToAddress(address), chain.Client) + if err != nil { + return nil, err + } + state.StaticLinkToken = lt + } + } + return &state, nil +} diff --git a/deployment/common/changeset/test_helpers.go b/deployment/common/changeset/test_helpers.go index 913b4544f30..2d5295282f5 100644 --- a/deployment/common/changeset/test_helpers.go +++ b/deployment/common/changeset/test_helpers.go @@ -90,6 +90,7 @@ func ApplyChangesets(t *testing.T, e deployment.Environment, timelocksPerChain m Chains: e.Chains, NodeIDs: e.NodeIDs, Offchain: e.Offchain, + OCRSecrets: e.OCRSecrets, } } return currentEnv, nil diff --git a/deployment/common/changeset/transfer_to_mcms_with_timelock_test.go b/deployment/common/changeset/transfer_to_mcms_with_timelock_test.go index f1f24cb0b05..6cdff286707 100644 --- a/deployment/common/changeset/transfer_to_mcms_with_timelock_test.go +++ b/deployment/common/changeset/transfer_to_mcms_with_timelock_test.go @@ -42,9 +42,9 @@ func TestTransferToMCMSWithTimelock(t *testing.T) { require.NoError(t, err) addrs, err := e.ExistingAddresses.AddressesForChain(chain1) require.NoError(t, err) - state, err := LoadMCMSWithTimelockState(e.Chains[chain1], addrs) + state, err := MaybeLoadMCMSWithTimelockState(e.Chains[chain1], addrs) require.NoError(t, err) - link, err := LoadLinkTokenState(e.Chains[chain1], addrs) + link, err := MaybeLoadLinkTokenState(e.Chains[chain1], addrs) require.NoError(t, err) e, err = ApplyChangesets(t, e, map[uint64]*owner_helpers.RBACTimelock{ chain1: state.Timelock, @@ -61,7 +61,7 @@ func TestTransferToMCMSWithTimelock(t *testing.T) { }) require.NoError(t, err) // We expect now that the link token is owned by the MCMS timelock. - link, err = LoadLinkTokenState(e.Chains[chain1], addrs) + link, err = MaybeLoadLinkTokenState(e.Chains[chain1], addrs) require.NoError(t, err) o, err := link.LinkToken.Owner(nil) require.NoError(t, err) diff --git a/deployment/common/types/types.go b/deployment/common/types/types.go index a6504d17a94..386ef8fbb36 100644 --- a/deployment/common/types/types.go +++ b/deployment/common/types/types.go @@ -16,7 +16,16 @@ const ( CancellerManyChainMultisig deployment.ContractType = "CancellerManyChainMultiSig" ProposerManyChainMultisig deployment.ContractType = "ProposerManyChainMultiSig" RBACTimelock deployment.ContractType = "RBACTimelock" - LinkToken deployment.ContractType = "LinkToken" + // LinkToken is the burn/mint link token. It should be used everywhere for + // new deployments. Corresponds to + // https://github.com/smartcontractkit/chainlink/blob/develop/core/gethwrappers/shared/generated/link_token/link_token.go#L34 + LinkToken deployment.ContractType = "LinkToken" + // StaticLinkToken represents the (very old) non-burn/mint link token. + // It is not used in new deployments, but still exists on some chains + // and has a distinct ABI from the new LinkToken. + // Corresponds to the ABI + // https://github.com/smartcontractkit/chainlink/blob/develop/core/gethwrappers/generated/link_token_interface/link_token_interface.go#L34 + StaticLinkToken deployment.ContractType = "StaticLinkToken" ) type MCMSWithTimelockConfig struct { 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..6dd1a00be3b 100644 --- a/deployment/common/view/v1_0/link_token.go +++ b/deployment/common/view/v1_0/link_token.go @@ -1,8 +1,11 @@ package v1_0 import ( + "fmt" "math/big" + "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink/deployment" commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/smartcontractkit/chainlink/deployment/common/view/types" @@ -11,22 +14,32 @@ import ( type LinkTokenView struct { types.ContractMetaData - Decimals uint8 `json:"decimals"` - Supply *big.Int `json:"supply"` + Decimals uint8 `json:"decimals"` + Supply *big.Int `json:"supply"` + Minters []common.Address `json:"minters"` + Burners []common.Address `json:"burners"` } func GenerateLinkTokenView(lt *link_token.LinkToken) (LinkTokenView, error) { owner, err := lt.Owner(nil) if err != nil { - return LinkTokenView{}, err + return LinkTokenView{}, fmt.Errorf("failed to get owner %s: %w", lt.Address(), err) } decimals, err := lt.Decimals(nil) if err != nil { - return LinkTokenView{}, err + return LinkTokenView{}, fmt.Errorf("failed to get decimals %s: %w", lt.Address(), err) } totalSupply, err := lt.TotalSupply(nil) if err != nil { - return LinkTokenView{}, err + return LinkTokenView{}, fmt.Errorf("failed to get total supply %s: %w", lt.Address(), err) + } + minters, err := lt.GetMinters(nil) + if err != nil { + return LinkTokenView{}, fmt.Errorf("failed to get minters %s: %w", lt.Address(), err) + } + burners, err := lt.GetBurners(nil) + if err != nil { + return LinkTokenView{}, fmt.Errorf("failed to get burners %s: %w", lt.Address(), err) } return LinkTokenView{ ContractMetaData: types.ContractMetaData{ @@ -39,5 +52,7 @@ func GenerateLinkTokenView(lt *link_token.LinkToken) (LinkTokenView, error) { }, Decimals: decimals, Supply: totalSupply, + Minters: minters, + Burners: burners, }, nil } diff --git a/deployment/common/view/v1_0/link_token_test.go b/deployment/common/view/v1_0/link_token_test.go new file mode 100644 index 00000000000..c83c0b3e3c2 --- /dev/null +++ b/deployment/common/view/v1_0/link_token_test.go @@ -0,0 +1,53 @@ +package v1_0 + +import ( + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zapcore" + + "github.com/smartcontractkit/chainlink/deployment/environment/memory" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/link_token" + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +func TestLinkTokenView(t *testing.T) { + e := memory.NewMemoryEnvironment(t, logger.TestLogger(t), zapcore.InfoLevel, memory.MemoryEnvironmentConfig{ + Chains: 1, + }) + chain := e.Chains[e.AllChainSelectors()[0]] + _, tx, lt, err := link_token.DeployLinkToken(chain.DeployerKey, chain.Client) + require.NoError(t, err) + _, err = chain.Confirm(tx) + require.NoError(t, err) + v, err := GenerateLinkTokenView(lt) + require.NoError(t, err) + + assert.Equal(t, v.Owner, chain.DeployerKey.From) + assert.Equal(t, v.TypeAndVersion, "LinkToken 1.0.0") + assert.Equal(t, v.Decimals, uint8(18)) + // Initially nothing minted and no minters/burners. + assert.Equal(t, v.Supply.String(), "0") + require.Len(t, v.Minters, 0) + require.Len(t, v.Burners, 0) + + // Add some minters + tx, err = lt.GrantMintAndBurnRoles(chain.DeployerKey, chain.DeployerKey.From) + require.NoError(t, err) + _, err = chain.Confirm(tx) + require.NoError(t, err) + tx, err = lt.Mint(chain.DeployerKey, chain.DeployerKey.From, big.NewInt(100)) + _, err = chain.Confirm(tx) + require.NoError(t, err) + + v, err = GenerateLinkTokenView(lt) + require.NoError(t, err) + + assert.Equal(t, v.Supply.String(), "100") + require.Len(t, v.Minters, 1) + require.Equal(t, v.Minters[0].String(), chain.DeployerKey.From.String()) + require.Len(t, v.Burners, 1) + require.Equal(t, v.Burners[0].String(), chain.DeployerKey.From.String()) +} diff --git a/deployment/common/view/v1_0/static_link_token.go b/deployment/common/view/v1_0/static_link_token.go new file mode 100644 index 00000000000..525f1a9f0c5 --- /dev/null +++ b/deployment/common/view/v1_0/static_link_token.go @@ -0,0 +1,40 @@ +package v1_0 + +import ( + "fmt" + "math/big" + + "github.com/smartcontractkit/chainlink/deployment" + commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" + "github.com/smartcontractkit/chainlink/deployment/common/view/types" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" +) + +type StaticLinkTokenView struct { + types.ContractMetaData + Decimals uint8 `json:"decimals"` + Supply *big.Int `json:"supply"` +} + +func GenerateStaticLinkTokenView(lt *link_token_interface.LinkToken) (StaticLinkTokenView, error) { + decimals, err := lt.Decimals(nil) + if err != nil { + return StaticLinkTokenView{}, fmt.Errorf("failed to get decimals %s: %w", lt.Address(), err) + } + totalSupply, err := lt.TotalSupply(nil) + if err != nil { + return StaticLinkTokenView{}, fmt.Errorf("failed to get total supply %s: %w", lt.Address(), err) + } + return StaticLinkTokenView{ + ContractMetaData: types.ContractMetaData{ + TypeAndVersion: deployment.TypeAndVersion{ + commontypes.StaticLinkToken, + deployment.Version1_0_0, + }.String(), + Address: lt.Address(), + // No owner. + }, + Decimals: decimals, + Supply: totalSupply, + }, nil +} diff --git a/deployment/common/view/v1_0/static_link_token_test.go b/deployment/common/view/v1_0/static_link_token_test.go new file mode 100644 index 00000000000..517efac9438 --- /dev/null +++ b/deployment/common/view/v1_0/static_link_token_test.go @@ -0,0 +1,32 @@ +package v1_0 + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zapcore" + + "github.com/smartcontractkit/chainlink/deployment/environment/memory" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +func TestStaticLinkTokenView(t *testing.T) { + e := memory.NewMemoryEnvironment(t, logger.TestLogger(t), zapcore.InfoLevel, memory.MemoryEnvironmentConfig{ + Chains: 1, + }) + chain := e.Chains[e.AllChainSelectors()[0]] + _, tx, lt, err := link_token_interface.DeployLinkToken(chain.DeployerKey, chain.Client) + require.NoError(t, err) + _, err = chain.Confirm(tx) + require.NoError(t, err) + v, err := GenerateStaticLinkTokenView(lt) + require.NoError(t, err) + + assert.Equal(t, v.Owner, common.HexToAddress("0x0")) // Ownerless + assert.Equal(t, v.TypeAndVersion, "StaticLinkToken 1.0.0") + assert.Equal(t, v.Decimals, uint8(18)) + assert.Equal(t, v.Supply.String(), "1000000000000000000000000000") +} diff --git a/deployment/environment.go b/deployment/environment.go index 2de16a32cab..3d120adbbf1 100644 --- a/deployment/environment.go +++ b/deployment/environment.go @@ -59,6 +59,24 @@ type Chain struct { 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 // including on and offchain components. It is intended to be // cross-family to enable a coherent view of a product deployed @@ -80,6 +98,7 @@ type Environment struct { NodeIDs []string Offchain OffchainClient GetContext func() context.Context + OCRSecrets OCRSecrets } func NewEnvironment( @@ -90,6 +109,7 @@ func NewEnvironment( nodeIDs []string, offchain OffchainClient, ctx func() context.Context, + secrets OCRSecrets, ) *Environment { return &Environment{ Name: name, @@ -99,6 +119,7 @@ func NewEnvironment( NodeIDs: nodeIDs, Offchain: offchain, GetContext: ctx, + OCRSecrets: secrets, } } @@ -147,7 +168,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/devenv/environment.go b/deployment/environment/devenv/environment.go index 094eba99adf..121caea43bb 100644 --- a/deployment/environment/devenv/environment.go +++ b/deployment/environment/devenv/environment.go @@ -52,5 +52,6 @@ func NewEnvironment(ctx func() context.Context, lggr logger.Logger, config Envir nodeIDs, offChain, ctx, + deployment.XXXGenerateTestOCRSecrets(), ), jd.don, nil } diff --git a/deployment/environment/memory/environment.go b/deployment/environment/memory/environment.go index e8f0c265101..d6c80a92a44 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,30 +67,30 @@ 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 } @@ -141,6 +142,7 @@ func NewMemoryEnvironmentFromChainsNodes( nodeIDs, // Note these have the p2p_ prefix. NewMemoryJobClient(nodes), ctx, + deployment.XXXGenerateTestOCRSecrets(), ) } @@ -160,5 +162,6 @@ func NewMemoryEnvironment(t *testing.T, lggr logger.Logger, logLevel zapcore.Lev nodeIDs, NewMemoryJobClient(nodes), func() context.Context { return tests.Context(t) }, + deployment.XXXGenerateTestOCRSecrets(), ) } diff --git a/deployment/go.mod b/deployment/go.mod index 7908b6a8048..b9a2fdb255f 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -23,7 +23,7 @@ require ( github.com/rs/zerolog v1.33.0 github.com/sethvargo/go-retry v0.2.4 github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86 - github.com/smartcontractkit/chain-selectors v1.0.31 + github.com/smartcontractkit/chain-selectors v1.0.34 github.com/smartcontractkit/chainlink-ccip v0.0.0-20241204015713-8956bb614e9e github.com/smartcontractkit/chainlink-common v0.3.1-0.20241206011233-b6684ee6508f github.com/smartcontractkit/chainlink-protos/job-distributor v0.6.0 @@ -415,7 +415,7 @@ require ( github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20241009055228-33d0c0bf38de // indirect github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20241009055228-33d0c0bf38de // indirect - github.com/smartcontractkit/wsrpc v0.8.2 // indirect + github.com/smartcontractkit/wsrpc v0.8.3 // indirect github.com/soheilhy/cmux v0.1.5 // indirect github.com/sony/gobreaker v0.5.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect diff --git a/deployment/go.sum b/deployment/go.sum index fec529e51c6..7513309dddf 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -1405,8 +1405,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86 h1:qQH6fZZe31nBAG6INHph3z5ysDTPptyu0TR9uoJ1+ok= github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86/go.mod h1:WtWOoVQQEHxRHL2hNmuRrvDfYfQG/CioFNoa9Rr2mBE= -github.com/smartcontractkit/chain-selectors v1.0.31 h1:oRHyK88KnsCh4OdU2hr0u70pm3KUgyMDyK0v0aOtUk4= -github.com/smartcontractkit/chain-selectors v1.0.31/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= +github.com/smartcontractkit/chain-selectors v1.0.34 h1:MJ17OGu8+jjl426pcKrJkCf3fePb3eCreuAnUA3RBj4= +github.com/smartcontractkit/chain-selectors v1.0.34/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= 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= @@ -1443,8 +1443,8 @@ github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20241009055228- github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20241009055228-33d0c0bf38de/go.mod h1:Sl2MF/Fp3fgJIVzhdGhmZZX2BlnM0oUUyBP4s4xYb6o= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20241009055228-33d0c0bf38de h1:66VQxXx3lvTaAZrMBkIcdH9VEjujUEvmBQdnyOJnkOc= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20241009055228-33d0c0bf38de/go.mod h1:NSc7hgOQbXG3DAwkOdWnZzLTZENXSwDJ7Va1nBp0YU0= -github.com/smartcontractkit/wsrpc v0.8.2 h1:XB/xcn/MMseHW+8JE8+a/rceA86ck7Ur6cEa9LiUC8M= -github.com/smartcontractkit/wsrpc v0.8.2/go.mod h1:2u/wfnhl5R4RlSXseN4n6HHIWk8w1Am3AT6gWftQbNg= +github.com/smartcontractkit/wsrpc v0.8.3 h1:9tDf7Ut61g36RJIyxV9iI73SqoOMasKPfURV9oMLrPg= +github.com/smartcontractkit/wsrpc v0.8.3/go.mod h1:2u/wfnhl5R4RlSXseN4n6HHIWk8w1Am3AT6gWftQbNg= github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 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/changeset/accept_ownership_test.go b/deployment/keystone/changeset/accept_ownership_test.go index ec65ef920ac..f205adda496 100644 --- a/deployment/keystone/changeset/accept_ownership_test.go +++ b/deployment/keystone/changeset/accept_ownership_test.go @@ -54,7 +54,7 @@ func TestAcceptAllOwnership(t *testing.T) { require.NoError(t, err) addrs, err := env.ExistingAddresses.AddressesForChain(registrySel) require.NoError(t, err) - timelock, err := commonchangeset.LoadMCMSWithTimelockState(env.Chains[registrySel], addrs) + timelock, err := commonchangeset.MaybeLoadMCMSWithTimelockState(env.Chains[registrySel], addrs) require.NoError(t, err) _, err = commonchangeset.ApplyChangesets(t, env, map[uint64]*owner_helpers.RBACTimelock{ diff --git a/deployment/keystone/changeset/configure_contracts.go b/deployment/keystone/changeset/configure_contracts.go index 17635a42ed1..3a92782e12b 100644 --- a/deployment/keystone/changeset/configure_contracts.go +++ b/deployment/keystone/changeset/configure_contracts.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/deployment" kslib "github.com/smartcontractkit/chainlink/deployment/keystone" ) @@ -14,7 +15,7 @@ var _ deployment.ChangeSet[InitialContractsCfg] = ConfigureInitialContractsChang type InitialContractsCfg struct { RegistryChainSel uint64 Dons []kslib.DonCapabilities - OCR3Config *kslib.OracleConfigWithSecrets + OCR3Config *kslib.OracleConfig } func ConfigureInitialContractsChangeset(e deployment.Environment, cfg InitialContractsCfg) (deployment.ChangesetOutput, error) { diff --git a/deployment/keystone/changeset/deploy_forwarder.go b/deployment/keystone/changeset/deploy_forwarder.go index 5207d99aacd..cf116decd54 100644 --- a/deployment/keystone/changeset/deploy_forwarder.go +++ b/deployment/keystone/changeset/deploy_forwarder.go @@ -11,7 +11,8 @@ var _ deployment.ChangeSet[uint64] = DeployForwarder // DeployForwarder deploys the KeystoneForwarder contract to all chains in the environment // callers must merge the output addressbook with the existing one -func DeployForwarder(env deployment.Environment, registryChainSel uint64) (deployment.ChangesetOutput, error) { +// TODO: add selectors to deploy only to specific chains +func DeployForwarder(env deployment.Environment, _ uint64) (deployment.ChangesetOutput, error) { lggr := env.Logger ab := deployment.NewMemoryAddressBook() for _, chain := range env.Chains { @@ -25,3 +26,42 @@ func DeployForwarder(env deployment.Environment, registryChainSel uint64) (deplo return deployment.ChangesetOutput{AddressBook: ab}, nil } + +var _ deployment.ChangeSet[ConfigureForwardContractsRequest] = ConfigureForwardContracts + +type ConfigureForwardContractsRequest struct { + WFDonName string + // workflow don node ids in the offchain client. Used to fetch and derive the signer keys + WFNodeIDs []string + RegistryChainSel uint64 + + UseMCMS bool +} + +func (r ConfigureForwardContractsRequest) Validate() error { + if len(r.WFNodeIDs) == 0 { + return fmt.Errorf("WFNodeIDs must not be empty") + } + return nil +} + +func ConfigureForwardContracts(env deployment.Environment, req ConfigureForwardContractsRequest) (deployment.ChangesetOutput, error) { + wfDon, err := kslib.NewRegisteredDon(env, kslib.RegisteredDonConfig{ + NodeIDs: req.WFNodeIDs, + Name: req.WFDonName, + RegistryChainSel: req.RegistryChainSel, + }) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to create registered don: %w", err) + } + r, err := kslib.ConfigureForwardContracts(&env, kslib.ConfigureForwarderContractsRequest{ + Dons: []kslib.RegisteredDon{*wfDon}, + UseMCMS: req.UseMCMS, + }) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to configure forward contracts: %w", err) + } + return deployment.ChangesetOutput{ + Proposals: r.Proposals, + }, nil +} diff --git a/deployment/keystone/changeset/deploy_forwarder_test.go b/deployment/keystone/changeset/deploy_forwarder_test.go index b6d8ec8f753..32a53f1cf08 100644 --- a/deployment/keystone/changeset/deploy_forwarder_test.go +++ b/deployment/keystone/changeset/deploy_forwarder_test.go @@ -1,14 +1,17 @@ package changeset_test import ( + "fmt" "testing" "go.uber.org/zap/zapcore" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/deployment" + commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" "github.com/smartcontractkit/chainlink/deployment/environment/memory" "github.com/smartcontractkit/chainlink/deployment/keystone/changeset" ) @@ -45,3 +48,90 @@ func TestDeployForwarder(t *testing.T) { require.Len(t, oaddrs, 1) }) } + +func TestConfigureForwarders(t *testing.T) { + t.Parallel() + + t.Run("no mcms ", func(t *testing.T) { + for _, nChains := range []int{1, 3} { + name := fmt.Sprintf("nChains=%d", nChains) + t.Run(name, func(t *testing.T) { + te := SetupTestEnv(t, TestConfig{ + WFDonConfig: DonConfig{N: 4}, + AssetDonConfig: DonConfig{N: 4}, + WriterDonConfig: DonConfig{N: 4}, + NumChains: nChains, + }) + + var wfNodes []string + for id, _ := range te.WFNodes { + wfNodes = append(wfNodes, id) + } + + cfg := changeset.ConfigureForwardContractsRequest{ + WFDonName: "test-wf-don", + WFNodeIDs: wfNodes, + RegistryChainSel: te.RegistrySelector, + } + csOut, err := changeset.ConfigureForwardContracts(te.Env, cfg) + require.NoError(t, err) + require.Nil(t, csOut.AddressBook) + require.Len(t, csOut.Proposals, 0) + // check that forwarder + // TODO set up a listener to check that the forwarder is configured + contractSet := te.ContractSets() + for selector := range te.Env.Chains { + cs, ok := contractSet[selector] + require.True(t, ok) + require.NotNil(t, cs.Forwarder) + } + }) + } + }) + + t.Run("with mcms", func(t *testing.T) { + for _, nChains := range []int{1, 3} { + name := fmt.Sprintf("nChains=%d", nChains) + t.Run(name, func(t *testing.T) { + te := SetupTestEnv(t, TestConfig{ + WFDonConfig: DonConfig{N: 4}, + AssetDonConfig: DonConfig{N: 4}, + WriterDonConfig: DonConfig{N: 4}, + NumChains: nChains, + UseMCMS: true, + }) + + var wfNodes []string + for id, _ := range te.WFNodes { + wfNodes = append(wfNodes, id) + } + + cfg := changeset.ConfigureForwardContractsRequest{ + WFDonName: "test-wf-don", + WFNodeIDs: wfNodes, + RegistryChainSel: te.RegistrySelector, + UseMCMS: true, + } + csOut, err := changeset.ConfigureForwardContracts(te.Env, cfg) + require.NoError(t, err) + require.Len(t, csOut.Proposals, nChains) + require.Nil(t, csOut.AddressBook) + + timelocks := make(map[uint64]*gethwrappers.RBACTimelock) + for selector, contractSet := range te.ContractSets() { + require.NotNil(t, contractSet.Timelock) + timelocks[selector] = contractSet.Timelock + } + _, err = commonchangeset.ApplyChangesets(t, te.Env, timelocks, []commonchangeset.ChangesetApplication{ + { + Changeset: commonchangeset.WrapChangeSet(changeset.ConfigureForwardContracts), + Config: cfg, + }, + }) + require.NoError(t, err) + + }) + } + }) + +} diff --git a/deployment/keystone/changeset/deploy_ocr3.go b/deployment/keystone/changeset/deploy_ocr3.go index fdf51e644be..0ce3d02844b 100644 --- a/deployment/keystone/changeset/deploy_ocr3.go +++ b/deployment/keystone/changeset/deploy_ocr3.go @@ -6,6 +6,7 @@ import ( "io" "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" + "github.com/smartcontractkit/chainlink/deployment" kslib "github.com/smartcontractkit/chainlink/deployment/keystone" ) @@ -33,7 +34,7 @@ var _ deployment.ChangeSet[ConfigureOCR3Config] = ConfigureOCR3Contract type ConfigureOCR3Config struct { ChainSel uint64 NodeIDs []string - OCR3Config *kslib.OracleConfigWithSecrets + OCR3Config *kslib.OracleConfig DryRun bool WriteGeneratedConfig io.Writer // if not nil, write the generated config to this writer as JSON [OCR2OracleConfig] diff --git a/deployment/keystone/changeset/deploy_ocr3_test.go b/deployment/keystone/changeset/deploy_ocr3_test.go index 0d49af68823..0e5fea6b7c6 100644 --- a/deployment/keystone/changeset/deploy_ocr3_test.go +++ b/deployment/keystone/changeset/deploy_ocr3_test.go @@ -12,7 +12,7 @@ import ( "github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers" "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink/deployment" + commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" "github.com/smartcontractkit/chainlink/deployment/environment/memory" kslib "github.com/smartcontractkit/chainlink/deployment/keystone" @@ -48,12 +48,9 @@ func TestConfigureOCR3(t *testing.T) { t.Parallel() lggr := logger.Test(t) - c := kslib.OracleConfigWithSecrets{ - OracleConfig: kslib.OracleConfig{ - MaxFaultyOracles: 1, - DeltaProgressMillis: 12345, - }, - OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), + c := kslib.OracleConfig{ + MaxFaultyOracles: 1, + DeltaProgressMillis: 12345, } t.Run("no mcms", func(t *testing.T) { diff --git a/deployment/keystone/changeset/helpers_test.go b/deployment/keystone/changeset/helpers_test.go index 85e69507009..faf112867ce 100644 --- a/deployment/keystone/changeset/helpers_test.go +++ b/deployment/keystone/changeset/helpers_test.go @@ -7,16 +7,19 @@ import ( "encoding/hex" "errors" "fmt" + "math" "math/big" "sort" - - "math" "testing" "github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/node" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zapcore" + "golang.org/x/exp/maps" + "github.com/smartcontractkit/chainlink/deployment" commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" @@ -26,9 +29,6 @@ import ( kschangeset "github.com/smartcontractkit/chainlink/deployment/keystone/changeset" kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" - "github.com/stretchr/testify/require" - "go.uber.org/zap/zapcore" - "golang.org/x/exp/maps" ) func TestSetupTestEnv(t *testing.T) { @@ -96,6 +96,7 @@ func (c TestConfig) Validate() error { } type TestEnv struct { + t *testing.T Env deployment.Environment RegistrySelector uint64 @@ -104,6 +105,15 @@ type TestEnv struct { AssetNodes map[string]memory.Node } +func (te TestEnv) ContractSets() map[uint64]kslib.ContractSet { + r, err := kslib.GetContractSets(te.Env.Logger, &kslib.GetContractSetsRequest{ + Chains: te.Env.Chains, + AddressBook: te.Env.ExistingAddresses, + }) + require.NoError(te.t, err) + return r.ContractSets +} + // SetupTestEnv sets up a keystone test environment with the given configuration func SetupTestEnv(t *testing.T, c TestConfig) TestEnv { require.NoError(t, c.Validate()) @@ -205,11 +215,8 @@ func SetupTestEnv(t *testing.T, c TestConfig) TestEnv { err = env.ExistingAddresses.Merge(e.ExistingAddresses) require.NoError(t, err) - var ocr3Config = keystone.OracleConfigWithSecrets{ - OracleConfig: keystone.OracleConfig{ - MaxFaultyOracles: len(wfNodes) / 3, - }, - OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), + var ocr3Config = keystone.OracleConfig{ + MaxFaultyOracles: len(wfNodes) / 3, } var allDons = []keystone.DonCapabilities{wfDon, cwDon, assetDon} @@ -250,57 +257,51 @@ func SetupTestEnv(t *testing.T, c TestConfig) TestEnv { if c.UseMCMS { // TODO: mcms on all the chains, currently only on the registry chain. need to fix this for forwarders - t.Logf("Enabling MCMS registry chain %d", registryChainSel) // deploy, configure and xfer ownership of MCMS + timelockCfgs := make(map[uint64]commontypes.MCMSWithTimelockConfig) + for sel := range env.Chains { + t.Logf("Enabling MCMS on chain %d", sel) + timelockCfgs[sel] = commontypes.MCMSWithTimelockConfig{ + Canceller: commonchangeset.SingleGroupMCMS(t), + Bypasser: commonchangeset.SingleGroupMCMS(t), + Proposer: commonchangeset.SingleGroupMCMS(t), + TimelockExecutors: env.AllDeployerKeys(), + TimelockMinDelay: big.NewInt(0), + } + } env, err = commonchangeset.ApplyChangesets(t, env, nil, []commonchangeset.ChangesetApplication{ { Changeset: commonchangeset.WrapChangeSet(commonchangeset.DeployMCMSWithTimelock), - Config: map[uint64]commontypes.MCMSWithTimelockConfig{ - registryChainSel: { - Canceller: commonchangeset.SingleGroupMCMS(t), - Bypasser: commonchangeset.SingleGroupMCMS(t), - Proposer: commonchangeset.SingleGroupMCMS(t), - TimelockExecutors: env.AllDeployerKeys(), - TimelockMinDelay: big.NewInt(0), - }, - }, + Config: timelockCfgs, }, }) require.NoError(t, err) // extract the MCMS address r, err := kslib.GetContractSets(lggr, &kslib.GetContractSetsRequest{ - Chains: map[uint64]deployment.Chain{ - registryChainSel: env.Chains[registryChainSel], - }, + Chains: env.Chains, AddressBook: env.ExistingAddresses, }) require.NoError(t, err) - mcms := r.ContractSets[registryChainSel].MCMSWithTimelockState - require.NotNil(t, mcms) - // transfer ownership of all contracts to the MCMS - env, err = commonchangeset.ApplyChangesets(t, env, map[uint64]*gethwrappers.RBACTimelock{registryChainSel: mcms.Timelock}, []commonchangeset.ChangesetApplication{ - { - Changeset: commonchangeset.WrapChangeSet(kschangeset.AcceptAllOwnershipsProposal), - Config: &kschangeset.AcceptAllOwnershipRequest{ - ChainSelector: registryChainSel, - MinDelay: 0, + for sel := range env.Chains { + mcms := r.ContractSets[sel].MCMSWithTimelockState + require.NotNil(t, mcms, "MCMS not found on chain %d", sel) + require.NoError(t, mcms.Validate()) + + // transfer ownership of all contracts to the MCMS + env, err = commonchangeset.ApplyChangesets(t, env, map[uint64]*gethwrappers.RBACTimelock{sel: mcms.Timelock}, []commonchangeset.ChangesetApplication{ + { + Changeset: commonchangeset.WrapChangeSet(kschangeset.AcceptAllOwnershipsProposal), + Config: &kschangeset.AcceptAllOwnershipRequest{ + ChainSelector: sel, + MinDelay: 0, + }, }, - }, - }) - require.NoError(t, err) - // ensure the MCMS is deployed - req = &keystone.GetContractSetsRequest{ - Chains: env.Chains, - AddressBook: env.ExistingAddresses, + }) + require.NoError(t, err) } - contractSetsResp, err = keystone.GetContractSets(lggr, req) - require.NoError(t, err) - require.Len(t, contractSetsResp.ContractSets, len(env.Chains)) - // check the mcms contract on registry chain - gotMCMS := contractSetsResp.ContractSets[registryChainSel].MCMSWithTimelockState - require.NoError(t, gotMCMS.Validate()) } return TestEnv{ + t: t, Env: env, RegistrySelector: registryChainSel, WFNodes: wfNodes, diff --git a/deployment/keystone/deploy.go b/deployment/keystone/deploy.go index 0370cc54ba9..9bd291e9c1e 100644 --- a/deployment/keystone/deploy.go +++ b/deployment/keystone/deploy.go @@ -7,18 +7,23 @@ import ( "encoding/hex" "errors" "fmt" + "math/big" "sort" "strconv" "strings" "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/rpc" "golang.org/x/exp/maps" + "github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers" + "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/mcms" "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/durationpb" @@ -39,8 +44,8 @@ type ConfigureContractsRequest struct { RegistryChainSel uint64 Env *deployment.Environment - Dons []DonCapabilities // externally sourced based on the environment - OCR3Config *OracleConfigWithSecrets // TODO: probably should be a map of don to config; but currently we only have one wf don therefore one config + Dons []DonCapabilities // externally sourced based on the environment + OCR3Config *OracleConfig // TODO: probably should be a map of don to config; but currently we only have one wf don therefore one config // TODO rm this option; unused DoContractDeploy bool // if false, the contracts are assumed to be deployed and the address book is used @@ -77,22 +82,7 @@ func ConfigureContracts(ctx context.Context, lggr logger.Logger, req ConfigureCo return nil, fmt.Errorf("invalid request: %w", err) } - addrBook := req.Env.ExistingAddresses - // TODO: KS-rm_deploy_opt remove this option; it's not used - if req.DoContractDeploy { - contractDeployCS, err := DeployContracts(req.Env, req.RegistryChainSel) - if err != nil { - return nil, fmt.Errorf("failed to deploy contracts: %w", err) - } - addrBook = contractDeployCS.AddressBook - } else { - lggr.Debug("skipping contract deployment") - } - if addrBook == nil { - return nil, errors.New("address book is nil") - } - - cfgRegistryResp, err := ConfigureRegistry(ctx, lggr, req, addrBook) + cfgRegistryResp, err := ConfigureRegistry(ctx, lggr, req, req.Env.ExistingAddresses) if err != nil { return nil, fmt.Errorf("failed to configure registry: %w", err) } @@ -107,21 +97,22 @@ func ConfigureContracts(ctx context.Context, lggr logger.Logger, req ConfigureCo if err != nil { return nil, fmt.Errorf("failed to assimilate registry to Dons: %w", err) } - err = ConfigureForwardContracts(req.Env, dons, addrBook) + // ignore response because we are not using mcms here and therefore no proposals are returned + _, err = ConfigureForwardContracts(req.Env, ConfigureForwarderContractsRequest{ + Dons: dons, + }) if err != nil { return nil, fmt.Errorf("failed to configure forwarder contracts: %w", err) } - err = ConfigureOCR3Contract(req.Env, req.RegistryChainSel, dons, addrBook, req.OCR3Config) + err = ConfigureOCR3Contract(req.Env, req.RegistryChainSel, dons, req.OCR3Config) if err != nil { return nil, fmt.Errorf("failed to configure OCR3 contract: %w", err) } return &ConfigureContractsResponse{ - Changeset: &deployment.ChangesetOutput{ - AddressBook: addrBook, - }, - DonInfos: cfgRegistryResp.DonInfos, + Changeset: &deployment.ChangesetOutput{}, // no new addresses, proposals etc + DonInfos: cfgRegistryResp.DonInfos, }, nil } @@ -306,47 +297,14 @@ func ConfigureRegistry(ctx context.Context, lggr logger.Logger, req ConfigureCon lggr.Infow("registered DONs", "dons", len(donsResp.DonInfos)) return &ConfigureContractsResponse{ - Changeset: &deployment.ChangesetOutput{ - AddressBook: addrBook, - }, - DonInfos: donsResp.DonInfos, + Changeset: &deployment.ChangesetOutput{}, // no new addresses, proposals etc + DonInfos: donsResp.DonInfos, }, nil } -// ConfigureForwardContracts configures the forwarder contracts on all chains for the given DONS -// the address book is required to contain the an address of the deployed forwarder contract for every chain in the environment -func ConfigureForwardContracts(env *deployment.Environment, dons []RegisteredDon, addrBook deployment.AddressBook) error { - contractSetsResp, err := GetContractSets(env.Logger, &GetContractSetsRequest{ - Chains: env.Chains, - AddressBook: addrBook, - }) - if err != nil { - return fmt.Errorf("failed to get contract sets: %w", err) - } - - // configure forwarders on all chains - for _, chain := range env.Chains { - // get the forwarder contract for the chain - contracts, ok := contractSetsResp.ContractSets[chain.Selector] - if !ok { - return fmt.Errorf("failed to get contract set for chain %d", chain.Selector) - } - fwrd := contracts.Forwarder - if fwrd == nil { - return fmt.Errorf("no forwarder contract found for chain %d", chain.Selector) - } - - err := configureForwarder(env.Logger, chain, fwrd, dons) - if err != nil { - return fmt.Errorf("failed to configure forwarder for chain selector %d: %w", chain.Selector, err) - } - } - return nil -} - // Depreciated: use changeset.ConfigureOCR3Contract instead // ocr3 contract on the registry chain for the wf dons -func ConfigureOCR3Contract(env *deployment.Environment, chainSel uint64, dons []RegisteredDon, addrBook deployment.AddressBook, cfg *OracleConfigWithSecrets) error { +func ConfigureOCR3Contract(env *deployment.Environment, chainSel uint64, dons []RegisteredDon, cfg *OracleConfig) error { registryChain, ok := env.Chains[chainSel] if !ok { return fmt.Errorf("chain %d not found in environment", chainSel) @@ -354,7 +312,7 @@ func ConfigureOCR3Contract(env *deployment.Environment, chainSel uint64, dons [] contractSetsResp, err := GetContractSets(env.Logger, &GetContractSetsRequest{ Chains: env.Chains, - AddressBook: addrBook, + AddressBook: env.ExistingAddresses, }) if err != nil { return fmt.Errorf("failed to get contract sets: %w", err) @@ -380,6 +338,7 @@ func ConfigureOCR3Contract(env *deployment.Environment, chainSel uint64, dons [] contract: contract, nodes: don.Nodes, contractSet: &contracts, + ocrSecrets: env.OCRSecrets, }) if err != nil { return fmt.Errorf("failed to configure OCR3 contract for don %s: %w", don.Name, err) @@ -396,7 +355,7 @@ type ConfigureOCR3Resp struct { type ConfigureOCR3Config struct { ChainSel uint64 NodeIDs []string - OCR3Config *OracleConfigWithSecrets + OCR3Config *OracleConfig DryRun bool UseMCMS bool @@ -440,6 +399,7 @@ func ConfigureOCR3ContractFromJD(env *deployment.Environment, cfg ConfigureOCR3C dryRun: cfg.DryRun, contractSet: &contracts, useMCMS: cfg.UseMCMS, + ocrSecrets: env.OCRSecrets, }) if err != nil { return nil, err @@ -978,27 +938,67 @@ func containsAllDONs(donInfos []kcr.CapabilitiesRegistryDONInfo, p2pIdsToDon map // configureForwarder sets the config for the forwarder contract on the chain for all Dons that accept workflows // dons that don't accept workflows are not registered with the forwarder -func configureForwarder(lggr logger.Logger, chain deployment.Chain, fwdr *kf.KeystoneForwarder, dons []RegisteredDon) error { - if fwdr == nil { - return errors.New("nil forwarder contract") - } +func configureForwarder(lggr logger.Logger, chain deployment.Chain, contractSet ContractSet, dons []RegisteredDon, useMCMS bool) ([]timelock.MCMSWithTimelockProposal, error) { + if contractSet.Forwarder == nil { + return nil, errors.New("nil forwarder contract") + } + var ( + fwdr = contractSet.Forwarder + proposals []timelock.MCMSWithTimelockProposal + ) for _, dn := range dons { if !dn.Info.AcceptsWorkflows { continue } ver := dn.Info.ConfigCount // note config count on the don info is the version on the forwarder - signers := dn.signers(chainsel.FamilyEVM) - tx, err := fwdr.SetConfig(chain.DeployerKey, dn.Info.Id, ver, dn.Info.F, signers) - if err != nil { - err = DecodeErr(kf.KeystoneForwarderABI, err) - return fmt.Errorf("failed to call SetConfig for forwarder %s on chain %d: %w", fwdr.Address().String(), chain.Selector, err) + signers := dn.Signers(chainsel.FamilyEVM) + txOpts := chain.DeployerKey + if useMCMS { + txOpts = deployment.SimTransactOpts() } - _, err = chain.Confirm(tx) + tx, err := fwdr.SetConfig(txOpts, dn.Info.Id, ver, dn.Info.F, signers) if err != nil { err = DecodeErr(kf.KeystoneForwarderABI, err) - return fmt.Errorf("failed to confirm SetConfig for forwarder %s: %w", fwdr.Address().String(), err) + return nil, fmt.Errorf("failed to call SetConfig for forwarder %s on chain %d: %w", fwdr.Address().String(), chain.Selector, err) + } + if !useMCMS { + _, err = chain.Confirm(tx) + if err != nil { + err = DecodeErr(kf.KeystoneForwarderABI, err) + return nil, fmt.Errorf("failed to confirm SetConfig for forwarder %s: %w", fwdr.Address().String(), err) + } + } else { + // create the mcms proposals + ops := timelock.BatchChainOperation{ + ChainIdentifier: mcms.ChainIdentifier(chain.Selector), + Batch: []mcms.Operation{ + { + To: fwdr.Address(), + Data: tx.Data(), + Value: big.NewInt(0), + }, + }, + } + timelocksPerChain := map[uint64]common.Address{ + chain.Selector: contractSet.Timelock.Address(), + } + proposerMCMSes := map[uint64]*gethwrappers.ManyChainMultiSig{ + chain.Selector: contractSet.ProposerMcm, + } + + proposal, err := proposalutils.BuildProposalFromBatches( + timelocksPerChain, + proposerMCMSes, + []timelock.BatchChainOperation{ops}, + "proposal to set forward config", + 0, + ) + if err != nil { + return nil, fmt.Errorf("failed to build proposal: %w", err) + } + proposals = append(proposals, *proposal) } lggr.Debugw("configured forwarder", "forwarder", fwdr.Address().String(), "donId", dn.Info.Id, "version", ver, "f", dn.Info.F, "signers", signers) } - return nil + return proposals, nil } diff --git a/deployment/keystone/deploy_test.go b/deployment/keystone/deploy_test.go index fd59f3007fd..eb167ed60fb 100644 --- a/deployment/keystone/deploy_test.go +++ b/deployment/keystone/deploy_test.go @@ -107,6 +107,7 @@ func TestDeployCLO(t *testing.T) { Offchain: clo.NewJobClient(lggr, clo.JobClientConfig{Nops: allNops}), Chains: allChains, Logger: lggr, + OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), } // assume that all the nodes in the provided input nops are part of the don for _, nop := range allNops { @@ -119,11 +120,8 @@ func TestDeployCLO(t *testing.T) { registryChainSel, err := chainsel.SelectorFromChainId(11155111) require.NoError(t, err) - var ocr3Config = keystone.OracleConfigWithSecrets{ - OracleConfig: keystone.OracleConfig{ - MaxFaultyOracles: len(wfNops) / 3, - }, - OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), + var ocr3Config = keystone.OracleConfig{ + MaxFaultyOracles: len(wfNops) / 3, } ctx := tests.Context(t) @@ -142,10 +140,7 @@ func TestDeployCLO(t *testing.T) { } deployResp, err := keystone.ConfigureContracts(ctx, lggr, deployReq) require.NoError(t, err) - ad := deployResp.Changeset.AddressBook - addrs, err := ad.Addresses() - require.NoError(t, err) - lggr.Infow("Deployed Keystone contracts", "address book", addrs) + ad := env.ExistingAddresses // all contracts on home chain homeChainAddrs, err := ad.AddressesForChain(registryChainSel) diff --git a/deployment/keystone/forwarder_deployer.go b/deployment/keystone/forwarder_deployer.go index cf29b20c693..d7cfa7991f4 100644 --- a/deployment/keystone/forwarder_deployer.go +++ b/deployment/keystone/forwarder_deployer.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/deployment" @@ -56,3 +57,43 @@ func (c *KeystoneForwarderDeployer) deploy(req DeployRequest) (*DeployResponse, c.contract = forwarder return resp, nil } + +type ConfigureForwarderContractsRequest struct { + Dons []RegisteredDon + + UseMCMS bool +} +type ConfigureForwarderContractsResponse struct { + Proposals []timelock.MCMSWithTimelockProposal +} + +// Depreciated: use [changeset.ConfigureForwarders] instead +// ConfigureForwardContracts configures the forwarder contracts on all chains for the given DONS +// the address book is required to contain the an address of the deployed forwarder contract for every chain in the environment +func ConfigureForwardContracts(env *deployment.Environment, req ConfigureForwarderContractsRequest) (*ConfigureForwarderContractsResponse, error) { + contractSetsResp, err := GetContractSets(env.Logger, &GetContractSetsRequest{ + Chains: env.Chains, + AddressBook: env.ExistingAddresses, + }) + if err != nil { + return nil, fmt.Errorf("failed to get contract sets: %w", err) + } + + var allProposals []timelock.MCMSWithTimelockProposal + // configure forwarders on all chains + for _, chain := range env.Chains { + // get the forwarder contract for the chain + contracts, ok := contractSetsResp.ContractSets[chain.Selector] + if !ok { + return nil, fmt.Errorf("failed to get contract set for chain %d", chain.Selector) + } + proposals, err := configureForwarder(env.Logger, chain, contracts, req.Dons, req.UseMCMS) + if err != nil { + return nil, fmt.Errorf("failed to configure forwarder for chain selector %d: %w", chain.Selector, err) + } + allProposals = append(allProposals, proposals...) + } + return &ConfigureForwarderContractsResponse{ + Proposals: allProposals, + }, nil +} diff --git a/deployment/keystone/ocr3config.go b/deployment/keystone/ocr3config.go index c4f8714b113..ccd738636ed 100644 --- a/deployment/keystone/ocr3config.go +++ b/deployment/keystone/ocr3config.go @@ -21,6 +21,7 @@ import ( "github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers" "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/mcms" "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" + "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" kocr3 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/ocr3_capability" @@ -30,12 +31,9 @@ import ( ) type TopLevelConfigSource struct { - OracleConfig OracleConfigWithSecrets -} -type OracleConfigWithSecrets struct { - OracleConfig - deployment.OCRSecrets `json:"-" toml:"-"` // don't spill secrets + OracleConfig OracleConfig } + type OracleConfig struct { MaxQueryLengthBytes uint32 MaxObservationLengthBytes uint32 @@ -151,10 +149,10 @@ func (c *OCR2OracleConfig) UnmarshalJSON(data []byte) error { return nil } -func GenerateOCR3Config(cfg OracleConfigWithSecrets, nca []NodeKeys) (OCR2OracleConfig, error) { +func GenerateOCR3Config(cfg OracleConfig, nca []NodeKeys, secrets deployment.OCRSecrets) (OCR2OracleConfig, error) { onchainPubKeys := [][]byte{} allPubKeys := map[string]any{} - if cfg.OCRSecrets.IsEmpty() { + if secrets.IsEmpty() { return OCR2OracleConfig{}, errors.New("OCRSecrets is required") } for _, n := range nca { @@ -236,8 +234,8 @@ func GenerateOCR3Config(cfg OracleConfigWithSecrets, nca []NodeKeys) (OCR2Oracle } signers, transmitters, f, onchainConfig, offchainConfigVersion, offchainConfig, err := ocr3confighelper.ContractSetConfigArgsDeterministic( - cfg.EphemeralSk, - cfg.SharedSecret, + secrets.EphemeralSk, + secrets.SharedSecret, time.Duration(cfg.DeltaProgressMillis)*time.Millisecond, time.Duration(cfg.DeltaResendMillis)*time.Millisecond, time.Duration(cfg.DeltaInitialMillis)*time.Millisecond, @@ -284,11 +282,12 @@ func GenerateOCR3Config(cfg OracleConfigWithSecrets, nca []NodeKeys) (OCR2Oracle } type configureOCR3Request struct { - cfg *OracleConfigWithSecrets - chain deployment.Chain - contract *kocr3.OCR3Capability - nodes []deployment.Node - dryRun bool + cfg *OracleConfig + chain deployment.Chain + contract *kocr3.OCR3Capability + nodes []deployment.Node + dryRun bool + ocrSecrets deployment.OCRSecrets useMCMS bool contractSet *ContractSet @@ -296,7 +295,7 @@ type configureOCR3Request struct { func (r configureOCR3Request) generateOCR3Config() (OCR2OracleConfig, error) { nks := makeNodeKeysSlice(r.nodes, r.chain.Selector) - return GenerateOCR3Config(*r.cfg, nks) + return GenerateOCR3Config(*r.cfg, nks, r.ocrSecrets) } type configureOCR3Response struct { diff --git a/deployment/keystone/ocr3config_test.go b/deployment/keystone/ocr3config_test.go index 4046787724a..55fa16af68c 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,13 +84,13 @@ 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()}, + cfg: &cfg, nodes: nodes, chain: deployment.Chain{ Selector: chain_selectors.ETHEREUM_TESTNET_SEPOLIA.Selector, }, + ocrSecrets: deployment.XXXGenerateTestOCRSecrets(), } got, err := r.generateOCR3Config() require.NoError(t, err) diff --git a/deployment/keystone/state.go b/deployment/keystone/state.go index 1e6ffdd895f..cbf449c7f31 100644 --- a/deployment/keystone/state.go +++ b/deployment/keystone/state.go @@ -78,7 +78,7 @@ func GetContractSets(lggr logger.Logger, req *GetContractSetsRequest) (*GetContr func loadContractSet(lggr logger.Logger, chain deployment.Chain, addresses map[string]deployment.TypeAndVersion) (*ContractSet, error) { var out ContractSet - mcmsWithTimelock, err := commonchangeset.LoadMCMSWithTimelockState(chain, addresses) + mcmsWithTimelock, err := commonchangeset.MaybeLoadMCMSWithTimelockState(chain, addresses) if err != nil { return nil, fmt.Errorf("failed to load mcms contract: %w", err) } diff --git a/deployment/keystone/types.go b/deployment/keystone/types.go index 52620a52a0e..d406487043c 100644 --- a/deployment/keystone/types.go +++ b/deployment/keystone/types.go @@ -230,7 +230,51 @@ type RegisteredDon struct { Nodes []deployment.Node } -func (d RegisteredDon) signers(chainFamily string) []common.Address { +type RegisteredDonConfig struct { + Name string + NodeIDs []string // ids in the offchain client + RegistryChainSel uint64 +} + +func NewRegisteredDon(env deployment.Environment, cfg RegisteredDonConfig) (*RegisteredDon, error) { + // load the don info from the capabilities registry + r, err := GetContractSets(env.Logger, &GetContractSetsRequest{ + Chains: env.Chains, + AddressBook: env.ExistingAddresses, + }) + if err != nil { + return nil, fmt.Errorf("failed to get contract sets: %w", err) + } + capReg := r.ContractSets[cfg.RegistryChainSel].CapabilitiesRegistry + + di, err := capReg.GetDONs(nil) + if err != nil { + return nil, fmt.Errorf("failed to get dons: %w", err) + } + // load the nodes from the offchain client + nodes, err := deployment.NodeInfo(cfg.NodeIDs, env.Offchain) + if err != nil { + return nil, fmt.Errorf("failed to get node info: %w", err) + } + want := sortedHash(nodes.PeerIDs()) + var don *kcr.CapabilitiesRegistryDONInfo + for i, d := range di { + got := sortedHash(d.NodeP2PIds) + if got == want { + don = &di[i] + } + } + if don == nil { + return nil, fmt.Errorf("don not found in registry") + } + return &RegisteredDon{ + Name: cfg.Name, + Info: *don, + Nodes: nodes, + }, nil +} + +func (d RegisteredDon) Signers(chainFamily string) []common.Address { sort.Slice(d.Nodes, func(i, j int) bool { return d.Nodes[i].PeerID.String() < d.Nodes[j].PeerID.String() }) diff --git a/deployment/multiclient.go b/deployment/multiclient.go index dcda07ebb0b..f1ac2f3c310 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,15 @@ 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) } + id, err := client.ChainID(context.Background()) + if err != nil { + return nil, fmt.Errorf("failed to get chain id: %w", err) + } + details, err := chainselectors.GetChainDetailsByChainIDAndFamily(id.String(), chainselectors.FamilyEVM) + if err != nil { + return nil, fmt.Errorf("failed to lookup chain details %w", err) + } + mc.chainName = details.ChainName clients = append(clients, client) } mc.Client = clients[0] @@ -134,11 +145,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 +158,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/deployment/multiclient_test.go b/deployment/multiclient_test.go index 0dbebbe3a6a..2e10c46e33f 100644 --- a/deployment/multiclient_test.go +++ b/deployment/multiclient_test.go @@ -1,6 +1,7 @@ package deployment import ( + "io/ioutil" "net/http" "net/http/httptest" "testing" @@ -15,20 +16,33 @@ func TestMultiClient(t *testing.T) { lggr := logger.TestLogger(t) // Expect an error if no RPCs supplied. s := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { - writer.WriteHeader(http.StatusOK) - _, err := writer.Write([]byte(`{"jsonrpc":"2.0","id":1,"result":true}`)) + b, err := ioutil.ReadAll(request.Body) require.NoError(t, err) + // TODO: Helper struct somewhere for this? + if string(b) == "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"eth_chainId\"}" { + writer.WriteHeader(http.StatusOK) + // Respond with 1337 + _, err = writer.Write([]byte(`{"jsonrpc":"2.0","id":1,"result":"0x539"}`)) + require.NoError(t, err) + return + } else { + // Dial + writer.WriteHeader(http.StatusOK) + _, err = writer.Write([]byte(`{"jsonrpc":"2.0","id":1,"result":true}`)) + require.NoError(t, err) + } })) defer s.Close() - _, err := NewMultiClient(lggr, []RPC{}) - require.Error(t, err) - // Expect defaults to be set if not provided. mc, err := NewMultiClient(lggr, []RPC{{WSURL: s.URL}}) require.NoError(t, err) + require.NotNil(t, mc) assert.Equal(t, mc.RetryConfig.Attempts, uint(RPC_DEFAULT_RETRY_ATTEMPTS)) assert.Equal(t, mc.RetryConfig.Delay, RPC_DEFAULT_RETRY_DELAY) + _, err = NewMultiClient(lggr, []RPC{}) + require.Error(t, err) + // Expect second client to be set as backup. mc, err = NewMultiClient(lggr, []RPC{ {WSURL: s.URL}, diff --git a/go.mod b/go.mod index ad71fa75be3..35069d38bbf 100644 --- a/go.mod +++ b/go.mod @@ -76,7 +76,7 @@ require ( github.com/scylladb/go-reflectx v1.0.1 github.com/shirou/gopsutil/v3 v3.24.3 github.com/shopspring/decimal v1.4.0 - github.com/smartcontractkit/chain-selectors v1.0.31 + github.com/smartcontractkit/chain-selectors v1.0.34 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.20241206011233-b6684ee6508f @@ -89,7 +89,7 @@ require ( github.com/smartcontractkit/libocr v0.0.0-20241007185508-adbe57025f12 github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20241009055228-33d0c0bf38de github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20241009055228-33d0c0bf38de - github.com/smartcontractkit/wsrpc v0.8.2 + github.com/smartcontractkit/wsrpc v0.8.3 github.com/spf13/cast v1.6.0 github.com/stretchr/testify v1.9.0 github.com/test-go/testify v1.1.4 diff --git a/go.sum b/go.sum index 42de8ab4a72..caa877d66fa 100644 --- a/go.sum +++ b/go.sum @@ -1119,8 +1119,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartcontractkit/chain-selectors v1.0.31 h1:oRHyK88KnsCh4OdU2hr0u70pm3KUgyMDyK0v0aOtUk4= -github.com/smartcontractkit/chain-selectors v1.0.31/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= +github.com/smartcontractkit/chain-selectors v1.0.34 h1:MJ17OGu8+jjl426pcKrJkCf3fePb3eCreuAnUA3RBj4= +github.com/smartcontractkit/chain-selectors v1.0.34/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= 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= @@ -1147,8 +1147,8 @@ github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20241009055228- github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20241009055228-33d0c0bf38de/go.mod h1:Sl2MF/Fp3fgJIVzhdGhmZZX2BlnM0oUUyBP4s4xYb6o= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20241009055228-33d0c0bf38de h1:66VQxXx3lvTaAZrMBkIcdH9VEjujUEvmBQdnyOJnkOc= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20241009055228-33d0c0bf38de/go.mod h1:NSc7hgOQbXG3DAwkOdWnZzLTZENXSwDJ7Va1nBp0YU0= -github.com/smartcontractkit/wsrpc v0.8.2 h1:XB/xcn/MMseHW+8JE8+a/rceA86ck7Ur6cEa9LiUC8M= -github.com/smartcontractkit/wsrpc v0.8.2/go.mod h1:2u/wfnhl5R4RlSXseN4n6HHIWk8w1Am3AT6gWftQbNg= +github.com/smartcontractkit/wsrpc v0.8.3 h1:9tDf7Ut61g36RJIyxV9iI73SqoOMasKPfURV9oMLrPg= +github.com/smartcontractkit/wsrpc v0.8.3/go.mod h1:2u/wfnhl5R4RlSXseN4n6HHIWk8w1Am3AT6gWftQbNg= github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index b9c6f213277..f4e6a9720e8 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -38,7 +38,7 @@ require ( github.com/shopspring/decimal v1.4.0 github.com/slack-go/slack v0.15.0 github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86 - github.com/smartcontractkit/chain-selectors v1.0.31 + github.com/smartcontractkit/chain-selectors v1.0.34 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.20241206011233-b6684ee6508f @@ -428,7 +428,7 @@ require ( github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20241009055228-33d0c0bf38de // indirect github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20241009055228-33d0c0bf38de // indirect - github.com/smartcontractkit/wsrpc v0.8.2 // indirect + github.com/smartcontractkit/wsrpc v0.8.3 // indirect github.com/soheilhy/cmux v0.1.5 // indirect github.com/sony/gobreaker v0.5.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 6914a06501f..9e0e176190d 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1426,8 +1426,8 @@ github.com/slack-go/slack v0.15.0 h1:LE2lj2y9vqqiOf+qIIy0GvEoxgF1N5yLGZffmEZykt0 github.com/slack-go/slack v0.15.0/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86 h1:qQH6fZZe31nBAG6INHph3z5ysDTPptyu0TR9uoJ1+ok= github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86/go.mod h1:WtWOoVQQEHxRHL2hNmuRrvDfYfQG/CioFNoa9Rr2mBE= -github.com/smartcontractkit/chain-selectors v1.0.31 h1:oRHyK88KnsCh4OdU2hr0u70pm3KUgyMDyK0v0aOtUk4= -github.com/smartcontractkit/chain-selectors v1.0.31/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= +github.com/smartcontractkit/chain-selectors v1.0.34 h1:MJ17OGu8+jjl426pcKrJkCf3fePb3eCreuAnUA3RBj4= +github.com/smartcontractkit/chain-selectors v1.0.34/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= 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= @@ -1466,8 +1466,8 @@ github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20241009055228- github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20241009055228-33d0c0bf38de/go.mod h1:Sl2MF/Fp3fgJIVzhdGhmZZX2BlnM0oUUyBP4s4xYb6o= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20241009055228-33d0c0bf38de h1:66VQxXx3lvTaAZrMBkIcdH9VEjujUEvmBQdnyOJnkOc= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20241009055228-33d0c0bf38de/go.mod h1:NSc7hgOQbXG3DAwkOdWnZzLTZENXSwDJ7Va1nBp0YU0= -github.com/smartcontractkit/wsrpc v0.8.2 h1:XB/xcn/MMseHW+8JE8+a/rceA86ck7Ur6cEa9LiUC8M= -github.com/smartcontractkit/wsrpc v0.8.2/go.mod h1:2u/wfnhl5R4RlSXseN4n6HHIWk8w1Am3AT6gWftQbNg= +github.com/smartcontractkit/wsrpc v0.8.3 h1:9tDf7Ut61g36RJIyxV9iI73SqoOMasKPfURV9oMLrPg= +github.com/smartcontractkit/wsrpc v0.8.3/go.mod h1:2u/wfnhl5R4RlSXseN4n6HHIWk8w1Am3AT6gWftQbNg= github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index 34717eea023..23f840a67f3 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -399,7 +399,7 @@ require ( github.com/shoenig/test v0.6.6 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/smartcontractkit/chain-selectors v1.0.31 // indirect + github.com/smartcontractkit/chain-selectors v1.0.34 // indirect github.com/smartcontractkit/chainlink-automation v0.8.1 // indirect github.com/smartcontractkit/chainlink-ccip v0.0.0-20241204015713-8956bb614e9e // indirect github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e // indirect @@ -413,7 +413,7 @@ require ( github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect github.com/smartcontractkit/libocr v0.0.0-20241007185508-adbe57025f12 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20241009055228-33d0c0bf38de // indirect - github.com/smartcontractkit/wsrpc v0.8.2 // indirect + github.com/smartcontractkit/wsrpc v0.8.3 // indirect github.com/soheilhy/cmux v0.1.5 // indirect github.com/sony/gobreaker v0.5.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index bcb3680e9c5..e9eaaf81dad 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1417,8 +1417,8 @@ github.com/slack-go/slack v0.15.0 h1:LE2lj2y9vqqiOf+qIIy0GvEoxgF1N5yLGZffmEZykt0 github.com/slack-go/slack v0.15.0/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86 h1:qQH6fZZe31nBAG6INHph3z5ysDTPptyu0TR9uoJ1+ok= github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86/go.mod h1:WtWOoVQQEHxRHL2hNmuRrvDfYfQG/CioFNoa9Rr2mBE= -github.com/smartcontractkit/chain-selectors v1.0.31 h1:oRHyK88KnsCh4OdU2hr0u70pm3KUgyMDyK0v0aOtUk4= -github.com/smartcontractkit/chain-selectors v1.0.31/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= +github.com/smartcontractkit/chain-selectors v1.0.34 h1:MJ17OGu8+jjl426pcKrJkCf3fePb3eCreuAnUA3RBj4= +github.com/smartcontractkit/chain-selectors v1.0.34/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= 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= @@ -1457,8 +1457,8 @@ github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20241009055228- github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20241009055228-33d0c0bf38de/go.mod h1:Sl2MF/Fp3fgJIVzhdGhmZZX2BlnM0oUUyBP4s4xYb6o= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20241009055228-33d0c0bf38de h1:66VQxXx3lvTaAZrMBkIcdH9VEjujUEvmBQdnyOJnkOc= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20241009055228-33d0c0bf38de/go.mod h1:NSc7hgOQbXG3DAwkOdWnZzLTZENXSwDJ7Va1nBp0YU0= -github.com/smartcontractkit/wsrpc v0.8.2 h1:XB/xcn/MMseHW+8JE8+a/rceA86ck7Ur6cEa9LiUC8M= -github.com/smartcontractkit/wsrpc v0.8.2/go.mod h1:2u/wfnhl5R4RlSXseN4n6HHIWk8w1Am3AT6gWftQbNg= +github.com/smartcontractkit/wsrpc v0.8.3 h1:9tDf7Ut61g36RJIyxV9iI73SqoOMasKPfURV9oMLrPg= +github.com/smartcontractkit/wsrpc v0.8.3/go.mod h1:2u/wfnhl5R4RlSXseN4n6HHIWk8w1Am3AT6gWftQbNg= github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= diff --git a/integration-tests/smoke/ccip/ccip_ooo_execution_test.go b/integration-tests/smoke/ccip/ccip_ooo_execution_test.go index 6a3a6b869cd..86ddd07ec85 100644 --- a/integration-tests/smoke/ccip/ccip_ooo_execution_test.go +++ b/integration-tests/smoke/ccip/ccip_ooo_execution_test.go @@ -14,7 +14,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "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/deployment/environment/memory" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -36,9 +36,12 @@ func Test_OutOfOrderExecution(t *testing.T) { IsUSDC: true, IsUSDCAttestationMissing: true, } - tenv, _, _ := testsetups.NewLocalDevEnvironmentWithDefaultPrice(t, lggr, config) - // Inmemory setup used for debugging and development, use instead of docker when needed - //tenv := changeset.NewMemoryEnvironmentWithJobsAndContracts(t, lggr, 2, 4, config) + tenv := changeset.NewMemoryEnvironmentWithJobsAndContracts(t, logger.TestLogger(t), memory.MemoryEnvironmentConfig{ + Chains: 2, + Nodes: 4, + Bootstraps: 1, + NumOfUsersPerChain: 2, + }, config) e := tenv.Env state, err := changeset.LoadOnchainState(e) diff --git a/integration-tests/smoke/ccip/ccip_token_transfer_test.go b/integration-tests/smoke/ccip/ccip_token_transfer_test.go index 81920246bed..13abe33fe7c 100644 --- a/integration-tests/smoke/ccip/ccip_token_transfer_test.go +++ b/integration-tests/smoke/ccip/ccip_token_transfer_test.go @@ -1,24 +1,17 @@ package smoke import ( - "context" "math/big" "testing" "golang.org/x/exp/maps" - "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/require" - sel "github.com/smartcontractkit/chain-selectors" - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - "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/deployment/environment/memory" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -28,28 +21,30 @@ func TestTokenTransfer(t *testing.T) { lggr := logger.TestLogger(t) ctx := tests.Context(t) config := &changeset.TestConfigs{} - tenv, _, _ := testsetups.NewLocalDevEnvironmentWithDefaultPrice(t, lggr, config) - inMemoryEnv := false - // use this if you are testing locally in memory - // tenv := changeset.NewMemoryEnvironmentWithJobsAndContracts(t, lggr, 2, 4, config) - // inMemoryEnv := true + tenv := changeset.NewMemoryEnvironmentWithJobsAndContracts(t, logger.TestLogger(t), memory.MemoryEnvironmentConfig{ + Chains: 2, + Nodes: 4, + Bootstraps: 1, + NumOfUsersPerChain: 3, + }, config) e := tenv.Env state, err := changeset.LoadOnchainState(e) require.NoError(t, err) + require.GreaterOrEqual(t, len(e.Chains), 2) allChainSelectors := maps.Keys(e.Chains) sourceChain, destChain := allChainSelectors[0], allChainSelectors[1] ownerSourceChain := e.Chains[sourceChain].DeployerKey ownerDestChain := e.Chains[destChain].DeployerKey - oneE18 := new(big.Int).SetUint64(1e18) - funds := new(big.Int).Mul(oneE18, new(big.Int).SetUint64(10)) + require.GreaterOrEqual(t, len(tenv.Users[sourceChain]), 2) + require.GreaterOrEqual(t, len(tenv.Users[destChain]), 2) + selfServeSrcTokenPoolDeployer := tenv.Users[sourceChain][1] + selfServeDestTokenPoolDeployer := tenv.Users[destChain][1] - // Deploy and fund self-serve actors - selfServeSrcTokenPoolDeployer := createAndFundSelfServeActor(ctx, t, ownerSourceChain, e.Chains[sourceChain], funds, inMemoryEnv) - selfServeDestTokenPoolDeployer := createAndFundSelfServeActor(ctx, t, ownerDestChain, e.Chains[destChain], funds, inMemoryEnv) + oneE18 := new(big.Int).SetUint64(1e18) // Deploy tokens and pool by CCIP Owner srcToken, _, destToken, _, err := changeset.DeployTransferableToken( @@ -226,53 +221,3 @@ func TestTokenTransfer(t *testing.T) { changeset.WaitForTokenBalances(ctx, t, e.Chains, expectedTokenBalances) } - -func createAndFundSelfServeActor( - ctx context.Context, - t *testing.T, - deployer *bind.TransactOpts, - chain deployment.Chain, - amountToFund *big.Int, - isInMemory bool, -) *bind.TransactOpts { - key, err := crypto.GenerateKey() - require.NoError(t, err) - - // Simulated backend sets chainID to 1337 always - chainID := big.NewInt(1337) - if !isInMemory { - // Docker environment runs real geth so chainID has to be set accordingly - stringChainID, err1 := sel.GetChainIDFromSelector(chain.Selector) - require.NoError(t, err1) - chainID, _ = new(big.Int).SetString(stringChainID, 10) - } - - actor, err := bind.NewKeyedTransactorWithChainID(key, chainID) - require.NoError(t, err) - - nonce, err := chain.Client.PendingNonceAt(ctx, deployer.From) - require.NoError(t, err) - - gasPrice, err := chain.Client.SuggestGasPrice(ctx) - require.NoError(t, err) - - tx := types.NewTx(&types.LegacyTx{ - Nonce: nonce, - To: &actor.From, - Value: amountToFund, - Gas: uint64(21000), - GasPrice: gasPrice, - Data: nil, - }) - - signedTx, err := deployer.Signer(deployer.From, tx) - require.NoError(t, err) - - err = chain.Client.SendTransaction(ctx, signedTx) - require.NoError(t, err) - - _, err = chain.Confirm(signedTx) - require.NoError(t, err) - - return actor -} diff --git a/integration-tests/smoke/ccip/ccip_usdc_test.go b/integration-tests/smoke/ccip/ccip_usdc_test.go index eacf03df926..2dae3f3c48e 100644 --- a/integration-tests/smoke/ccip/ccip_usdc_test.go +++ b/integration-tests/smoke/ccip/ccip_usdc_test.go @@ -13,7 +13,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "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/deployment/environment/memory" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/burn_mint_erc677" @@ -34,13 +34,12 @@ func TestUSDCTokenTransfer(t *testing.T) { config := &changeset.TestConfigs{ IsUSDC: true, } - tenv, _, _ := testsetups.NewLocalDevEnvironmentWithDefaultPrice(t, lggr, config) - //tenv := changeset.NewMemoryEnvironmentWithJobsAndContracts(t, lggr, memory.MemoryEnvironmentConfig{ - // Chains: 3, - // NumOfUsersPerChain: 3, - // Nodes: 5, - // Bootstraps: 1, - //}, config) + tenv := changeset.NewMemoryEnvironmentWithJobsAndContracts(t, lggr, memory.MemoryEnvironmentConfig{ + Chains: 3, + NumOfUsersPerChain: 3, + Nodes: 4, + Bootstraps: 1, + }, config) e := tenv.Env state, err := changeset.LoadOnchainState(e) diff --git a/integration-tests/testsetups/ccip/test_helpers.go b/integration-tests/testsetups/ccip/test_helpers.go index 4d51093d419..3112d738869 100644 --- a/integration-tests/testsetups/ccip/test_helpers.go +++ b/integration-tests/testsetups/ccip/test_helpers.go @@ -244,7 +244,6 @@ func NewLocalDevEnvironment( Config: changeset.NewChainsConfig{ HomeChainSel: homeChainSel, FeedChainSel: feedSel, - OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), ChainConfigByChain: chainConfigs, }, },