From 659b75adf96f294bbd784747deb8cfda3a1b8295 Mon Sep 17 00:00:00 2001 From: Makram Date: Wed, 2 Oct 2024 19:30:21 +0400 Subject: [PATCH] [CCIP-2917] core/capabilities/ccip: use relayers instead of legacyevm (#14611) * core/capabilities/ccip: use relayers instead of legacyevm * fix lint * address CR comments * fix * goimports --- .../ccip/configs/evm/chain_writer.go | 36 ++-- .../ccip/configs/evm/chain_writer_test.go | 103 +++++++++ .../ccip/configs/evm/contract_reader.go | 36 ++-- core/capabilities/ccip/delegate.go | 64 +++--- .../capabilities/ccip/oraclecreator/plugin.go | 195 ++++++++++-------- core/services/chainlink/application.go | 2 +- 6 files changed, 278 insertions(+), 158 deletions(-) create mode 100644 core/capabilities/ccip/configs/evm/chain_writer_test.go diff --git a/core/capabilities/ccip/configs/evm/chain_writer.go b/core/capabilities/ccip/configs/evm/chain_writer.go index 9bc377ba23e..f88bfce937b 100644 --- a/core/capabilities/ccip/configs/evm/chain_writer.go +++ b/core/capabilities/ccip/configs/evm/chain_writer.go @@ -1,7 +1,6 @@ package evm import ( - "encoding/json" "fmt" "github.com/ethereum/go-ethereum/accounts/abi" @@ -19,28 +18,29 @@ var ( offrampABI = evmtypes.MustGetABI(offramp.OffRampABI) ) -func MustChainWriterConfig( - fromAddress common.Address, - maxGasPrice *assets.Wei, - commitGasLimit, - execBatchGasLimit uint64, -) []byte { - rawConfig := ChainWriterConfigRaw(fromAddress, maxGasPrice, commitGasLimit, execBatchGasLimit) - encoded, err := json.Marshal(rawConfig) - if err != nil { - panic(fmt.Errorf("failed to marshal ChainWriterConfig: %w", err)) - } - - return encoded -} - // ChainWriterConfigRaw returns a ChainWriterConfig that can be used to transmit commit and execute reports. func ChainWriterConfigRaw( fromAddress common.Address, maxGasPrice *assets.Wei, commitGasLimit, execBatchGasLimit uint64, -) evmrelaytypes.ChainWriterConfig { +) (evmrelaytypes.ChainWriterConfig, error) { + if fromAddress == common.HexToAddress("0x0") { + return evmrelaytypes.ChainWriterConfig{}, fmt.Errorf("fromAddress cannot be zero") + } + if maxGasPrice == nil { + return evmrelaytypes.ChainWriterConfig{}, fmt.Errorf("maxGasPrice cannot be nil") + } + if maxGasPrice.Cmp(assets.NewWeiI(0)) <= 0 { + return evmrelaytypes.ChainWriterConfig{}, fmt.Errorf("maxGasPrice must be greater than zero") + } + if commitGasLimit == 0 { + return evmrelaytypes.ChainWriterConfig{}, fmt.Errorf("commitGasLimit must be greater than zero") + } + if execBatchGasLimit == 0 { + return evmrelaytypes.ChainWriterConfig{}, fmt.Errorf("execBatchGasLimit must be greater than zero") + } + return evmrelaytypes.ChainWriterConfig{ Contracts: map[string]*evmrelaytypes.ContractConfig{ consts.ContractNameOffRamp: { @@ -60,7 +60,7 @@ func ChainWriterConfigRaw( }, }, MaxGasPrice: maxGasPrice, - } + }, nil } // mustGetMethodName panics if the method name is not found in the provided ABI. diff --git a/core/capabilities/ccip/configs/evm/chain_writer_test.go b/core/capabilities/ccip/configs/evm/chain_writer_test.go new file mode 100644 index 00000000000..e24863866cc --- /dev/null +++ b/core/capabilities/ccip/configs/evm/chain_writer_test.go @@ -0,0 +1,103 @@ +package evm_test + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/assert" + + "github.com/smartcontractkit/chainlink-ccip/pkg/consts" + "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/configs/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" +) + +func TestChainWriterConfigRaw(t *testing.T) { + tests := []struct { + name string + fromAddress common.Address + maxGasPrice *assets.Wei + commitGasLimit uint64 + execBatchGasLimit uint64 + expectedError string + }{ + { + name: "valid input", + fromAddress: common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678"), + maxGasPrice: assets.NewWeiI(1000000000), + commitGasLimit: 21000, + execBatchGasLimit: 42000, + expectedError: "", + }, + { + name: "zero fromAddress", + fromAddress: common.HexToAddress("0x0"), + maxGasPrice: assets.NewWeiI(1000000000), + commitGasLimit: 21000, + execBatchGasLimit: 42000, + expectedError: "fromAddress cannot be zero", + }, + { + name: "nil maxGasPrice", + fromAddress: common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678"), + maxGasPrice: nil, + commitGasLimit: 21000, + execBatchGasLimit: 42000, + expectedError: "maxGasPrice cannot be nil", + }, + { + name: "zero maxGasPrice", + fromAddress: common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678"), + maxGasPrice: assets.NewWeiI(0), + commitGasLimit: 21000, + execBatchGasLimit: 42000, + expectedError: "maxGasPrice must be greater than zero", + }, + { + name: "negative maxGasPrice", + fromAddress: common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678"), + maxGasPrice: assets.NewWeiI(-1), + commitGasLimit: 21000, + execBatchGasLimit: 42000, + expectedError: "maxGasPrice must be greater than zero", + }, + { + name: "zero commitGasLimit", + fromAddress: common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678"), + maxGasPrice: assets.NewWeiI(1000000000), + commitGasLimit: 0, + execBatchGasLimit: 42000, + expectedError: "commitGasLimit must be greater than zero", + }, + { + name: "zero execBatchGasLimit", + fromAddress: common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678"), + maxGasPrice: assets.NewWeiI(1000000000), + commitGasLimit: 21000, + execBatchGasLimit: 0, + expectedError: "execBatchGasLimit must be greater than zero", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + config, err := evm.ChainWriterConfigRaw(tt.fromAddress, tt.maxGasPrice, tt.commitGasLimit, tt.execBatchGasLimit) + if tt.expectedError != "" { + assert.EqualError(t, err, tt.expectedError) + } else { + assert.NoError(t, err) + assert.Equal(t, + tt.fromAddress, + config.Contracts[consts.ContractNameOffRamp].Configs[consts.MethodCommit].FromAddress) + assert.Equal(t, + tt.commitGasLimit, + config.Contracts[consts.ContractNameOffRamp].Configs[consts.MethodCommit].GasLimit) + assert.Equal(t, + tt.execBatchGasLimit, + config.Contracts[consts.ContractNameOffRamp].Configs[consts.MethodExecute].GasLimit) + assert.Equal(t, + tt.maxGasPrice, + config.MaxGasPrice) + } + }) + } +} diff --git a/core/capabilities/ccip/configs/evm/contract_reader.go b/core/capabilities/ccip/configs/evm/contract_reader.go index 250b07bd1b1..e9b4314d68f 100644 --- a/core/capabilities/ccip/configs/evm/contract_reader.go +++ b/core/capabilities/ccip/configs/evm/contract_reader.go @@ -34,30 +34,6 @@ var ( // TODO: replace with generated ABI when the contract will be defined var rmnHomeString = "[{\"inputs\":[],\"name\":\"getAllConfigs\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"num\",\"type\":\"uint256\"}],\"name\":\"store\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" -// MustSourceReaderConfig returns a ChainReaderConfig that can be used to read from the onramp. -// The configuration is marshaled into JSON so that it can be passed to the relayer NewContractReader() method. -func MustSourceReaderConfig() []byte { - rawConfig := SourceReaderConfig - encoded, err := json.Marshal(rawConfig) - if err != nil { - panic(fmt.Errorf("failed to marshal ChainReaderConfig into JSON: %w", err)) - } - - return encoded -} - -// MustDestReaderConfig returns a ChainReaderConfig that can be used to read from the offramp. -// The configuration is marshaled into JSON so that it can be passed to the relayer NewContractReader() method. -func MustDestReaderConfig() []byte { - rawConfig := DestReaderConfig - encoded, err := json.Marshal(rawConfig) - if err != nil { - panic(fmt.Errorf("failed to marshal ChainReaderConfig into JSON: %w", err)) - } - - return encoded -} - func MergeReaderConfigs(configs ...evmrelaytypes.ChainReaderConfig) evmrelaytypes.ChainReaderConfig { allContracts := make(map[string]evmrelaytypes.ChainContractReader) for _, c := range configs { @@ -241,6 +217,8 @@ var SourceReaderConfig = evmrelaytypes.ChainReaderConfig{ }, } +// FeedReaderConfig provides a ChainReaderConfig that can be used to read from a price feed +// that is deployed on-chain. var FeedReaderConfig = evmrelaytypes.ChainReaderConfig{ Contracts: map[string]evmrelaytypes.ChainContractReader{ consts.ContractNamePriceAggregator: { @@ -307,6 +285,16 @@ var HomeChainReaderConfigRaw = evmrelaytypes.ChainReaderConfig{ }, } +var HomeChainReaderConfig = mustMarshal(HomeChainReaderConfigRaw) + +func mustMarshal(v interface{}) []byte { + b, err := json.Marshal(v) + if err != nil { + panic(err) + } + return b +} + func mustGetEventName(event string, tabi abi.ABI) string { e, ok := tabi.Events[event] if !ok { diff --git a/core/capabilities/ccip/delegate.go b/core/capabilities/ccip/delegate.go index 30007a7b2cf..d9fa4f4d91d 100644 --- a/core/capabilities/ccip/delegate.go +++ b/core/capabilities/ccip/delegate.go @@ -3,15 +3,18 @@ package ccip import ( "context" "fmt" + "math/big" "strconv" "time" "github.com/smartcontractkit/chainlink-common/pkg/loop" + "golang.org/x/exp/maps" "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/common" configsevm "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/configs/evm" "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/launcher" "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/oraclecreator" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" "github.com/smartcontractkit/chainlink/v2/core/services/registrysyncer" @@ -29,7 +32,6 @@ import ( cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" - "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/config" kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -40,7 +42,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/services/relay" - "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" "github.com/smartcontractkit/chainlink/v2/core/services/telemetry" "github.com/smartcontractkit/chainlink/v2/plugins" ) @@ -54,13 +55,13 @@ type Delegate struct { lggr logger.Logger registrarConfig plugins.RegistrarConfig pipelineRunner pipeline.Runner - chains legacyevm.LegacyChainContainer relayers RelayGetter keystore keystore.Master ds sqlutil.DataSource peerWrapper *ocrcommon.SingletonPeerWrapper monitoringEndpointGen telemetry.MonitoringEndpointGenerator capabilityConfig config.Capabilities + evmConfigs toml.EVMConfigs isNewlyCreatedJob bool } @@ -69,25 +70,25 @@ func NewDelegate( lggr logger.Logger, registrarConfig plugins.RegistrarConfig, pipelineRunner pipeline.Runner, - chains legacyevm.LegacyChainContainer, relayers RelayGetter, keystore keystore.Master, ds sqlutil.DataSource, peerWrapper *ocrcommon.SingletonPeerWrapper, monitoringEndpointGen telemetry.MonitoringEndpointGenerator, capabilityConfig config.Capabilities, + evmConfigs toml.EVMConfigs, ) *Delegate { return &Delegate{ lggr: lggr, registrarConfig: registrarConfig, pipelineRunner: pipelineRunner, - chains: chains, relayers: relayers, ds: ds, keystore: keystore, peerWrapper: peerWrapper, monitoringEndpointGen: monitoringEndpointGen, capabilityConfig: capabilityConfig, + evmConfigs: evmConfigs, } } @@ -115,7 +116,7 @@ func (d *Delegate) ServicesForSpec(ctx context.Context, spec job.Job) (services cfg := d.capabilityConfig rid := cfg.ExternalRegistry().RelayID() - relayer, err := d.relayers.Get(rid) + homeChainRelayer, err := d.relayers.Get(rid) if err != nil { return nil, fmt.Errorf("could not fetch relayer %s configured for capabilities registry: %w", rid, err) } @@ -124,12 +125,12 @@ func (d *Delegate) ServicesForSpec(ctx context.Context, spec job.Job) (services func() (p2ptypes.PeerID, error) { return p2ptypes.PeerID(p2pID.PeerID()), nil }, - relayer, + homeChainRelayer, cfg.ExternalRegistry().Address(), registrysyncer.NewORM(d.ds, d.lggr), ) if err != nil { - return nil, fmt.Errorf("could not configure syncer: %w", err) + return nil, fmt.Errorf("could not create registry syncer: %w", err) } ocrKeys, err := d.getOCRKeys(spec.CCIPSpec.OCRKeyBundleIDs) @@ -137,7 +138,11 @@ func (d *Delegate) ServicesForSpec(ctx context.Context, spec job.Job) (services return nil, err } - transmitterKeys, err := d.getTransmitterKeys(ctx, d.chains) + allRelayers, err := d.relayers.GetIDToRelayerMap() + if err != nil { + return nil, fmt.Errorf("could not fetch all relayers: %w", err) + } + transmitterKeys, err := d.getTransmitterKeys(ctx, maps.Keys(allRelayers)) if err != nil { return nil, err } @@ -153,7 +158,7 @@ func (d *Delegate) ServicesForSpec(ctx context.Context, spec job.Job) (services homeChainContractReader, ccipConfigBinding, err := d.getHomeChainContractReader( ctx, - d.chains, + homeChainRelayer, spec.CCIPSpec.CapabilityLabelledName, spec.CCIPSpec.CapabilityVersion) if err != nil { @@ -186,7 +191,7 @@ func (d *Delegate) ServicesForSpec(ctx context.Context, spec job.Job) (services oracleCreator = oraclecreator.NewPluginOracleCreator( ocrKeys, transmitterKeys, - d.chains, + allRelayers, d.peerWrapper, spec.ExternalJobID, spec.ID, @@ -198,6 +203,7 @@ func (d *Delegate) ServicesForSpec(ctx context.Context, spec job.Job) (services bootstrapperLocators, hcr, cciptypes.ChainSelector(homeChainChainSelector), + d.evmConfigs, ) } else { oracleCreator = oraclecreator.NewBootstrapOracleCreator( @@ -223,6 +229,7 @@ func (d *Delegate) ServicesForSpec(ctx context.Context, spec job.Job) (services registrySyncer.AddLauncher(capLauncher) return []job.ServiceCtx{ + homeChainContractReader, registrySyncer, hcr, capLauncher, @@ -260,13 +267,17 @@ func (d *Delegate) getOCRKeys(ocrKeyBundleIDs job.JSONConfig) (map[string]ocr2ke return ocrKeys, nil } -func (d *Delegate) getTransmitterKeys(ctx context.Context, chains legacyevm.LegacyChainContainer) (map[types.RelayID][]string, error) { +func (d *Delegate) getTransmitterKeys(ctx context.Context, relayIDs []types.RelayID) (map[types.RelayID][]string, error) { transmitterKeys := make(map[types.RelayID][]string) - for _, chain := range chains.Slice() { - relayID := types.NewRelayID(relay.NetworkEVM, chain.ID().String()) - ethKeys, err2 := d.keystore.Eth().EnabledAddressesForChain(ctx, chain.ID()) - if err2 != nil { - return nil, fmt.Errorf("error getting enabled addresses for chain: %s %w", chain.ID().String(), err2) + for _, relayID := range relayIDs { + chainID, ok := new(big.Int).SetString(relayID.ChainID, 10) + if !ok { + return nil, fmt.Errorf("error parsing chain ID, expected big int: %s", relayID.ChainID) + } + + ethKeys, err := d.keystore.Eth().EnabledAddressesForChain(ctx, chainID) + if err != nil { + return nil, fmt.Errorf("error getting enabled addresses for chain: %s %w", chainID.String(), err) } transmitterKeys[relayID] = func() (r []string) { @@ -281,26 +292,11 @@ func (d *Delegate) getTransmitterKeys(ctx context.Context, chains legacyevm.Lega func (d *Delegate) getHomeChainContractReader( ctx context.Context, - chains legacyevm.LegacyChainContainer, + homeChainRelayer loop.Relayer, capabilityLabelledName, capabilityVersion string, ) (types.ContractReader, types.BoundContract, error) { - // home chain is where the capability registry is deployed, - // which should be set correctly in toml config. - homeChainRelayID := d.capabilityConfig.ExternalRegistry().RelayID() - homeChain, err := chains.Get(homeChainRelayID.ChainID) - if err != nil { - return nil, types.BoundContract{}, fmt.Errorf("home chain relayer not found, chain id: %s, err: %w", homeChainRelayID.String(), err) - } - - reader, err := evm.NewChainReaderService( - context.Background(), - d.lggr, - homeChain.LogPoller(), - homeChain.HeadTracker(), - homeChain.Client(), - configsevm.HomeChainReaderConfigRaw, - ) + reader, err := homeChainRelayer.NewContractReader(ctx, configsevm.HomeChainReaderConfig) if err != nil { return nil, types.BoundContract{}, fmt.Errorf("failed to create home chain contract reader: %w", err) } diff --git a/core/capabilities/ccip/oraclecreator/plugin.go b/core/capabilities/ccip/oraclecreator/plugin.go index 455bcde83e7..ec027610fcb 100644 --- a/core/capabilities/ccip/oraclecreator/plugin.go +++ b/core/capabilities/ccip/oraclecreator/plugin.go @@ -2,7 +2,9 @@ package oraclecreator import ( "context" + "encoding/json" "fmt" + "math/big" "time" "github.com/ethereum/go-ethereum/common" @@ -15,6 +17,8 @@ import ( evmconfig "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/configs/evm" "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/ocrimpls" cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" chainsel "github.com/smartcontractkit/chain-selectors" @@ -23,6 +27,7 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/loop" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" commitocr3 "github.com/smartcontractkit/chainlink-ccip/commit" @@ -32,14 +37,11 @@ import ( "github.com/smartcontractkit/chainlink-ccip/pluginconfig" "github.com/smartcontractkit/chainlink-common/pkg/types" - - "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ocr2key" "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" "github.com/smartcontractkit/chainlink/v2/core/services/relay" - "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" evmrelaytypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" "github.com/smartcontractkit/chainlink/v2/core/services/synchronization" "github.com/smartcontractkit/chainlink/v2/core/services/telemetry" @@ -49,6 +51,7 @@ var _ cctypes.OracleCreator = &pluginOracleCreator{} const ( defaultCommitGasLimit = 500_000 + defaultExecGasLimit = 6_500_000 ) // pluginOracleCreator creates oracles that reference plugins running @@ -56,7 +59,6 @@ const ( type pluginOracleCreator struct { ocrKeyBundles map[string]ocr2key.KeyBundle transmitters map[types.RelayID][]string - chains legacyevm.LegacyChainContainer peerWrapper *ocrcommon.SingletonPeerWrapper externalJobID uuid.UUID jobID int32 @@ -68,12 +70,14 @@ type pluginOracleCreator struct { bootstrapperLocators []commontypes.BootstrapperLocator homeChainReader ccipreaderpkg.HomeChain homeChainSelector cciptypes.ChainSelector + relayers map[types.RelayID]loop.Relayer + evmConfigs toml.EVMConfigs } func NewPluginOracleCreator( ocrKeyBundles map[string]ocr2key.KeyBundle, transmitters map[types.RelayID][]string, - chains legacyevm.LegacyChainContainer, + relayers map[types.RelayID]loop.Relayer, peerWrapper *ocrcommon.SingletonPeerWrapper, externalJobID uuid.UUID, jobID int32, @@ -85,11 +89,12 @@ func NewPluginOracleCreator( bootstrapperLocators []commontypes.BootstrapperLocator, homeChainReader ccipreaderpkg.HomeChain, homeChainSelector cciptypes.ChainSelector, + evmConfigs toml.EVMConfigs, ) cctypes.OracleCreator { return &pluginOracleCreator{ ocrKeyBundles: ocrKeyBundles, transmitters: transmitters, - chains: chains, + relayers: relayers, peerWrapper: peerWrapper, externalJobID: externalJobID, jobID: jobID, @@ -101,6 +106,7 @@ func NewPluginOracleCreator( bootstrapperLocators: bootstrapperLocators, homeChainReader: homeChainReader, homeChainSelector: homeChainSelector, + evmConfigs: evmConfigs, } } @@ -275,6 +281,10 @@ func (i *pluginOracleCreator) createReadersAndWriters( var execBatchGasLimit uint64 if !ofc.execEmpty() { execBatchGasLimit = ofc.exec().BatchGasLimit + } else { + // Set the default here so chain writer config validation doesn't fail. + // For commit, this won't be used, so its harmless. + execBatchGasLimit = defaultExecGasLimit } homeChainID, err := i.getChainID(i.homeChainSelector) @@ -284,33 +294,57 @@ func (i *pluginOracleCreator) createReadersAndWriters( contractReaders := make(map[cciptypes.ChainSelector]types.ContractReader) chainWriters := make(map[cciptypes.ChainSelector]types.ChainWriter) - for _, chain := range i.chains.Slice() { - chainSelector, err1 := i.getChainSelector(chain.ID().Uint64()) + for relayID, relayer := range i.relayers { + chainID, ok := new(big.Int).SetString(relayID.ChainID, 10) + if !ok { + return nil, nil, fmt.Errorf("error parsing chain ID, expected big int: %s", relayID.ChainID) + } + + chainSelector, err1 := i.getChainSelector(chainID.Uint64()) if err1 != nil { - return nil, nil, err1 + return nil, nil, fmt.Errorf("failed to get chain selector from chain ID %s: %w", chainID.String(), err1) + } + + chainReaderConfig, err1 := getChainReaderConfig(chainID.Uint64(), destChainID, homeChainID, ofc, chainSelector) + if err1 != nil { + return nil, nil, fmt.Errorf("failed to get chain reader config: %w", err1) } - chainReaderConfig := getChainReaderConfig(chain.ID().Uint64(), destChainID, homeChainID, ofc, chainSelector) - cr, err1 := createChainReader(i.lggr, chain, chainReaderConfig, pluginType) + // TODO: context. + cr, err1 := relayer.NewContractReader(context.Background(), chainReaderConfig) if err1 != nil { return nil, nil, err1 } - if err2 := bindContracts(chain, cr, config, destChainID); err2 != nil { - return nil, nil, err2 + if chainID.Uint64() == destChainID { + offrampAddressHex := common.BytesToAddress(config.Config.OfframpAddress).Hex() + err2 := cr.Bind(context.Background(), []types.BoundContract{ + { + Address: offrampAddressHex, + Name: consts.ContractNameOffRamp, + }, + }) + if err2 != nil { + return nil, nil, fmt.Errorf("failed to bind chain reader for dest chain %s's offramp at %s: %w", chainID.String(), offrampAddressHex, err) + } } - if err3 := cr.Start(context.Background()); err3 != nil { - return nil, nil, fmt.Errorf("failed to start contract reader for chain %s: %w", chain.ID(), err3) + if err2 := cr.Start(context.Background()); err2 != nil { + return nil, nil, fmt.Errorf("failed to start contract reader for chain %s: %w", chainID.String(), err2) } - cw, err1 := createChainWriter(i.lggr, chain, pluginType, i.transmitters, execBatchGasLimit) + cw, err1 := createChainWriter( + chainID, + i.evmConfigs, + relayer, + i.transmitters, + execBatchGasLimit) if err1 != nil { return nil, nil, err1 } if err4 := cw.Start(context.Background()); err4 != nil { - return nil, nil, fmt.Errorf("failed to start chain writer for chain %s: %w", chain.ID(), err4) + return nil, nil, fmt.Errorf("failed to start chain writer for chain %s: %w", chainID.String(), err4) } contractReaders[chainSelector] = cr @@ -371,7 +405,7 @@ func getChainReaderConfig( homeChainID uint64, ofc offChainConfig, chainSelector cciptypes.ChainSelector, -) evmrelaytypes.ChainReaderConfig { +) ([]byte, error) { var chainReaderConfig evmrelaytypes.ChainReaderConfig if chainID == destChainID { chainReaderConfig = evmconfig.DestReaderConfig @@ -390,7 +424,13 @@ func getChainReaderConfig( if chainID == homeChainID { chainReaderConfig = evmconfig.MergeReaderConfigs(chainReaderConfig, evmconfig.HomeChainReaderConfigRaw) } - return chainReaderConfig + + marshaledConfig, err := json.Marshal(chainReaderConfig) + if err != nil { + return nil, fmt.Errorf("failed to marshal chain reader config: %w", err) + } + + return marshaledConfig, nil } func isUSDCEnabled(chainID uint64, destChainID uint64, ofc offChainConfig) bool { @@ -405,83 +445,76 @@ func isUSDCEnabled(chainID uint64, destChainID uint64, ofc offChainConfig) bool return ofc.exec().IsUSDCEnabled() } -func createChainReader( - lggr logger.Logger, - chain legacyevm.Chain, - chainReaderConfig evmrelaytypes.ChainReaderConfig, - pluginType cctypes.PluginType, -) (types.ContractReader, error) { - cr, err := evm.NewChainReaderService( - context.Background(), - lggr. - Named("EVMChainReaderService"). - Named(chain.ID().String()). - Named(pluginType.String()), - chain.LogPoller(), - chain.HeadTracker(), - chain.Client(), - chainReaderConfig, - ) - if err != nil { - return nil, fmt.Errorf("failed to create contract reader for chain %s: %w", chain.ID(), err) - } - return cr, nil -} - -func bindContracts( - chain legacyevm.Chain, - cr types.ContractReader, - config cctypes.OCR3ConfigWithMeta, - destChainID uint64, -) error { - if chain.ID().Uint64() == destChainID { - offrampAddressHex := common.BytesToAddress(config.Config.OfframpAddress).Hex() - err := cr.Bind(context.Background(), []types.BoundContract{ - { - Address: offrampAddressHex, - Name: consts.ContractNameOffRamp, - }, - }) - if err != nil { - return fmt.Errorf("failed to bind chain reader for dest chain %s's offramp at %s: %w", chain.ID(), offrampAddressHex, err) - } - } - return nil -} - func createChainWriter( - lggr logger.Logger, - chain legacyevm.Chain, - pluginType cctypes.PluginType, + chainID *big.Int, + evmConfigs toml.EVMConfigs, + relayer loop.Relayer, transmitters map[types.RelayID][]string, execBatchGasLimit uint64, ) (types.ChainWriter, error) { var fromAddress common.Address - transmitter, ok := transmitters[types.NewRelayID(relay.NetworkEVM, chain.ID().String())] + transmitter, ok := transmitters[types.NewRelayID(relay.NetworkEVM, chainID.String())] if ok { // TODO: remove EVM-specific stuff fromAddress = common.HexToAddress(transmitter[0]) } - cw, err := evm.NewChainWriterService( - lggr.Named("EVMChainWriterService"). - Named(chain.ID().String()). - Named(pluginType.String()), - chain.Client(), - chain.TxManager(), - chain.GasEstimator(), - evmconfig.ChainWriterConfigRaw( - fromAddress, - chain.Config().EVM().GasEstimator().PriceMaxKey(fromAddress), - defaultCommitGasLimit, - execBatchGasLimit, - ), + + maxGasPrice := getKeySpecificMaxGasPrice(evmConfigs, chainID, fromAddress) + if maxGasPrice == nil { + return nil, fmt.Errorf("failed to find max gas price for chain %s", chainID.String()) + } + + chainWriterRawConfig, err := evmconfig.ChainWriterConfigRaw( + fromAddress, + maxGasPrice, + defaultCommitGasLimit, + execBatchGasLimit, ) if err != nil { - return nil, fmt.Errorf("failed to create chain writer for chain %s: %w", chain.ID(), err) + return nil, fmt.Errorf("failed to create chain writer config: %w", err) + } + + chainWriterConfig, err := json.Marshal(chainWriterRawConfig) + if err != nil { + return nil, fmt.Errorf("failed to marshal chain writer config: %w", err) + } + + // TODO: context. + cw, err := relayer.NewChainWriter(context.Background(), chainWriterConfig) + if err != nil { + return nil, fmt.Errorf("failed to create chain writer for chain %s: %w", chainID.String(), err) } + return cw, nil } +func getKeySpecificMaxGasPrice(evmConfigs toml.EVMConfigs, chainID *big.Int, fromAddress common.Address) *assets.Wei { + var maxGasPrice *assets.Wei + + // If a chain is enabled it should have some configuration in the TOML config + // of the chainlink node. + for _, config := range evmConfigs { + if config.ChainID.ToInt().Cmp(chainID) != 0 { + continue + } + + // find the key-specific max gas price for the given fromAddress. + for _, keySpecific := range config.KeySpecific { + if keySpecific.Key.Address() == fromAddress { + maxGasPrice = keySpecific.GasEstimator.PriceMax + } + } + + // if we didn't find a key-specific max gas price, use the one specified + // in the gas estimator config, which should have a default value. + if maxGasPrice == nil { + maxGasPrice = config.GasEstimator.PriceMax + } + } + + return maxGasPrice +} + type offChainConfig struct { commitOffchainConfig *pluginconfig.CommitOffchainConfig execOffchainConfig *pluginconfig.ExecuteOffchainConfig diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 873f5080c6a..c9e72244cf1 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -554,13 +554,13 @@ func NewApplication(opts ApplicationOpts) (Application, error) { globalLogger, loopRegistrarConfig, pipelineRunner, - opts.RelayerChainInteroperators.LegacyEVMChains(), relayerChainInterops, opts.KeyStore, opts.DS, peerWrapper, telemetryManager, cfg.Capabilities(), + cfg.EVMConfigs(), ) } else { globalLogger.Debug("Off-chain reporting v2 disabled")