Skip to content

Commit

Permalink
Support "dummy" relayer type (#13583)
Browse files Browse the repository at this point in the history
* Implement new relayer type "dummy"

This allows testing of plugins without needing a real connection to any
chain.

* Add changeset

* Spelling fix

* Remove useless file

* Update core/services/relay/dummy/llo_provider.go

Co-authored-by: Jordan Krage <[email protected]>

* Fix typo

* Move onchainconfig into llo package

---------

Co-authored-by: Jordan Krage <[email protected]>
  • Loading branch information
samsondav and jmank88 authored Jun 25, 2024
1 parent 1d640a2 commit 8ccaa14
Show file tree
Hide file tree
Showing 24 changed files with 661 additions and 85 deletions.
7 changes: 7 additions & 0 deletions .changeset/tender-carpets-cry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"chainlink": patch
---

Add new relayer type "dummy" for testing.

#added
5 changes: 5 additions & 0 deletions core/chains/evm/logpoller/log_poller.go
Original file line number Diff line number Diff line change
Expand Up @@ -1021,6 +1021,11 @@ func (lp *logPoller) latestBlocks(ctx context.Context) (*evmtypes.Head, int64, e
if err != nil {
return nil, 0, err
}
if latestBlock == nil {
// Shouldn't happen with a real client, but still better rather to
// return error than panic
return nil, 0, errors.New("latest block is nil")
}
// If chain has fewer blocks than finalityDepth, return 0
return latestBlock, mathutil.Max(latestBlock.Number-lp.finalityDepth, 0), nil
}
Expand Down
2 changes: 1 addition & 1 deletion core/cmd/shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G
}
// evm always enabled for backward compatibility
// TODO BCF-2510 this needs to change in order to clear the path for EVM extraction
initOps := []chainlink.CoreRelayerChainInitFunc{chainlink.InitEVM(ctx, relayerFactory, evmFactoryCfg)}
initOps := []chainlink.CoreRelayerChainInitFunc{chainlink.InitDummy(ctx, relayerFactory), chainlink.InitEVM(ctx, relayerFactory, evmFactoryCfg)}

if cfg.CosmosEnabled() {
cosmosCfg := chainlink.CosmosFactoryConfig{
Expand Down
2 changes: 1 addition & 1 deletion core/internal/cltest/cltest.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ func NewApplicationWithConfig(t testing.TB, cfg chainlink.GeneralConfig, flagsAn

testCtx := testutils.Context(t)
// evm alway enabled for backward compatibility
initOps := []chainlink.CoreRelayerChainInitFunc{chainlink.InitEVM(testCtx, relayerFactory, evmOpts)}
initOps := []chainlink.CoreRelayerChainInitFunc{chainlink.InitDummy(testCtx, relayerFactory), chainlink.InitEVM(testCtx, relayerFactory, evmOpts)}

if cfg.CosmosEnabled() {
cosmosCfg := chainlink.CosmosFactoryConfig{
Expand Down
4 changes: 3 additions & 1 deletion core/internal/testutils/testutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,15 +367,17 @@ func RequireLogMessage(t *testing.T, observedLogs *observer.ObservedLogs, msg st
//
// observedZapCore, observedLogs := observer.New(zap.DebugLevel)
// lggr := logger.TestLogger(t, observedZapCore)
func WaitForLogMessage(t *testing.T, observedLogs *observer.ObservedLogs, msg string) {
func WaitForLogMessage(t *testing.T, observedLogs *observer.ObservedLogs, msg string) (le observer.LoggedEntry) {
AssertEventually(t, func() bool {
for _, l := range observedLogs.All() {
if strings.Contains(l.Message, msg) {
le = l
return true
}
}
return false
})
return
}

// WaitForLogMessageCount waits until at least count log message containing the
Expand Down
24 changes: 24 additions & 0 deletions core/services/chainlink/relayer_chain_interoperators.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,19 @@ type ChainsNodesStatuser interface {

var _ RelayerChainInteroperators = &CoreRelayerChainInteroperators{}

type DummyFactory interface {
NewDummy(config DummyFactoryConfig) (loop.Relayer, error)
}

// CoreRelayerChainInteroperators implements [RelayerChainInteroperators]
// as needed for the core [chainlink.Application]
type CoreRelayerChainInteroperators struct {
mu sync.Mutex
loopRelayers map[types.RelayID]loop.Relayer
legacyChains legacyChains

dummyFactory DummyFactory

// we keep an explicit list of services because the legacy implementations have more than
// just the relayer service
srvs []services.ServiceCtx
Expand All @@ -98,6 +104,14 @@ func NewCoreRelayerChainInteroperators(initFuncs ...CoreRelayerChainInitFunc) (*
// CoreRelayerChainInitFunc is a hook in the constructor to create relayers from a factory.
type CoreRelayerChainInitFunc func(op *CoreRelayerChainInteroperators) error

// InitDummy instantiates a dummy relayer
func InitDummy(ctx context.Context, factory RelayerFactory) CoreRelayerChainInitFunc {
return func(op *CoreRelayerChainInteroperators) error {
op.dummyFactory = &factory
return nil
}
}

// InitEVM is a option for instantiating evm relayers
func InitEVM(ctx context.Context, factory RelayerFactory, config EVMFactoryConfig) CoreRelayerChainInitFunc {
return func(op *CoreRelayerChainInteroperators) (err error) {
Expand Down Expand Up @@ -178,6 +192,16 @@ func (rs *CoreRelayerChainInteroperators) Get(id types.RelayID) (loop.Relayer, e
defer rs.mu.Unlock()
lr, exist := rs.loopRelayers[id]
if !exist {
// lazily create dummy relayers
if id.Network == "dummy" {
var err error
lr, err = rs.dummyFactory.NewDummy(DummyFactoryConfig{id.ChainID})
if err != nil {
return nil, err
}
rs.loopRelayers[id] = lr
return lr, nil
}
return nil, fmt.Errorf("%w: %s", ErrNoSuchRelayer, id)
}
return lr, nil
Expand Down
6 changes: 6 additions & 0 deletions core/services/chainlink/relayer_chain_interoperators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,10 @@ func TestCoreRelayerChainInteroperators(t *testing.T) {
expectedStarknetNodeCnt int
expectedStarknetRelayerIds []types.RelayID

expectedDummyChainCnt int
expectedDummyNodeCnt int
expectedDummyRelayerIds []types.RelayID

expectedCosmosChainCnt int
expectedCosmosNodeCnt int
expectedCosmosRelayerIds []types.RelayID
Expand Down Expand Up @@ -368,6 +372,8 @@ func TestCoreRelayerChainInteroperators(t *testing.T) {
expectedChainCnt, expectedNodeCnt = tt.expectedSolanaChainCnt, tt.expectedSolanaNodeCnt
case relay.NetworkStarkNet:
expectedChainCnt, expectedNodeCnt = tt.expectedStarknetChainCnt, tt.expectedStarknetNodeCnt
case relay.NetworkDummy:
expectedChainCnt, expectedNodeCnt = tt.expectedDummyChainCnt, tt.expectedDummyNodeCnt
case relay.NetworkAptos:
t.Skip("aptos doesn't need a CoreRelayerChainInteroperator")

Expand Down
9 changes: 9 additions & 0 deletions core/services/chainlink/relayer_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/logger"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore"
corerelay "github.com/smartcontractkit/chainlink/v2/core/services/relay"
"github.com/smartcontractkit/chainlink/v2/core/services/relay/dummy"
evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm"
"github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc"
"github.com/smartcontractkit/chainlink/v2/plugins"
Expand All @@ -39,6 +40,14 @@ type RelayerFactory struct {
CapabilitiesRegistry *capabilities.Registry
}

type DummyFactoryConfig struct {
ChainID string
}

func (r *RelayerFactory) NewDummy(config DummyFactoryConfig) (loop.Relayer, error) {
return dummy.NewRelayer(r.Logger, config.ChainID), nil
}

type EVMFactoryConfig struct {
legacyevm.ChainOpts
evmrelay.CSAETHKeystore
Expand Down
6 changes: 2 additions & 4 deletions core/services/llo/bm/dummy_transmitter.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package bm

import (
"context"
"crypto/ed25519"
"fmt"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
Expand Down Expand Up @@ -37,10 +35,10 @@ type transmitter struct {
fromAccount string
}

func NewTransmitter(lggr logger.Logger, fromAccount ed25519.PublicKey) Transmitter {
func NewTransmitter(lggr logger.Logger, fromAccount string) Transmitter {
return &transmitter{
lggr.Named("DummyTransmitter"),
fmt.Sprintf("%x", fromAccount),
fromAccount,
}
}

Expand Down
3 changes: 1 addition & 2 deletions core/services/llo/bm/dummy_transmitter_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package bm

import (
"crypto/ed25519"
"testing"

"github.com/stretchr/testify/require"
Expand All @@ -19,7 +18,7 @@ import (

func Test_DummyTransmitter(t *testing.T) {
lggr, observedLogs := logger.TestLoggerObserved(t, zapcore.DebugLevel)
tr := NewTransmitter(lggr, ed25519.PublicKey("dummy"))
tr := NewTransmitter(lggr, "dummy")

servicetest.Run(t, tr)

Expand Down
4 changes: 4 additions & 0 deletions core/services/llo/data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ func (d *dataSource) Observe(ctx context.Context, streamIDs map[llotypes.StreamI
sv := make(llo.StreamValues)
var mu sync.Mutex

d.lggr.Debugw("Observing streams", "streamIDs", streamIDs)

for streamID := range streamIDs {
go func(streamID llotypes.StreamID) {
defer wg.Done()
Expand Down Expand Up @@ -99,5 +101,7 @@ func (d *dataSource) Observe(ctx context.Context, streamIDs map[llotypes.StreamI

wg.Wait()

d.lggr.Debugw("Observed streams", "streamIDs", streamIDs, "values", sv)

return sv, nil
}
21 changes: 21 additions & 0 deletions core/services/llo/onchain_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package llo

type OnchainConfig struct{}

type OnchainConfigCodec interface {
Encode(OnchainConfig) ([]byte, error)
Decode([]byte) (OnchainConfig, error)
}

var _ OnchainConfigCodec = &JSONOnchainConfigCodec{}

// TODO: Replace this with protobuf, if it is actually used for something
type JSONOnchainConfigCodec struct{}

func (c *JSONOnchainConfigCodec) Encode(OnchainConfig) ([]byte, error) {
return nil, nil
}

func (c *JSONOnchainConfigCodec) Decode([]byte) (OnchainConfig, error) {
return OnchainConfig{}, nil
}
15 changes: 6 additions & 9 deletions core/services/ocr2/delegate.go
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,7 @@ func (d *Delegate) newServicesMercury(
return nil, ErrJobSpecNoRelayer{Err: err, PluginName: "mercury"}
}
if rid.Network != relay.NetworkEVM {
return nil, fmt.Errorf("mercury services: expected EVM relayer got %s", rid.Network)
return nil, fmt.Errorf("mercury services: expected EVM relayer got %q", rid.Network)
}
relayer, err := d.RelayGetter.Get(rid)
if err != nil {
Expand Down Expand Up @@ -895,9 +895,6 @@ func (d *Delegate) newServicesLLO(
if err != nil {
return nil, ErrJobSpecNoRelayer{Err: err, PluginName: "streams"}
}
if rid.Network != relay.NetworkEVM {
return nil, fmt.Errorf("streams services: expected EVM relayer got %s", rid.Network)
}
relayer, err := d.RelayGetter.Get(rid)
if err != nil {
return nil, ErrRelayNotEnabled{Err: err, Relay: spec.Relay, PluginName: "streams"}
Expand Down Expand Up @@ -1079,7 +1076,7 @@ func (d *Delegate) newServicesDKG(
return nil, ErrJobSpecNoRelayer{Err: err, PluginName: "DKG"}
}
if rid.Network != relay.NetworkEVM {
return nil, fmt.Errorf("DKG services: expected EVM relayer got %s", rid.Network)
return nil, fmt.Errorf("DKG services: expected EVM relayer got %q", rid.Network)
}

chain, err2 := d.legacyChains.Get(rid.ChainID)
Expand Down Expand Up @@ -1144,7 +1141,7 @@ func (d *Delegate) newServicesOCR2VRF(
return nil, ErrJobSpecNoRelayer{Err: err, PluginName: "VRF"}
}
if rid.Network != relay.NetworkEVM {
return nil, fmt.Errorf("VRF services: expected EVM relayer got %s", rid.Network)
return nil, fmt.Errorf("VRF services: expected EVM relayer got %q", rid.Network)
}
chain, err2 := d.legacyChains.Get(rid.ChainID)
if err2 != nil {
Expand Down Expand Up @@ -1358,7 +1355,7 @@ func (d *Delegate) newServicesOCR2Keepers21(
return nil, ErrJobSpecNoRelayer{Err: err, PluginName: "keeper2"}
}
if rid.Network != relay.NetworkEVM {
return nil, fmt.Errorf("keeper2 services: expected EVM relayer got %s", rid.Network)
return nil, fmt.Errorf("keeper2 services: expected EVM relayer got %q", rid.Network)
}

transmitterID := spec.TransmitterID.String
Expand Down Expand Up @@ -1511,7 +1508,7 @@ func (d *Delegate) newServicesOCR2Keepers20(
return nil, ErrJobSpecNoRelayer{Err: err, PluginName: "keepers2.0"}
}
if rid.Network != relay.NetworkEVM {
return nil, fmt.Errorf("keepers2.0 services: expected EVM relayer got %s", rid.Network)
return nil, fmt.Errorf("keepers2.0 services: expected EVM relayer got %q", rid.Network)
}
chain, err2 := d.legacyChains.Get(rid.ChainID)
if err2 != nil {
Expand Down Expand Up @@ -1639,7 +1636,7 @@ func (d *Delegate) newServicesOCR2Functions(
return nil, ErrJobSpecNoRelayer{Err: err, PluginName: "functions"}
}
if rid.Network != relay.NetworkEVM {
return nil, fmt.Errorf("functions services: expected EVM relayer got %s", rid.Network)
return nil, fmt.Errorf("functions services: expected EVM relayer got %q", rid.Network)
}
chain, err := d.legacyChains.Get(rid.ChainID)
if err != nil {
Expand Down
6 changes: 5 additions & 1 deletion core/services/ocr2/plugins/llo/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ type PluginConfig struct {
ChannelDefinitionsContractFromBlock int64 `json:"channelDefinitionsContractFromBlock" toml:"channelDefinitionsContractFromBlock"`

// NOTE: ChannelDefinitions is an override.
// If Channe}lDefinitions is specified, values for
// If ChannelDefinitions is specified, values for
// ChannelDefinitionsContractAddress and
// ChannelDefinitionsContractFromBlock will be ignored
ChannelDefinitions string `json:"channelDefinitions" toml:"channelDefinitions"`
Expand All @@ -41,6 +41,10 @@ type PluginConfig struct {
KeyBundleIDs map[string]string `json:"keyBundleIDs" toml:"keyBundleIDs"`
}

func (p *PluginConfig) Unmarshal(data []byte) error {
return json.Unmarshal(data, p)
}

func (p PluginConfig) Validate() (merr error) {
if p.RawServerURL == "" {
merr = errors.New("llo: ServerURL must be specified")
Expand Down
Loading

0 comments on commit 8ccaa14

Please sign in to comment.