From c17067b874bebcda72cae8a335d9c6b5fc681f3a Mon Sep 17 00:00:00 2001 From: Domino Valdano <2644901+reductionista@users.noreply.github.com> Date: Wed, 6 Dec 2023 09:20:41 -0800 Subject: [PATCH] Interfaces and skeleton methods for ChainReader EVM POC (#10990) * Implement skeleton interfaces, structs, & methods for ChainReader EVM POC - Read ChainReader config in from RelayConfig - Add some initialization and validation relay skeletons - Use medianProviderWrapper instead of passing medianContract separately This avoids us having to modify the signature of NewMedianFactory, which would require further modifications to all non-evm repos and chainlink-relay - Add chain_reader_test.go with some basic relay tests Co-authored-by: Jordan Krage - Add chain reader config validation - Add chain reader config validation tests - Add config for chain reader median contract to cr validation testcases - Add unimplemented Encode(), Decode(), GetMaxEncodingSize(), GetMaxDecodingSize() - Add ChainReader() method to mock provider for plugin test - Rename relaymercury.ChainReader to MercuryChainReader, resolve name collisions - Add tests for errors during ChainReader construction - Propagate InvalidConfig & any other errors back to client We should ignore Unimplemented until node ops have been given ample time to migrate to the new job spec (including a section for ChainReader config) so that we can remove the old product-specific MedianContract component from MedianProvider. All other errors we can immediately start passing back to the client, letting the core node decide how to handle them (eg. displaying an "invalid job spec" message to the UI if the RelayConfig was invalid or the ContractID missing) * Update relay versions * Simplify chain reader config validation * Update commit hashes one final time now that all relays are merged. --------- Co-authored-by: ilija --- core/scripts/go.mod | 9 +- core/scripts/go.sum | 18 +- core/services/chainlink/relayer_factory.go | 3 +- core/services/functions/listener.go | 1 + .../services/ocr2/plugins/functions/plugin.go | 1 + core/services/ocr2/plugins/median/plugin.go | 68 +++++ core/services/ocr2/plugins/median/services.go | 83 +++++- core/services/ocr2/plugins/mercury/plugin.go | 2 +- .../plugins/ocr2keeper/integration_21_test.go | 1 - core/services/ocr2/plugins/ocr2keeper/util.go | 1 - core/services/relay/evm/chain_reader.go | 154 ++++++++++ core/services/relay/evm/chain_reader_test.go | 272 ++++++++++++++++++ core/services/relay/evm/config_poller.go | 1 + core/services/relay/evm/evm.go | 34 ++- core/services/relay/evm/functions.go | 5 + .../relay/evm/functions/logpoller_wrapper.go | 1 + .../evm/functions/offchain_config_digester.go | 8 +- core/services/relay/evm/median_test.go | 47 +++ .../relay/evm/mercury/v1/data_source.go | 9 +- .../relay/evm/mercury/v1/data_source_test.go | 26 +- core/services/relay/evm/mercury_provider.go | 54 ++-- core/services/relay/evm/ocr2keeper.go | 4 + core/services/relay/evm/ocr2vrf.go | 8 + core/services/relay/evm/types/types.go | 35 ++- .../relay/grpc_provider_server_test.go | 3 +- core/services/relay/relay_test.go | 1 + go.mod | 9 +- go.sum | 18 +- integration-tests/docker/test_env/test_env.go | 18 +- integration-tests/go.mod | 9 +- integration-tests/go.sum | 18 +- plugins/medianpoc/plugin_test.go | 7 +- 32 files changed, 828 insertions(+), 100 deletions(-) create mode 100644 core/services/ocr2/plugins/median/plugin.go create mode 100644 core/services/relay/evm/chain_reader.go create mode 100644 core/services/relay/evm/chain_reader_test.go create mode 100644 core/services/relay/evm/median_test.go diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 91e26caef69..b0b5f801ed5 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -119,6 +119,7 @@ require ( github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-gonic/gin v1.9.1 // indirect github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect + github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-ldap/ldap/v3 v3.4.5 // indirect @@ -257,12 +258,12 @@ require ( github.com/shirou/gopsutil/v3 v3.23.10 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c // indirect - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 // indirect + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4 // indirect + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e // indirect github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 // indirect - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e // indirect + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/wsrpc v0.7.2 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 1622f1d24df..dd69053b405 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -443,6 +443,8 @@ github.com/go-faster/errors v0.6.1/go.mod h1:5MGV2/2T9yvlrbhe9pD9LO5Z/2zCSq2T8j+ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 h1:ymLjT4f35nQbASLnvxEde4XOBL+Sn7rFuV+FOJqkljg= +github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0/go.mod h1:6daplAwHHGbUGib4990V3Il26O0OC4aRyvewaaAihaA= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= @@ -1212,18 +1214,18 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c h1:YFyo0pCmKkpB4EOSykCZFueRXNxQ7OhKiCnhoVIzydo= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4 h1:2LC5HtHkAm7I1h5j4Uz0KTX+h4fo4z/5NoAARSF4ZVA= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e h1:xvqffqFec2HkEcUKrCkm4FDJRnn/+gHmvrE/dz3Zlw8= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e/go.mod h1:soVgcl4CbfR6hC9UptjuCQhz19HJaFEjwnOpiySkxg0= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1/go.mod h1:GuPvyXryvbiUZIHmPeLBz4L+yJKeyGUjrDfd1KNne+o= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d/go.mod h1:YPAfLNowdBwiKiYOwgwtbJHi8AJWbcxkbOY0ItAvkfc= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 h1:DudPr8ZNMEVgDwHBvnrpvK96JrGcGCG+LLBnlqaPGnE= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3/go.mod h1:UfW7/PZKon+iDEHtrHOfvMnS5GfYOW/SdMZ6P97rPEk= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 h1:yxaHuDTtj2xxtsR8b+LJw8xDvyr6v/F6GP3InsP4wPI= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664/go.mod h1:3Fa+HQTZ3R3fPC0hUqugvoo+NEeo8Y4J2WOnQfi7O34= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e h1:/tCHhoAJM+ittEHPZTtJsAgXmYujKiDW0ub9HXW9qtY= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e/go.mod h1:9YIi413QRRytafTzpWm+Z+5NWBNxSqokhKyeEZ3ynlA= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725 h1:NbhPVwxx+53WN/Uld1V6c4iLgoGvUYFOsVd2kfcexe8= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725/go.mod h1:vHrPBipRL52NdPp77KXNU2k1IoCUa1B33N9otZQPYko= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= diff --git a/core/services/chainlink/relayer_factory.go b/core/services/chainlink/relayer_factory.go index 8b8749013fc..4ed73d8e53b 100644 --- a/core/services/chainlink/relayer_factory.go +++ b/core/services/chainlink/relayer_factory.go @@ -5,9 +5,8 @@ import ( "errors" "fmt" - "github.com/pelletier/go-toml/v2" - "github.com/jmoiron/sqlx" + "github.com/pelletier/go-toml/v2" "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos" diff --git a/core/services/functions/listener.go b/core/services/functions/listener.go index 65c364adb7c..f9d74f1bae9 100644 --- a/core/services/functions/listener.go +++ b/core/services/functions/listener.go @@ -16,6 +16,7 @@ import ( "github.com/smartcontractkit/libocr/commontypes" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/cbor" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/ocr2/plugins/functions/plugin.go b/core/services/ocr2/plugins/functions/plugin.go index 2ebd7e30805..61fa7f5d825 100644 --- a/core/services/ocr2/plugins/functions/plugin.go +++ b/core/services/ocr2/plugins/functions/plugin.go @@ -14,6 +14,7 @@ import ( libocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus" "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/ocr2/plugins/median/plugin.go b/core/services/ocr2/plugins/median/plugin.go new file mode 100644 index 00000000000..cad2099832d --- /dev/null +++ b/core/services/ocr2/plugins/median/plugin.go @@ -0,0 +1,68 @@ +package median + +import ( + "context" + + "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/types" +) + +type Plugin struct { + loop.Plugin + stop services.StopChan +} + +func NewPlugin(lggr logger.Logger) *Plugin { + return &Plugin{Plugin: loop.Plugin{Logger: lggr}, stop: make(services.StopChan)} +} + +func (p *Plugin) NewMedianFactory(ctx context.Context, provider types.MedianProvider, dataSource, juelsPerFeeCoin median.DataSource, errorLog loop.ErrorLog) (loop.ReportingPluginFactory, error) { + var ctxVals loop.ContextValues + ctxVals.SetValues(ctx) + lggr := logger.With(p.Logger, ctxVals.Args()...) + + factory := median.NumericalMedianFactory{ + ContractTransmitter: provider.MedianContract(), + DataSource: dataSource, + JuelsPerFeeCoinDataSource: juelsPerFeeCoin, + Logger: logger.NewOCRWrapper(lggr, true, func(msg string) { + ctx, cancelFn := p.stop.NewCtx() + defer cancelFn() + if err := errorLog.SaveError(ctx, msg); err != nil { + lggr.Errorw("Unable to save error", "err", msg) + } + }), + OnchainConfigCodec: provider.OnchainConfigCodec(), + ReportCodec: provider.ReportCodec(), + } + s := &reportingPluginFactoryService{lggr: logger.Named(lggr, "ReportingPluginFactory"), ReportingPluginFactory: factory} + + p.SubService(s) + + return s, nil +} + +type reportingPluginFactoryService struct { + services.StateMachine + lggr logger.Logger + ocrtypes.ReportingPluginFactory +} + +func (r *reportingPluginFactoryService) Name() string { return r.lggr.Name() } + +func (r *reportingPluginFactoryService) Start(ctx context.Context) error { + return r.StartOnce("ReportingPluginFactory", func() error { return nil }) +} + +func (r *reportingPluginFactoryService) Close() error { + return r.StopOnce("ReportingPluginFactory", func() error { return nil }) +} + +func (r *reportingPluginFactoryService) HealthReport() map[string]error { + return map[string]error{r.Name(): r.Healthy()} +} diff --git a/core/services/ocr2/plugins/median/services.go b/core/services/ocr2/plugins/median/services.go index 2d1ef720545..4adfc306d64 100644 --- a/core/services/ocr2/plugins/median/services.go +++ b/core/services/ocr2/plugins/median/services.go @@ -5,9 +5,14 @@ import ( "encoding/json" "errors" "fmt" + "math/big" "time" + "github.com/ethereum/go-ethereum/common" libocr "github.com/smartcontractkit/libocr/offchainreporting2plus" + ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + mediantypes "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-common/pkg/types" @@ -52,6 +57,21 @@ func (m *medianConfig) JobPipelineResultWriteQueueDepth() uint64 { return m.jobPipelineResultWriteQueueDepth } +// This wrapper avoids the need to modify the signature of NewMedianFactory in all of the non-evm +// relay repos as well as its primary definition in chainlink-common. Once ChainReader is implemented +// and working on all 4 blockchain families, we can remove the original MedianContract() method from +// MedianProvider and pass medianContract as a separate param to NewMedianFactory +type medianProviderWrapper struct { + types.MedianProvider + contract mediantypes.MedianContract +} + +// Override relay's implementation of MedianContract with product plugin's implementation of +// MedianContract, making use of product-agnostic ChainReader to read the contract instead of relay MedianContract +func (m medianProviderWrapper) MedianContract() mediantypes.MedianContract { + return m.contract +} + func NewMedianServices(ctx context.Context, jb job.Job, isNewlyCreatedJob bool, @@ -125,11 +145,24 @@ func NewMedianServices(ctx context.Context, CreatedAt: time.Now(), }, lggr) - if cmdName := env.MedianPluginCmd.Get(); cmdName != "" { + medianPluginCmd := env.MedianPluginCmd.Get() + medianLoopEnabled := medianPluginCmd != "" + + // TODO BCF-2821 handle this properly as this blocks Solana chain reader dev + if !medianLoopEnabled && medianProvider.ChainReader() != nil { + lggr.Info("Chain Reader enabled") + medianProvider = medianProviderWrapper{ + medianProvider, // attach newer MedianContract which uses ChainReader + newMedianContract(provider.ChainReader(), common.HexToAddress(spec.ContractID)), + } + } else { + lggr.Info("Chain Reader disabled") + } + if medianLoopEnabled { // use unique logger names so we can use it to register a loop medianLggr := lggr.Named("Median").Named(spec.ContractID).Named(spec.GetID()) - cmdFn, telem, err2 := cfg.RegisterLOOP(medianLggr.Name(), cmdName) + cmdFn, telem, err2 := cfg.RegisterLOOP(medianLggr.Name(), medianPluginCmd) if err2 != nil { err = fmt.Errorf("failed to register loop: %w", err2) abort() @@ -159,3 +192,49 @@ func NewMedianServices(ctx context.Context, } return } + +type medianContract struct { + chainReader types.ChainReader + contract types.BoundContract +} + +type latestTransmissionDetailsResponse struct { + configDigest ocr2types.ConfigDigest + epoch uint32 + round uint8 + latestAnswer *big.Int + latestTimestamp time.Time +} + +type latestRoundRequested struct { + configDigest ocr2types.ConfigDigest + epoch uint32 + round uint8 +} + +func (m *medianContract) LatestTransmissionDetails(ctx context.Context) (configDigest ocr2types.ConfigDigest, epoch uint32, round uint8, latestAnswer *big.Int, latestTimestamp time.Time, err error) { + var resp latestTransmissionDetailsResponse + + err = m.chainReader.GetLatestValue(ctx, m.contract, "LatestTransmissionDetails", nil, &resp) + if err != nil { + return + } + + return resp.configDigest, resp.epoch, resp.round, resp.latestAnswer, resp.latestTimestamp, err +} + +func (m *medianContract) LatestRoundRequested(ctx context.Context, lookback time.Duration) (configDigest ocr2types.ConfigDigest, epoch uint32, round uint8, err error) { + var resp latestRoundRequested + + err = m.chainReader.GetLatestValue(ctx, m.contract, "LatestRoundReported", map[string]string{}, &resp) + if err != nil { + return + } + + return resp.configDigest, resp.epoch, resp.round, err +} + +func newMedianContract(chainReader types.ChainReader, address common.Address) *medianContract { + contract := types.BoundContract{Address: address.String(), Name: "median", Pending: true} + return &medianContract{chainReader, contract} +} diff --git a/core/services/ocr2/plugins/mercury/plugin.go b/core/services/ocr2/plugins/mercury/plugin.go index f5c11dc7313..b2767d6bcf5 100644 --- a/core/services/ocr2/plugins/mercury/plugin.go +++ b/core/services/ocr2/plugins/mercury/plugin.go @@ -67,7 +67,7 @@ func NewServices( lggr, saver, chEnhancedTelem, - ocr2Provider.ChainReader(), + ocr2Provider.MercuryChainReader(), ocr2Provider.MercuryServerFetcher(), pluginConfig.InitialBlockNumber.Ptr(), feedID, diff --git a/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go b/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go index 7ccd785a668..c2b6612f664 100644 --- a/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go @@ -32,7 +32,6 @@ import ( ocrTypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/chainlink-automation/pkg/v3/config" - "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" diff --git a/core/services/ocr2/plugins/ocr2keeper/util.go b/core/services/ocr2/plugins/ocr2keeper/util.go index 02a0d033bc1..76e5bb6e00e 100644 --- a/core/services/ocr2/plugins/ocr2keeper/util.go +++ b/core/services/ocr2/plugins/ocr2keeper/util.go @@ -12,7 +12,6 @@ import ( ocr2keepers20polling "github.com/smartcontractkit/chainlink-automation/pkg/v2/observer/polling" ocr2keepers20runner "github.com/smartcontractkit/chainlink-automation/pkg/v2/runner" ocr2keepers21 "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" diff --git a/core/services/relay/evm/chain_reader.go b/core/services/relay/evm/chain_reader.go new file mode 100644 index 00000000000..e4da4cc1a49 --- /dev/null +++ b/core/services/relay/evm/chain_reader.go @@ -0,0 +1,154 @@ +package evm + +import ( + "context" + "fmt" + "slices" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" +) + +type ChainReaderService interface { + services.ServiceCtx + commontypes.ChainReader +} + +type chainReader struct { + lggr logger.Logger + contractID common.Address + lp logpoller.LogPoller +} + +// NewChainReaderService constructor for ChainReader +func NewChainReaderService(lggr logger.Logger, lp logpoller.LogPoller, contractID common.Address, config types.ChainReaderConfig) (*chainReader, error) { + if err := validateChainReaderConfig(config); err != nil { + return nil, fmt.Errorf("%w: %w", commontypes.ErrInvalidConfig, err) + } + + // TODO BCF-2814 implement initialisation of chain reading definitions and pass them into chainReader + return &chainReader{lggr.Named("ChainReader"), contractID, lp}, nil +} + +func (cr *chainReader) Name() string { return cr.lggr.Name() } + +func (cr *chainReader) initialize() error { + // Initialize chain reader, start cache polling loop, etc. + return nil +} + +func (cr *chainReader) Start(ctx context.Context) error { + if err := cr.initialize(); err != nil { + return fmt.Errorf("Failed to initialize ChainReader: %w", err) + } + return nil +} + +func (cr *chainReader) Close() error { return nil } + +func (cr *chainReader) Ready() error { return nil } + +func (cr *chainReader) HealthReport() map[string]error { + return map[string]error{cr.Name(): nil} +} + +func (cr *chainReader) GetLatestValue(ctx context.Context, bc commontypes.BoundContract, method string, params any, returnVal any) error { + return commontypes.UnimplementedError("Unimplemented method GetLatestValue called") +} + +func validateChainReaderConfig(cfg types.ChainReaderConfig) error { + if len(cfg.ChainContractReaders) == 0 { + return fmt.Errorf("%w: no contract readers defined", commontypes.ErrInvalidConfig) + } + + for contractName, chainContractReader := range cfg.ChainContractReaders { + abi, err := abi.JSON(strings.NewReader(chainContractReader.ContractABI)) + if err != nil { + return fmt.Errorf("invalid abi: %w", err) + } + + for chainReadingDefinitionName, chainReaderDefinition := range chainContractReader.ChainReaderDefinitions { + switch chainReaderDefinition.ReadType { + case types.Method: + err = validateMethods(abi, chainReaderDefinition) + case types.Event: + err = validateEvents(abi, chainReaderDefinition) + default: + return fmt.Errorf("%w: invalid chainreading definition read type: %d for contract: %q", commontypes.ErrInvalidConfig, chainReaderDefinition.ReadType, contractName) + } + if err != nil { + return fmt.Errorf("%w: invalid chainreading definition: %q for contract: %q, err: %w", commontypes.ErrInvalidConfig, chainReadingDefinitionName, contractName, err) + } + } + } + + return nil +} + +func validateEvents(contractABI abi.ABI, chainReaderDefinition types.ChainReaderDefinition) error { + event, methodExists := contractABI.Events[chainReaderDefinition.ChainSpecificName] + if !methodExists { + return fmt.Errorf("event: %s doesn't exist", chainReaderDefinition.ChainSpecificName) + } + + var abiEventIndexedInputs []abi.Argument + for _, eventInput := range event.Inputs { + if eventInput.Indexed { + abiEventIndexedInputs = append(abiEventIndexedInputs, eventInput) + } + } + + var chainReaderEventParams []string + for chainReaderEventParam := range chainReaderDefinition.Params { + chainReaderEventParams = append(chainReaderEventParams, chainReaderEventParam) + } + + if !areChainReaderArgumentsValid(abiEventIndexedInputs, chainReaderEventParams) { + var abiEventIndexedInputsNames []string + for _, abiEventIndexedInput := range abiEventIndexedInputs { + abiEventIndexedInputsNames = append(abiEventIndexedInputsNames, abiEventIndexedInput.Name) + } + return fmt.Errorf("params: [%s] don't match abi event indexed inputs: [%s]", strings.Join(chainReaderEventParams, ","), strings.Join(abiEventIndexedInputsNames, ",")) + } + return nil +} + +func validateMethods(abi abi.ABI, chainReaderDefinition types.ChainReaderDefinition) error { + method, methodExists := abi.Methods[chainReaderDefinition.ChainSpecificName] + if !methodExists { + return fmt.Errorf("method: %q doesn't exist", chainReaderDefinition.ChainSpecificName) + } + + var methodNames []string + for methodName := range chainReaderDefinition.Params { + methodNames = append(methodNames, methodName) + } + + if !areChainReaderArgumentsValid(method.Inputs, methodNames) { + var abiMethodInputs []string + for _, input := range method.Inputs { + abiMethodInputs = append(abiMethodInputs, input.Name) + } + return fmt.Errorf("params: [%s] don't match abi method inputs: [%s]", strings.Join(methodNames, ","), strings.Join(abiMethodInputs, ",")) + } + + return nil +} + +func areChainReaderArgumentsValid(contractArgs []abi.Argument, chainReaderArgs []string) bool { + for _, contractArg := range contractArgs { + if !slices.Contains(chainReaderArgs, contractArg.Name) { + return false + } + } + + return true +} diff --git a/core/services/relay/evm/chain_reader_test.go b/core/services/relay/evm/chain_reader_test.go new file mode 100644 index 00000000000..ece2234ab55 --- /dev/null +++ b/core/services/relay/evm/chain_reader_test.go @@ -0,0 +1,272 @@ +package evm + +import ( + "encoding/json" + "fmt" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + + mocklogpoller "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm/mocks" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/logger" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" +) + +type chainReaderTestHelper struct { +} + +func (crTestHelper chainReaderTestHelper) makeChainReaderConfig(abi string, params map[string]any) evmtypes.ChainReaderConfig { + return evmtypes.ChainReaderConfig{ + ChainContractReaders: map[string]evmtypes.ChainContractReader{ + "MyContract": { + ContractABI: abi, + ChainReaderDefinitions: map[string]evmtypes.ChainReaderDefinition{ + "MyGenericMethod": { + ChainSpecificName: "name", + Params: params, + CacheEnabled: false, + ReadType: evmtypes.Method, + }, + }, + }, + }, + } +} + +func (crTestHelper chainReaderTestHelper) makeChainReaderConfigFromStrings(abi string, chainReadingDefinitions string) (evmtypes.ChainReaderConfig, error) { + chainReaderConfigTemplate := `{ + "chainContractReaders": { + "testContract": { + "contractName": "testContract", + "contractABI": "[%s]", + "chainReaderDefinitions": { + %s + } + } + } + }` + + abi = strings.Replace(abi, `"`, `\"`, -1) + formattedCfgJsonString := fmt.Sprintf(chainReaderConfigTemplate, abi, chainReadingDefinitions) + var chainReaderConfig evmtypes.ChainReaderConfig + err := json.Unmarshal([]byte(formattedCfgJsonString), &chainReaderConfig) + return chainReaderConfig, err +} + +func TestNewChainReader(t *testing.T) { + lggr := logger.TestLogger(t) + lp := mocklogpoller.NewLogPoller(t) + chain := mocks.NewChain(t) + contractID := testutils.NewAddress() + contractABI := `[{"inputs":[{"internalType":"string","name":"param","type":"string"}],"name":"name","stateMutability":"view","type":"function"}]` + + t.Run("happy path", func(t *testing.T) { + params := make(map[string]any) + params["param"] = "" + chainReaderConfig := chainReaderTestHelper{}.makeChainReaderConfig(contractABI, params) + chain.On("LogPoller").Return(lp) + _, err := NewChainReaderService(lggr, chain.LogPoller(), contractID, chainReaderConfig) + assert.NoError(t, err) + }) + + t.Run("invalid config", func(t *testing.T) { + invalidChainReaderConfig := chainReaderTestHelper{}.makeChainReaderConfig(contractABI, map[string]any{}) // missing param + _, err := NewChainReaderService(lggr, chain.LogPoller(), contractID, invalidChainReaderConfig) + assert.ErrorIs(t, err, commontypes.ErrInvalidConfig) + }) + + t.Run("ChainReader config is empty", func(t *testing.T) { + emptyChainReaderConfig := evmtypes.ChainReaderConfig{} + _, err := NewChainReaderService(lggr, chain.LogPoller(), contractID, emptyChainReaderConfig) + assert.ErrorIs(t, err, commontypes.ErrInvalidConfig) + assert.ErrorContains(t, err, "no contract readers defined") + }) +} + +func TestChainReaderStartClose(t *testing.T) { + lggr := logger.TestLogger(t) + lp := mocklogpoller.NewLogPoller(t) + cr := chainReader{ + lggr: lggr, + lp: lp, + } + err := cr.Start(testutils.Context(t)) + assert.NoError(t, err) + err = cr.Close() + assert.NoError(t, err) +} + +// TODO Chain Reading Definitions return values are WIP, waiting on codec work and BCF-2789 +func TestValidateChainReaderConfig_HappyPath(t *testing.T) { + type testCase struct { + name string + abiInput string + chainReadingDefinitions string + } + + var testCases []testCase + testCases = append(testCases, + testCase{ + name: "eventWithMultipleIndexedTopics", + abiInput: `{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"Swap","type":"event"}`, + chainReadingDefinitions: `"Swap":{ + "chainSpecificName": "Swap", + "params":{ + "sender": "0x0", + "to": "0x0" + }, + "readType": 1 + }`, + }) + + testCases = append(testCases, + testCase{ + name: "methodWithOneParamAndMultipleResponses", + abiInput: `{"constant":true,"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"getUserAccountData","payable":false,"stateMutability":"view","type":"function"}`, + chainReadingDefinitions: `"getUserAccountData":{ + "chainSpecificName": "getUserAccountData", + "params":{ + "_user": "0x0" + }, + "readType": 0 + }`, + }) + + testCases = append(testCases, + testCase{ + name: "methodWithMultipleParamsAndOneResult", + abiInput: `{"inputs":[{"internalType":"address","name":"_input","type":"address"},{"internalType":"address","name":"_output","type":"address"},{"internalType":"uint256","name":"_inputQuantity","type":"uint256"}],"name":"getSwapOutput","stateMutability":"view","type":"function"}`, + chainReadingDefinitions: `"getSwapOutput":{ + "chainSpecificName": "getSwapOutput", + "params":{ + "_input":"0x0", + "_output":"0x0", + "_inputQuantity":"0x0" + }, + "readType": 0 + }`, + }) + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + cfg, err := chainReaderTestHelper{}.makeChainReaderConfigFromStrings(tc.abiInput, tc.chainReadingDefinitions) + assert.NoError(t, err) + assert.NoError(t, validateChainReaderConfig(cfg)) + }) + } + + t.Run("large config with all test cases", func(t *testing.T) { + var largeABI string + var manyChainReadingDefinitions string + for _, tc := range testCases { + largeABI += tc.abiInput + "," + manyChainReadingDefinitions += tc.chainReadingDefinitions + "," + } + + largeABI = largeABI[:len(largeABI)-1] + manyChainReadingDefinitions = manyChainReadingDefinitions[:len(manyChainReadingDefinitions)-1] + cfg, err := chainReaderTestHelper{}.makeChainReaderConfigFromStrings(largeABI, manyChainReadingDefinitions) + assert.NoError(t, err) + assert.NoError(t, validateChainReaderConfig(cfg)) + }) +} + +// TODO Chain Reading Definitions return values are WIP, waiting on codec work and BCF-2789 +func TestValidateChainReaderConfig_BadPath(t *testing.T) { + type testCase struct { + name string + abiInput string + chainReadingDefinitions string + expected error + } + + var testCases []testCase + mismatchedEventArgumentsTestABI := `{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"Swap","type":"event"}` + testCases = append(testCases, + testCase{ + name: "mismatched abi and event chain reading param values", + abiInput: mismatchedEventArgumentsTestABI, + chainReadingDefinitions: `"Swap":{ + "chainSpecificName": "Swap", + "params":{ + "malformedParam": "0x0" + }, + "readType": 1 + }`, + expected: fmt.Errorf("invalid chainreading definition: \"Swap\" for contract: \"testContract\", err: params: [malformedParam] don't match abi event indexed inputs: [sender]"), + }) + + mismatchedFunctionArgumentsTestABI := `{"constant":true,"inputs":[{"internalType":"address","name":"from","type":"address"}],"name":"Swap","payable":false,"stateMutability":"view","type":"function"}` + testCases = append(testCases, + testCase{ + name: "mismatched abi and method chain reading param values", + abiInput: mismatchedFunctionArgumentsTestABI, + chainReadingDefinitions: `"Swap":{ + "chainSpecificName": "Swap", + "params":{ + "malformedParam": "0x0" + }, + "readType": 0 + }`, + expected: fmt.Errorf("invalid chainreading definition: \"Swap\" for contract: \"testContract\", err: params: [malformedParam] don't match abi method inputs: [from]"), + }, + ) + + testCases = append(testCases, + testCase{ + name: "event doesn't exist", + abiInput: `{"constant":true,"inputs":[],"name":"someName","payable":false,"stateMutability":"view","type":"function"}`, + chainReadingDefinitions: `"TestMethod":{ + "chainSpecificName": "Swap", + "readType": 1 + }`, + expected: fmt.Errorf("invalid chainreading definition: \"TestMethod\" for contract: \"testContract\", err: event: Swap doesn't exist"), + }, + ) + + testCases = append(testCases, + testCase{ + name: "method doesn't exist", + abiInput: `{"constant":true,"inputs":[],"name":"someName","payable":false,"stateMutability":"view","type":"function"}`, + chainReadingDefinitions: `"TestMethod":{ + "chainSpecificName": "Swap", + "readType": 0 + }`, + expected: fmt.Errorf("invalid chainreading definition: \"TestMethod\" for contract: \"testContract\", err: method: \"Swap\" doesn't exist"), + }, + ) + + testCases = append(testCases, testCase{ + name: "invalid abi", + abiInput: `broken abi`, + chainReadingDefinitions: `"TestMethod":{ + "chainSpecificName": "Swap", + "readType": 0 + }`, + expected: fmt.Errorf("invalid abi"), + }) + + testCases = append(testCases, testCase{ + name: "invalid read type", + abiInput: `{"constant":true,"inputs":[],"name":"someName","payable":false,"stateMutability":"view","type":"function"}`, + chainReadingDefinitions: `"TestMethod":{"readType": 59}`, + expected: fmt.Errorf("invalid chainreading definition read type: 59"), + }) + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + cfg, err := chainReaderTestHelper{}.makeChainReaderConfigFromStrings(tc.abiInput, tc.chainReadingDefinitions) + assert.NoError(t, err) + if tc.expected == nil { + assert.NoError(t, validateChainReaderConfig(cfg)) + } else { + assert.ErrorContains(t, validateChainReaderConfig(cfg), tc.expected.Error()) + } + }) + } +} diff --git a/core/services/relay/evm/config_poller.go b/core/services/relay/evm/config_poller.go index fe39ed0e343..dc75fe037fe 100644 --- a/core/services/relay/evm/config_poller.go +++ b/core/services/relay/evm/config_poller.go @@ -17,6 +17,7 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index 088a69a2582..aea704adacf 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -54,6 +54,7 @@ type Relayer struct { mercuryPool wsrpc.Pool eventBroadcaster pg.EventBroadcaster pgCfg pg.QConfig + chainReader commontypes.ChainReader } type CSAETHKeystore interface { @@ -189,8 +190,7 @@ func (r *Relayer) NewMercuryProvider(rargs commontypes.RelayArgs, pargs commonty } transmitter := mercury.NewTransmitter(lggr, cw.ContractConfigTracker(), client, privKey.PublicKey, rargs.JobID, *relayConfig.FeedID, r.db, r.pgCfg, transmitterCodec) - chainReader := NewChainReader(r.chain.HeadTracker()) - return NewMercuryProvider(cw, transmitter, reportCodecV1, reportCodecV2, reportCodecV3, chainReader, lggr), nil + return NewMercuryProvider(cw, r.chainReader, NewMercuryChainReader(r.chain.HeadTracker()), transmitter, reportCodecV1, reportCodecV2, reportCodecV3, lggr), nil } func (r *Relayer) NewFunctionsProvider(rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (commontypes.FunctionsProvider, error) { @@ -502,6 +502,10 @@ func (r *Relayer) NewMedianProvider(rargs commontypes.RelayArgs, pargs commontyp if expectedChainID != r.chain.ID().String() { return nil, fmt.Errorf("internal error: chain id in spec does not match this relayer's chain: have %s expected %s", relayConfig.ChainID.String(), r.chain.ID().String()) } + if !common.IsHexAddress(relayOpts.ContractID) { + return nil, fmt.Errorf("invalid contractID %s, expected hex address", relayOpts.ContractID) + } + contractID := common.HexToAddress(relayOpts.ContractID) configWatcher, err := newConfigProvider(lggr, r.chain, relayOpts, r.eventBroadcaster) if err != nil { @@ -518,12 +522,26 @@ func (r *Relayer) NewMedianProvider(rargs commontypes.RelayArgs, pargs commontyp if err != nil { return nil, err } - return &medianProvider{ + + medianProvider := medianProvider{ configWatcher: configWatcher, reportCodec: reportCodec, contractTransmitter: contractTransmitter, medianContract: medianContract, - }, nil + } + + // allow fallback until chain reader is default and median contract is removed, but still log just in case + var chainReaderService commontypes.ChainReader + if relayConfig.ChainReader != nil { + if chainReaderService, err = NewChainReaderService(lggr, r.chain.LogPoller(), contractID, *relayConfig.ChainReader); err != nil { + return nil, err + } + } else { + lggr.Info("ChainReader missing from RelayConfig; falling back to internal MedianContract") + } + medianProvider.chainReader = chainReaderService + + return &medianProvider, nil } var _ commontypes.MedianProvider = (*medianProvider)(nil) @@ -533,8 +551,8 @@ type medianProvider struct { contractTransmitter ContractTransmitter reportCodec median.ReportCodec medianContract *medianContract - - ms services.MultiStart + chainReader commontypes.ChainReader + ms services.MultiStart } func (p *medianProvider) Name() string { @@ -582,3 +600,7 @@ func (p *medianProvider) OffchainConfigDigester() ocrtypes.OffchainConfigDigeste func (p *medianProvider) ContractConfigTracker() ocrtypes.ContractConfigTracker { return p.configWatcher.ContractConfigTracker() } + +func (p *medianProvider) ChainReader() commontypes.ChainReader { + return p.chainReader +} diff --git a/core/services/relay/evm/functions.go b/core/services/relay/evm/functions.go index 10e5d543b1a..f1d652fd6f8 100644 --- a/core/services/relay/evm/functions.go +++ b/core/services/relay/evm/functions.go @@ -18,6 +18,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txm "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" @@ -85,6 +86,10 @@ func (p *functionsProvider) Name() string { return p.configWatcher.Name() } +func (p *functionsProvider) ChainReader() commontypes.ChainReader { + return nil +} + func NewFunctionsProvider(chain legacyevm.Chain, rargs commontypes.RelayArgs, pargs commontypes.PluginArgs, lggr logger.Logger, ethKeystore keystore.Eth, pluginType functionsRelay.FunctionsPluginType) (evmRelayTypes.FunctionsProvider, error) { relayOpts := evmRelayTypes.NewRelayOpts(rargs) relayConfig, err := relayOpts.RelayConfig() diff --git a/core/services/relay/evm/functions/logpoller_wrapper.go b/core/services/relay/evm/functions/logpoller_wrapper.go index 95f45022ab3..e7f3a1a96af 100644 --- a/core/services/relay/evm/functions/logpoller_wrapper.go +++ b/core/services/relay/evm/functions/logpoller_wrapper.go @@ -11,6 +11,7 @@ import ( "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_coordinator" diff --git a/core/services/relay/evm/functions/offchain_config_digester.go b/core/services/relay/evm/functions/offchain_config_digester.go index b4467543d98..29547e794ce 100644 --- a/core/services/relay/evm/functions/offchain_config_digester.go +++ b/core/services/relay/evm/functions/offchain_config_digester.go @@ -10,13 +10,13 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2/chains/evmutil" "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - relaytypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" + evmRelayTypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) var ( - _ types.OffchainConfigDigester = &functionsOffchainConfigDigester{} - _ relaytypes.RouteUpdateSubscriber = &functionsOffchainConfigDigester{} - FunctionsDigestPrefix = types.ConfigDigestPrefixEVM + _ types.OffchainConfigDigester = &functionsOffchainConfigDigester{} + _ evmRelayTypes.RouteUpdateSubscriber = &functionsOffchainConfigDigester{} + FunctionsDigestPrefix = types.ConfigDigestPrefixEVM // In order to support multiple OCR plugins with a single jobspec & OCR2Base contract, each plugin must have a unique config digest. // This is accomplished by overriding the single config digest from the contract with a unique prefix for each plugin via this custom offchain digester & config poller. ThresholdDigestPrefix = types.ConfigDigestPrefix(7) diff --git a/core/services/relay/evm/median_test.go b/core/services/relay/evm/median_test.go new file mode 100644 index 00000000000..4286290d289 --- /dev/null +++ b/core/services/relay/evm/median_test.go @@ -0,0 +1,47 @@ +package evm + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm/mocks" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/logger" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +func TestNewMedianProvider(t *testing.T) { + lggr := logger.TestLogger(t) + + chain := mocks.NewChain(t) + chainID := testutils.NewRandomEVMChainID() + chain.On("ID").Return(chainID) + contractID := testutils.NewAddress() + relayer := Relayer{lggr: lggr, chain: chain} + + pargs := commontypes.PluginArgs{} + + t.Run("wrong chainID", func(t *testing.T) { + relayConfigBadChainID := evmtypes.RelayConfig{} + rc, err2 := json.Marshal(&relayConfigBadChainID) + rargs2 := commontypes.RelayArgs{ContractID: contractID.String(), RelayConfig: rc} + require.NoError(t, err2) + _, err2 = relayer.NewMedianProvider(rargs2, pargs) + assert.ErrorContains(t, err2, "chain id in spec does not match") + }) + + t.Run("invalid contractID", func(t *testing.T) { + relayConfig := evmtypes.RelayConfig{ChainID: utils.NewBig(chainID)} + rc, err2 := json.Marshal(&relayConfig) + require.NoError(t, err2) + rargsBadContractID := commontypes.RelayArgs{ContractID: "NotAContractID", RelayConfig: rc} + _, err2 = relayer.NewMedianProvider(rargsBadContractID, pargs) + assert.ErrorContains(t, err2, "invalid contractID") + }) +} diff --git a/core/services/relay/evm/mercury/v1/data_source.go b/core/services/relay/evm/mercury/v1/data_source.go index 429780ef2eb..ce48ec6cf94 100644 --- a/core/services/relay/evm/mercury/v1/data_source.go +++ b/core/services/relay/evm/mercury/v1/data_source.go @@ -67,7 +67,7 @@ type datasource struct { mu sync.RWMutex chEnhancedTelem chan<- ocrcommon.EnhancedTelemetryMercuryData - chainReader mercury.ChainReader + mercuryChainReader mercury.ChainReader fetcher Fetcher initialBlockNumber *int64 @@ -77,8 +77,8 @@ type datasource struct { var _ v1.DataSource = &datasource{} -func NewDataSource(orm types.DataSourceORM, pr pipeline.Runner, jb job.Job, spec pipeline.Spec, lggr logger.Logger, s ocrcommon.Saver, enhancedTelemChan chan ocrcommon.EnhancedTelemetryMercuryData, chainReader mercury.ChainReader, fetcher Fetcher, initialBlockNumber *int64, feedID mercuryutils.FeedID) *datasource { - return &datasource{pr, jb, spec, lggr, s, orm, reportcodec.ReportCodec{}, feedID, sync.RWMutex{}, enhancedTelemChan, chainReader, fetcher, initialBlockNumber, insufficientBlocksCount.WithLabelValues(feedID.String()), zeroBlocksCount.WithLabelValues(feedID.String())} +func NewDataSource(orm types.DataSourceORM, pr pipeline.Runner, jb job.Job, spec pipeline.Spec, lggr logger.Logger, s ocrcommon.Saver, enhancedTelemChan chan ocrcommon.EnhancedTelemetryMercuryData, mercuryChainReader mercury.ChainReader, fetcher Fetcher, initialBlockNumber *int64, feedID mercuryutils.FeedID) *datasource { + return &datasource{pr, jb, spec, lggr, s, orm, reportcodec.ReportCodec{}, feedID, sync.RWMutex{}, enhancedTelemChan, mercuryChainReader, fetcher, initialBlockNumber, insufficientBlocksCount.WithLabelValues(feedID.String()), zeroBlocksCount.WithLabelValues(feedID.String())} } type ErrEmptyLatestReport struct { @@ -292,7 +292,8 @@ func (ds *datasource) executeRun(ctx context.Context) (*pipeline.Run, pipeline.T } func (ds *datasource) setLatestBlocks(ctx context.Context, obs *v1types.Observation) error { - latestBlocks, err := ds.chainReader.LatestHeads(ctx, nBlocksObservation) + latestBlocks, err := ds.mercuryChainReader.LatestHeads(ctx, nBlocksObservation) + if err != nil { ds.lggr.Errorw("failed to read latest blocks", "error", err) return err diff --git a/core/services/relay/evm/mercury/v1/data_source_test.go b/core/services/relay/evm/mercury/v1/data_source_test.go index 72dc4327f19..ce0d71acc6f 100644 --- a/core/services/relay/evm/mercury/v1/data_source_test.go +++ b/core/services/relay/evm/mercury/v1/data_source_test.go @@ -15,7 +15,7 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" + mercurytypes "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" v1 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v1" commonmocks "github.com/smartcontractkit/chainlink/v2/common/mocks" @@ -33,7 +33,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/utils" ) -var _ mercury.ServerFetcher = &mockFetcher{} +var _ mercurytypes.ServerFetcher = &mockFetcher{} type mockFetcher struct { num *int64 @@ -71,10 +71,10 @@ func (m *mockORM) LatestReport(ctx context.Context, feedID [32]byte, qopts ...pg type mockChainReader struct { err error - obs []mercury.Head + obs []mercurytypes.Head } -func (m *mockChainReader) LatestHeads(context.Context, int) ([]mercury.Head, error) { +func (m *mockChainReader) LatestHeads(context.Context, int) ([]mercurytypes.Head, error) { return m.obs, m.err } @@ -118,7 +118,7 @@ func TestMercury_Observe(t *testing.T) { ds.spec = spec h := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) - ds.chainReader = evm.NewChainReader(h) + ds.mercuryChainReader = evm.NewMercuryChainReader(h) head := &evmtypes.Head{ Number: int64(rand.Int31()), @@ -210,7 +210,7 @@ func TestMercury_Observe(t *testing.T) { t.Run("if no current block available", func(t *testing.T) { h2 := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) h2.On("LatestChain").Return((*evmtypes.Head)(nil)) - ds.chainReader = evm.NewChainReader(h2) + ds.mercuryChainReader = evm.NewMercuryChainReader(h2) obs, err := ds.Observe(ctx, repts, true) assert.NoError(t, err) @@ -221,7 +221,7 @@ func TestMercury_Observe(t *testing.T) { }) }) - ds.chainReader = evm.NewChainReader(h) + ds.mercuryChainReader = evm.NewMercuryChainReader(h) t.Run("when fetchMaxFinalizedBlockNum=false", func(t *testing.T) { t.Run("when run execution fails, returns error", func(t *testing.T) { @@ -321,7 +321,7 @@ func TestMercury_Observe(t *testing.T) { t.Run("when chain length is zero", func(t *testing.T) { ht2 := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) ht2.On("LatestChain").Return((*evmtypes.Head)(nil)) - ds.chainReader = evm.NewChainReader(ht2) + ds.mercuryChainReader = evm.NewMercuryChainReader(ht2) obs, err := ds.Observe(ctx, repts, true) assert.NoError(t, err) @@ -346,7 +346,7 @@ func TestMercury_Observe(t *testing.T) { ht2 := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) ht2.On("LatestChain").Return(h6) - ds.chainReader = evm.NewChainReader(ht2) + ds.mercuryChainReader = evm.NewMercuryChainReader(ht2) obs, err := ds.Observe(ctx, repts, true) assert.NoError(t, err) @@ -369,7 +369,7 @@ func TestMercury_Observe(t *testing.T) { ht2 := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) ht2.On("LatestChain").Return(heads[len(heads)-1]) - ds.chainReader = evm.NewChainReader(ht2) + ds.mercuryChainReader = evm.NewMercuryChainReader(ht2) obs, err := ds.Observe(ctx, repts, true) assert.NoError(t, err) @@ -384,7 +384,7 @@ func TestMercury_Observe(t *testing.T) { }) t.Run("when chain reader returns an error", func(t *testing.T) { - ds.chainReader = &mockChainReader{ + ds.mercuryChainReader = &mockChainReader{ err: io.EOF, obs: nil, } @@ -414,7 +414,7 @@ func TestMercury_SetLatestBlocks(t *testing.T) { t.Run("returns head from headtracker if present", func(t *testing.T) { headTracker := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) headTracker.On("LatestChain").Return(&h, nil) - ds.chainReader = evm.NewChainReader(headTracker) + ds.mercuryChainReader = evm.NewMercuryChainReader(headTracker) obs := v1.Observation{} err := ds.setLatestBlocks(testutils.Context(t), &obs) @@ -433,7 +433,7 @@ func TestMercury_SetLatestBlocks(t *testing.T) { // This can happen in some cases e.g. RPC node is offline headTracker.On("LatestChain").Return((*evmtypes.Head)(nil)) - ds.chainReader = evm.NewChainReader(headTracker) + ds.mercuryChainReader = evm.NewChainReader(headTracker) obs := v1.Observation{} err := ds.setLatestBlocks(testutils.Context(t), &obs) diff --git a/core/services/relay/evm/mercury_provider.go b/core/services/relay/evm/mercury_provider.go index b6a2232da3f..2ff882efa6b 100644 --- a/core/services/relay/evm/mercury_provider.go +++ b/core/services/relay/evm/mercury_provider.go @@ -7,48 +7,50 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink-common/pkg/types" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" mercurytypes "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" v1 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v1" v2 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v2" v3 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v3" - relaymercury "github.com/smartcontractkit/chainlink-data-streams/mercury" + "github.com/smartcontractkit/chainlink-data-streams/mercury" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury" + evmmercury "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury" ) -var _ types.MercuryProvider = (*mercuryProvider)(nil) +var _ commontypes.MercuryProvider = (*mercuryProvider)(nil) type mercuryProvider struct { - configWatcher *configWatcher - transmitter mercury.Transmitter - reportCodecV1 v1.ReportCodec - reportCodecV2 v2.ReportCodec - reportCodecV3 v3.ReportCodec - chainReader mercurytypes.ChainReader - logger logger.Logger - - ms services.MultiStart + configWatcher *configWatcher + chainReader commontypes.ChainReader + transmitter evmmercury.Transmitter + reportCodecV1 v1.ReportCodec + reportCodecV2 v2.ReportCodec + reportCodecV3 v3.ReportCodec + mercuryChainReader mercurytypes.ChainReader + logger logger.Logger + ms services.MultiStart } func NewMercuryProvider( configWatcher *configWatcher, - transmitter mercury.Transmitter, + chainReader commontypes.ChainReader, + mercuryChainReader mercurytypes.ChainReader, + transmitter evmmercury.Transmitter, reportCodecV1 v1.ReportCodec, reportCodecV2 v2.ReportCodec, reportCodecV3 v3.ReportCodec, - chainReader mercurytypes.ChainReader, lggr logger.Logger, ) *mercuryProvider { return &mercuryProvider{ configWatcher, + chainReader, transmitter, reportCodecV1, reportCodecV2, reportCodecV3, - chainReader, + mercuryChainReader, lggr, services.MultiStart{}, } @@ -77,6 +79,10 @@ func (p *mercuryProvider) HealthReport() map[string]error { return report } +func (p *mercuryProvider) MercuryChainReader() mercurytypes.ChainReader { + return p.mercuryChainReader +} + func (p *mercuryProvider) ContractConfigTracker() ocrtypes.ContractConfigTracker { return p.configWatcher.ContractConfigTracker() } @@ -86,7 +92,7 @@ func (p *mercuryProvider) OffchainConfigDigester() ocrtypes.OffchainConfigDigest } func (p *mercuryProvider) OnchainConfigCodec() mercurytypes.OnchainConfigCodec { - return relaymercury.StandardOnchainConfigCodec{} + return mercury.StandardOnchainConfigCodec{} } func (p *mercuryProvider) ReportCodecV1() v1.ReportCodec { @@ -109,23 +115,27 @@ func (p *mercuryProvider) MercuryServerFetcher() mercurytypes.ServerFetcher { return p.transmitter } -func (p *mercuryProvider) ChainReader() mercurytypes.ChainReader { +func (p *mercuryProvider) ChainReader() commontypes.ChainReader { return p.chainReader } -var _ mercurytypes.ChainReader = (*chainReader)(nil) +var _ mercurytypes.ChainReader = (*mercuryChainReader)(nil) -type chainReader struct { +type mercuryChainReader struct { tracker httypes.HeadTracker } func NewChainReader(h httypes.HeadTracker) mercurytypes.ChainReader { - return &chainReader{ + return &mercuryChainReader{h} +} + +func NewMercuryChainReader(h httypes.HeadTracker) mercurytypes.ChainReader { + return &mercuryChainReader{ tracker: h, } } -func (r *chainReader) LatestHeads(ctx context.Context, k int) ([]mercurytypes.Head, error) { +func (r *mercuryChainReader) LatestHeads(ctx context.Context, k int) ([]mercurytypes.Head, error) { evmBlocks := r.tracker.LatestChain().AsSlice(k) if len(evmBlocks) == 0 { return nil, nil diff --git a/core/services/relay/evm/ocr2keeper.go b/core/services/relay/evm/ocr2keeper.go index 3b3bfeb652d..abc03c7abb1 100644 --- a/core/services/relay/evm/ocr2keeper.go +++ b/core/services/relay/evm/ocr2keeper.go @@ -130,6 +130,10 @@ func (c *ocr2keeperProvider) ContractTransmitter() ocrtypes.ContractTransmitter return c.contractTransmitter } +func (c *ocr2keeperProvider) ChainReader() commontypes.ChainReader { + return nil +} + func newOCR2KeeperConfigProvider(lggr logger.Logger, chain legacyevm.Chain, rargs commontypes.RelayArgs) (*configWatcher, error) { var relayConfig types.RelayConfig err := json.Unmarshal(rargs.RelayConfig, &relayConfig) diff --git a/core/services/relay/evm/ocr2vrf.go b/core/services/relay/evm/ocr2vrf.go index b7a2220588b..39d0503b8b1 100644 --- a/core/services/relay/evm/ocr2vrf.go +++ b/core/services/relay/evm/ocr2vrf.go @@ -110,6 +110,10 @@ func (c *dkgProvider) ContractTransmitter() ocrtypes.ContractTransmitter { return c.contractTransmitter } +func (c *dkgProvider) ChainReader() commontypes.ChainReader { + return nil +} + type ocr2vrfProvider struct { *configWatcher contractTransmitter ContractTransmitter @@ -119,6 +123,10 @@ func (c *ocr2vrfProvider) ContractTransmitter() ocrtypes.ContractTransmitter { return c.contractTransmitter } +func (c *ocr2vrfProvider) ChainReader() commontypes.ChainReader { + return nil +} + func newOCR2VRFConfigProvider(lggr logger.Logger, chain legacyevm.Chain, rargs commontypes.RelayArgs) (*configWatcher, error) { var relayConfig types.RelayConfig err := json.Unmarshal(rargs.RelayConfig, &relayConfig) diff --git a/core/services/relay/evm/types/types.go b/core/services/relay/evm/types/types.go index d2edef8b118..24afb65c55a 100644 --- a/core/services/relay/evm/types/types.go +++ b/core/services/relay/evm/types/types.go @@ -20,11 +20,38 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/utils" ) +type ChainReaderConfig struct { + // ChainContractReaders key is contract name + ChainContractReaders map[string]ChainContractReader `json:"chainContractReaders"` +} + +type ChainContractReader struct { + ContractABI string `json:"contractABI"` + // ChainReaderDefinitions key is chainAgnostic read name. + ChainReaderDefinitions map[string]ChainReaderDefinition `json:"chainReaderDefinitions"` +} + +type ChainReaderDefinition struct { + ChainSpecificName string `json:"chainSpecificName"` // chain specific contract method name or event type. + Params map[string]any `json:"params"` + ReturnValues []string `json:"returnValues"` + CacheEnabled bool `json:"cacheEnabled"` + ReadType ReadType `json:"readType"` +} + +type ReadType int64 + +const ( + Method ReadType = 0 + Event ReadType = 1 +) + type RelayConfig struct { - ChainID *utils.Big `json:"chainID"` - FromBlock uint64 `json:"fromBlock"` - EffectiveTransmitterID null.String `json:"effectiveTransmitterID"` - ConfigContractAddress *common.Address `json:"configContractAddress"` + ChainID *utils.Big `json:"chainID"` + FromBlock uint64 `json:"fromBlock"` + EffectiveTransmitterID null.String `json:"effectiveTransmitterID"` + ConfigContractAddress *common.Address `json:"configContractAddress"` + ChainReader *ChainReaderConfig `json:"chainReader"` // Contract-specific SendingKeys pq.StringArray `json:"sendingKeys"` diff --git a/core/services/relay/grpc_provider_server_test.go b/core/services/relay/grpc_provider_server_test.go index 6aff32f5e32..72bbbca0f44 100644 --- a/core/services/relay/grpc_provider_server_test.go +++ b/core/services/relay/grpc_provider_server_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" ) @@ -17,7 +18,7 @@ func TestProviderServer(t *testing.T) { lggr := logger.TestLogger(t) _, err := NewProviderServer(mp, "unsupported-type", lggr) - require.Error(t, err) + require.ErrorContains(t, err, "unsupported-type") ps, err := NewProviderServer(staticMedianProvider{}, types.Median, lggr) require.NoError(t, err) diff --git a/core/services/relay/relay_test.go b/core/services/relay/relay_test.go index 18a7b1b44ea..d23895699df 100644 --- a/core/services/relay/relay_test.go +++ b/core/services/relay/relay_test.go @@ -10,6 +10,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" ) diff --git a/go.mod b/go.mod index 0cbd2f16d1c..2ac067959ef 100644 --- a/go.mod +++ b/go.mod @@ -66,12 +66,12 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4 + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/libocr v0.0.0-20231130143053-c5102a9c0fb7 github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 @@ -171,6 +171,7 @@ require ( github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect + github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-ldap/ldap/v3 v3.4.5 diff --git a/go.sum b/go.sum index 96e5c069454..b7305210e05 100644 --- a/go.sum +++ b/go.sum @@ -440,6 +440,8 @@ github.com/go-faster/errors v0.6.1/go.mod h1:5MGV2/2T9yvlrbhe9pD9LO5Z/2zCSq2T8j+ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 h1:ymLjT4f35nQbASLnvxEde4XOBL+Sn7rFuV+FOJqkljg= +github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0/go.mod h1:6daplAwHHGbUGib4990V3Il26O0OC4aRyvewaaAihaA= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= @@ -1215,18 +1217,18 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c h1:YFyo0pCmKkpB4EOSykCZFueRXNxQ7OhKiCnhoVIzydo= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4 h1:2LC5HtHkAm7I1h5j4Uz0KTX+h4fo4z/5NoAARSF4ZVA= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e h1:xvqffqFec2HkEcUKrCkm4FDJRnn/+gHmvrE/dz3Zlw8= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e/go.mod h1:soVgcl4CbfR6hC9UptjuCQhz19HJaFEjwnOpiySkxg0= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1/go.mod h1:GuPvyXryvbiUZIHmPeLBz4L+yJKeyGUjrDfd1KNne+o= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d/go.mod h1:YPAfLNowdBwiKiYOwgwtbJHi8AJWbcxkbOY0ItAvkfc= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 h1:DudPr8ZNMEVgDwHBvnrpvK96JrGcGCG+LLBnlqaPGnE= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3/go.mod h1:UfW7/PZKon+iDEHtrHOfvMnS5GfYOW/SdMZ6P97rPEk= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 h1:yxaHuDTtj2xxtsR8b+LJw8xDvyr6v/F6GP3InsP4wPI= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664/go.mod h1:3Fa+HQTZ3R3fPC0hUqugvoo+NEeo8Y4J2WOnQfi7O34= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e h1:/tCHhoAJM+ittEHPZTtJsAgXmYujKiDW0ub9HXW9qtY= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e/go.mod h1:9YIi413QRRytafTzpWm+Z+5NWBNxSqokhKyeEZ3ynlA= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725 h1:NbhPVwxx+53WN/Uld1V6c4iLgoGvUYFOsVd2kfcexe8= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725/go.mod h1:vHrPBipRL52NdPp77KXNU2k1IoCUa1B33N9otZQPYko= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= diff --git a/integration-tests/docker/test_env/test_env.go b/integration-tests/docker/test_env/test_env.go index 030a3ae9622..b1b179badc5 100644 --- a/integration-tests/docker/test_env/test_env.go +++ b/integration-tests/docker/test_env/test_env.go @@ -206,6 +206,15 @@ func (te *CLClusterTestEnv) Cleanup() error { te.logWhetherAllContainersAreRunning() + // Getting the absolute path + wd, err := os.Getwd() + if err != nil { + return err + } + + wd = filepath.Join(wd, "logs") + te.l.Info().Str("Working dir", wd).Msg("Would write test logs here") + // TODO: This is an imperfect and temporary solution, see TT-590 for a more sustainable solution // Collect logs if the test fails, or if we just want them if te.t.Failed() || os.Getenv("TEST_LOG_COLLECT") == "true" { @@ -285,7 +294,14 @@ func (te *CLClusterTestEnv) collectTestLogs() error { return err } - te.l.Info().Str("Logs Location", folder).Msg("Wrote test logs") + // Getting the absolute path + wd, err := os.Getwd() + if err != nil { + return err + } + absolutePath := filepath.Join(wd, folder) + + te.l.Info().Str("Logs absolute Location", absolutePath).Msg("Wrote test logs") return nil } diff --git a/integration-tests/go.mod b/integration-tests/go.mod index a161e67941d..01d7c4dbef9 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -24,7 +24,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4 github.com/smartcontractkit/chainlink-testing-framework v1.20.0 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 @@ -160,6 +160,7 @@ require ( github.com/gin-gonic/gin v1.9.1 // indirect github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect github.com/go-errors/errors v1.4.2 // indirect + github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-ldap/ldap/v3 v3.4.5 // indirect @@ -351,11 +352,11 @@ require ( github.com/shopspring/decimal v1.3.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 // indirect + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e // indirect github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 // indirect - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e // indirect + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/wsrpc v0.7.2 // indirect github.com/soheilhy/cmux v0.1.5 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 0392afe287a..f3d707d18ec 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -520,6 +520,8 @@ github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3Bop github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 h1:ymLjT4f35nQbASLnvxEde4XOBL+Sn7rFuV+FOJqkljg= +github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0/go.mod h1:6daplAwHHGbUGib4990V3Il26O0OC4aRyvewaaAihaA= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= @@ -1505,18 +1507,18 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c h1:YFyo0pCmKkpB4EOSykCZFueRXNxQ7OhKiCnhoVIzydo= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231204152334-1f32103bbb4c/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4 h1:2LC5HtHkAm7I1h5j4Uz0KTX+h4fo4z/5NoAARSF4ZVA= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231205033838-dfac15e672d4/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e h1:xvqffqFec2HkEcUKrCkm4FDJRnn/+gHmvrE/dz3Zlw8= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231206164210-03f8b219402e/go.mod h1:soVgcl4CbfR6hC9UptjuCQhz19HJaFEjwnOpiySkxg0= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1/go.mod h1:GuPvyXryvbiUZIHmPeLBz4L+yJKeyGUjrDfd1KNne+o= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d/go.mod h1:YPAfLNowdBwiKiYOwgwtbJHi8AJWbcxkbOY0ItAvkfc= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 h1:DudPr8ZNMEVgDwHBvnrpvK96JrGcGCG+LLBnlqaPGnE= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3/go.mod h1:UfW7/PZKon+iDEHtrHOfvMnS5GfYOW/SdMZ6P97rPEk= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 h1:yxaHuDTtj2xxtsR8b+LJw8xDvyr6v/F6GP3InsP4wPI= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664/go.mod h1:3Fa+HQTZ3R3fPC0hUqugvoo+NEeo8Y4J2WOnQfi7O34= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e h1:/tCHhoAJM+ittEHPZTtJsAgXmYujKiDW0ub9HXW9qtY= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231206154215-ec1718b7df3e/go.mod h1:9YIi413QRRytafTzpWm+Z+5NWBNxSqokhKyeEZ3ynlA= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725 h1:NbhPVwxx+53WN/Uld1V6c4iLgoGvUYFOsVd2kfcexe8= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231205180940-ea2e3e916725/go.mod h1:vHrPBipRL52NdPp77KXNU2k1IoCUa1B33N9otZQPYko= github.com/smartcontractkit/chainlink-testing-framework v1.20.0 h1:gQPQRKJuMh6QTAIMkqZ7v5WkjEmbcoMIX/V6WPVrvuI= github.com/smartcontractkit/chainlink-testing-framework v1.20.0/go.mod h1:+FVgkz6phTc+piVT57AcQfr3I8xvZgZ1lOpRPOu/dLQ= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= diff --git a/plugins/medianpoc/plugin_test.go b/plugins/medianpoc/plugin_test.go index 0d6c0360e43..ba06d5ea462 100644 --- a/plugins/medianpoc/plugin_test.go +++ b/plugins/medianpoc/plugin_test.go @@ -7,9 +7,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" @@ -72,6 +71,10 @@ func (p provider) OnchainConfigCodec() median.OnchainConfigCodec { return mockOnchainConfigCodec{} } +func (p provider) ChainReader() types.ChainReader { + return nil +} + func TestNewPlugin(t *testing.T) { lggr := logger.TestLogger(t) p := NewPlugin(lggr)