From e515e44e21f0f2c1355dd4917cc46aa01b9d46e9 Mon Sep 17 00:00:00 2001 From: Nick Corin Date: Thu, 23 May 2024 23:20:22 +0200 Subject: [PATCH 01/36] evm: Add a stub chainwriter impl --- core/services/relay/evm/chain_writer.go | 118 ++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 core/services/relay/evm/chain_writer.go diff --git a/core/services/relay/evm/chain_writer.go b/core/services/relay/evm/chain_writer.go new file mode 100644 index 00000000000..24787488f20 --- /dev/null +++ b/core/services/relay/evm/chain_writer.go @@ -0,0 +1,118 @@ +package evm + +import ( + "context" + "fmt" + "math/big" + "time" + + "github.com/google/uuid" + commonservices "github.com/smartcontractkit/chainlink-common/pkg/services" + evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" +) + +type ChainWriter interface { + // SubmitSignedTransaction packs and broadcasts a transaction to the underlying chain. + // + // The `transactionID` will be used by the underlying TXM as an idempotency key, and unique reference to track transaction attempts. + SubmitSignedTransaction(ctx context.Context, payload []byte, signature map[string]any, transactionID uuid.UUID, toAddress string, meta *TxMeta, value big.Int) (int64, error) + + // GetTransactionStatus returns the current status of a transaction in the underlying chain's TXM. + GetTransactionStatus(ctx context.Context, transactionID uuid.UUID) (TransactionStatus, error) + + // GetFeeComponents retrieves the associated gas costs for executing a transaction. + GetFeeComponents(ctx context.Context) (ChainFeeComponents, error) +} + +// TxMeta contains metadata fields for a transaction. +// +// Eventually this will replace, or be replaced by (via a move), the `TxMeta` in core: +// https://github.com/smartcontractkit/chainlink/blob/dfc399da715f16af1fcf6441ea5fc47b71800fa1/common/txmgr/types/tx.go#L121 +type TxMeta = map[string]string + +// TransactionStatus are the status we expect every TXM to support and that can be returned by StatusForUUID. +type TransactionStatus int + +const ( + Unknown TransactionStatus = iota + Unconfirmed + Finalized + Failed + Fatal +) + +// ChainFeeComponents contains the different cost components of executing a transaction. +type ChainFeeComponents struct { + // The cost of executing transaction in the chain's EVM (or the L2 environment). + ExecutionFee big.Int + + // The cost associated with an L2 posting a transaction's data to the L1. + DataAvailabilityFee big.Int +} + +type ChainWriterService interface { + services.ServiceCtx + ChainWriter +} + +// Compile-time assertion that chainWriter implements the ChainWriterService interface. +var _ ChainWriterService = (*chainWriter)(nil) + +func NewChainWriterService(config evm.ChainWrtier, logger logger.Logger, client evmclient.Client) ChainWriterService { + return &chainWriter{logger: logger, client: client, config: config} +} + +type chainWriter struct { + commonservices.StateMachine + + logger logger.Logger + client evmclient.Client + config evm.ChainWriter +} + +func (writer *chainWriter) SubmitSignedTransaction(ctx context.Context, payload []byte, signature map[string]any, transactionID uuid.UUID, toAddress string, meta *TxMeta, value big.Int) (int64, error) { + return 0, fmt.Errorf("not implemented") +} + +func (writer *chainWriter) GetTransactionStatus(ctx context.Context, transactionID uuid.UUID) (TransactionStatus, error) { + return Unknown, fmt.Errorf("not implemented") +} + +func (writer *chainWriter) GetFeeComponents(ctx context.Context) (ChainFeeComponents, error) { + return ChainFeeComponents{}, fmt.Errorf("not implemented") +} + +func (writer *chainWriter) Close() error { + return writer.StopOnce(writer.Name(), func() error { + _, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + // TODO(nickcorin): Add shutdown steps here. + return nil + }) +} + +func (writer *chainWriter) HealthReport() map[string]error { + return map[string]error{ + writer.Name(): nil, + } +} + +func (writer *chainWriter) Name() string { + return "chain-writer" +} + +func (writer *chainWriter) Ready() error { + // TODO(nickcorin): Return nil here once the implementation is done. + return fmt.Errorf("not fully implemented") +} + +func (writer *chainWriter) Start(ctx context.Context) error { + return writer.StartOnce(writer.Name(), func() error { + // TODO(nickcorin): Add startup steps here. + return nil + }) +} From 6b1d8f2c0a527c7d21bed240ad60ce09f760fe9a Mon Sep 17 00:00:00 2001 From: Nick Corin Date: Thu, 23 May 2024 23:41:31 +0200 Subject: [PATCH 02/36] evm: Fix config parameter --- core/services/relay/evm/chain_writer.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/services/relay/evm/chain_writer.go b/core/services/relay/evm/chain_writer.go index 24787488f20..599bc25b2aa 100644 --- a/core/services/relay/evm/chain_writer.go +++ b/core/services/relay/evm/chain_writer.go @@ -9,9 +9,9 @@ import ( "github.com/google/uuid" commonservices "github.com/smartcontractkit/chainlink-common/pkg/services" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services" - "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" ) type ChainWriter interface { @@ -61,7 +61,7 @@ type ChainWriterService interface { // Compile-time assertion that chainWriter implements the ChainWriterService interface. var _ ChainWriterService = (*chainWriter)(nil) -func NewChainWriterService(config evm.ChainWrtier, logger logger.Logger, client evmclient.Client) ChainWriterService { +func NewChainWriterService(config config.ChainWriter, logger logger.Logger, client evmclient.Client) ChainWriterService { return &chainWriter{logger: logger, client: client, config: config} } @@ -70,7 +70,7 @@ type chainWriter struct { logger logger.Logger client evmclient.Client - config evm.ChainWriter + config config.ChainWriter } func (writer *chainWriter) SubmitSignedTransaction(ctx context.Context, payload []byte, signature map[string]any, transactionID uuid.UUID, toAddress string, meta *TxMeta, value big.Int) (int64, error) { From a9ed95e35f98fc34245db844ab442123d6acf0f2 Mon Sep 17 00:00:00 2001 From: Nick Corin Date: Fri, 24 May 2024 13:45:41 +0200 Subject: [PATCH 03/36] evm: Change the chainwriter receiver name --- core/services/relay/evm/chain_writer.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/core/services/relay/evm/chain_writer.go b/core/services/relay/evm/chain_writer.go index 599bc25b2aa..a32ac645479 100644 --- a/core/services/relay/evm/chain_writer.go +++ b/core/services/relay/evm/chain_writer.go @@ -73,20 +73,20 @@ type chainWriter struct { config config.ChainWriter } -func (writer *chainWriter) SubmitSignedTransaction(ctx context.Context, payload []byte, signature map[string]any, transactionID uuid.UUID, toAddress string, meta *TxMeta, value big.Int) (int64, error) { +func (w *chainWriter) SubmitSignedTransaction(ctx context.Context, payload []byte, signature map[string]any, transactionID uuid.UUID, toAddress string, meta *TxMeta, value big.Int) (int64, error) { return 0, fmt.Errorf("not implemented") } -func (writer *chainWriter) GetTransactionStatus(ctx context.Context, transactionID uuid.UUID) (TransactionStatus, error) { +func (w *chainWriter) GetTransactionStatus(ctx context.Context, transactionID uuid.UUID) (TransactionStatus, error) { return Unknown, fmt.Errorf("not implemented") } -func (writer *chainWriter) GetFeeComponents(ctx context.Context) (ChainFeeComponents, error) { +func (w *chainWriter) GetFeeComponents(ctx context.Context) (ChainFeeComponents, error) { return ChainFeeComponents{}, fmt.Errorf("not implemented") } -func (writer *chainWriter) Close() error { - return writer.StopOnce(writer.Name(), func() error { +func (w *chainWriter) Close() error { + return w.StopOnce(w.Name(), func() error { _, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() @@ -95,23 +95,23 @@ func (writer *chainWriter) Close() error { }) } -func (writer *chainWriter) HealthReport() map[string]error { +func (w *chainWriter) HealthReport() map[string]error { return map[string]error{ - writer.Name(): nil, + w.Name(): nil, } } -func (writer *chainWriter) Name() string { +func (w *chainWriter) Name() string { return "chain-writer" } -func (writer *chainWriter) Ready() error { +func (w *chainWriter) Ready() error { // TODO(nickcorin): Return nil here once the implementation is done. return fmt.Errorf("not fully implemented") } -func (writer *chainWriter) Start(ctx context.Context) error { - return writer.StartOnce(writer.Name(), func() error { +func (w *chainWriter) Start(ctx context.Context) error { + return w.StartOnce(w.Name(), func() error { // TODO(nickcorin): Add startup steps here. return nil }) From 8d7680c675c0093d1bddac4a8ad2c10e0eef57a9 Mon Sep 17 00:00:00 2001 From: Nick Corin Date: Fri, 24 May 2024 14:59:53 +0200 Subject: [PATCH 04/36] evm: Remove the chain writer interface to reference chainlink-common --- core/services/ocr2/plugins/median/services.go | 2 +- core/services/relay/evm/chain_writer.go | 51 +++---------------- go.mod | 2 +- go.sum | 2 + 4 files changed, 11 insertions(+), 46 deletions(-) diff --git a/core/services/ocr2/plugins/median/services.go b/core/services/ocr2/plugins/median/services.go index 897531b82fb..ae717a8fd23 100644 --- a/core/services/ocr2/plugins/median/services.go +++ b/core/services/ocr2/plugins/median/services.go @@ -167,7 +167,7 @@ func NewMedianServices(ctx context.Context, abort() return } - median := loop.NewMedianService(lggr, telem, cmdFn, medianProvider, dataSource, juelsPerFeeCoinSource, gasPriceSubunitsDataSource, errorLog) + median := loop.NewMedianService(lggr, telem, cmdFn, medianProvider, dataSource, juelsPerFeeCoinSource, errorLog) argsNoPlugin.ReportingPluginFactory = median srvs = append(srvs, median) } else { diff --git a/core/services/relay/evm/chain_writer.go b/core/services/relay/evm/chain_writer.go index a32ac645479..fe1b55fa8d9 100644 --- a/core/services/relay/evm/chain_writer.go +++ b/core/services/relay/evm/chain_writer.go @@ -12,50 +12,13 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services" -) - -type ChainWriter interface { - // SubmitSignedTransaction packs and broadcasts a transaction to the underlying chain. - // - // The `transactionID` will be used by the underlying TXM as an idempotency key, and unique reference to track transaction attempts. - SubmitSignedTransaction(ctx context.Context, payload []byte, signature map[string]any, transactionID uuid.UUID, toAddress string, meta *TxMeta, value big.Int) (int64, error) - - // GetTransactionStatus returns the current status of a transaction in the underlying chain's TXM. - GetTransactionStatus(ctx context.Context, transactionID uuid.UUID) (TransactionStatus, error) - - // GetFeeComponents retrieves the associated gas costs for executing a transaction. - GetFeeComponents(ctx context.Context) (ChainFeeComponents, error) -} -// TxMeta contains metadata fields for a transaction. -// -// Eventually this will replace, or be replaced by (via a move), the `TxMeta` in core: -// https://github.com/smartcontractkit/chainlink/blob/dfc399da715f16af1fcf6441ea5fc47b71800fa1/common/txmgr/types/tx.go#L121 -type TxMeta = map[string]string - -// TransactionStatus are the status we expect every TXM to support and that can be returned by StatusForUUID. -type TransactionStatus int - -const ( - Unknown TransactionStatus = iota - Unconfirmed - Finalized - Failed - Fatal + "github.com/smartcontractkit/chainlink-common/pkg/types" ) -// ChainFeeComponents contains the different cost components of executing a transaction. -type ChainFeeComponents struct { - // The cost of executing transaction in the chain's EVM (or the L2 environment). - ExecutionFee big.Int - - // The cost associated with an L2 posting a transaction's data to the L1. - DataAvailabilityFee big.Int -} - type ChainWriterService interface { services.ServiceCtx - ChainWriter + types.ChainWriter } // Compile-time assertion that chainWriter implements the ChainWriterService interface. @@ -73,16 +36,16 @@ type chainWriter struct { config config.ChainWriter } -func (w *chainWriter) SubmitSignedTransaction(ctx context.Context, payload []byte, signature map[string]any, transactionID uuid.UUID, toAddress string, meta *TxMeta, value big.Int) (int64, error) { +func (w *chainWriter) SubmitSignedTransaction(ctx context.Context, payload []byte, signature map[string]any, transactionID uuid.UUID, toAddress string, meta *types.TxMeta, value big.Int) (int64, error) { return 0, fmt.Errorf("not implemented") } -func (w *chainWriter) GetTransactionStatus(ctx context.Context, transactionID uuid.UUID) (TransactionStatus, error) { - return Unknown, fmt.Errorf("not implemented") +func (w *chainWriter) GetTransactionStatus(ctx context.Context, transactionID uuid.UUID) (types.TransactionStatus, error) { + return types.Unknown, fmt.Errorf("not implemented") } -func (w *chainWriter) GetFeeComponents(ctx context.Context) (ChainFeeComponents, error) { - return ChainFeeComponents{}, fmt.Errorf("not implemented") +func (w *chainWriter) GetFeeComponents(ctx context.Context) (*types.ChainFeeComponents, error) { + return nil, fmt.Errorf("not implemented") } func (w *chainWriter) Close() error { diff --git a/go.mod b/go.mod index 34fe808a821..1d7ce3d53db 100644 --- a/go.mod +++ b/go.mod @@ -72,7 +72,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chain-selectors v1.0.10 github.com/smartcontractkit/chainlink-automation v1.0.3 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240522203001-10ea0211efd7 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240523205053-03a5a4e1a596 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240508101745-af1ed7bc8a69 github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 diff --git a/go.sum b/go.sum index d76c84b55c2..48691e7da74 100644 --- a/go.sum +++ b/go.sum @@ -1173,6 +1173,8 @@ github.com/smartcontractkit/chainlink-automation v1.0.3 h1:h/ijT0NiyV06VxYVgcNfs github.com/smartcontractkit/chainlink-automation v1.0.3/go.mod h1:RjboV0Qd7YP+To+OrzHGXaxUxoSONveCoAK2TQ1INLU= github.com/smartcontractkit/chainlink-common v0.1.7-0.20240522203001-10ea0211efd7 h1:od+11B83s0mQwAMPP3lhtb0nYz63pIKpJEKddfFpu/M= github.com/smartcontractkit/chainlink-common v0.1.7-0.20240522203001-10ea0211efd7/go.mod h1:cFHRblGbGn/rFYOOGsNbtLicMc1+5YdN0KoebYr93pk= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240523205053-03a5a4e1a596 h1:WI1fUFlMVPUpSQrqd2NOEJ2+UCcflT+HnIKXYOiSXe8= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240523205053-03a5a4e1a596/go.mod h1:s+68EchlrXqHKRW3JJgZLEARvzMSKRI5+cE5Zx7pVJA= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240508101745-af1ed7bc8a69 h1:Sec/GpBpUVaTEax1kSHlTvkzF/+d3w5roAQXaj5+SLA= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240508101745-af1ed7bc8a69/go.mod h1:ZQKf+0OLzCLYIisH/OdOIQuFRI6bDuw+jPBTATyHfFM= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= From c37c1425f824705f45d595e69e003feb0baf1fdc Mon Sep 17 00:00:00 2001 From: Nick Corin Date: Fri, 24 May 2024 15:14:39 +0200 Subject: [PATCH 05/36] evm: Update common dep, and fix signature --- core/services/ocr2/plugins/median/services.go | 2 +- core/services/relay/evm/chain_writer.go | 4 ++-- go.mod | 2 +- go.sum | 2 ++ 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/core/services/ocr2/plugins/median/services.go b/core/services/ocr2/plugins/median/services.go index ae717a8fd23..897531b82fb 100644 --- a/core/services/ocr2/plugins/median/services.go +++ b/core/services/ocr2/plugins/median/services.go @@ -167,7 +167,7 @@ func NewMedianServices(ctx context.Context, abort() return } - median := loop.NewMedianService(lggr, telem, cmdFn, medianProvider, dataSource, juelsPerFeeCoinSource, errorLog) + median := loop.NewMedianService(lggr, telem, cmdFn, medianProvider, dataSource, juelsPerFeeCoinSource, gasPriceSubunitsDataSource, errorLog) argsNoPlugin.ReportingPluginFactory = median srvs = append(srvs, median) } else { diff --git a/core/services/relay/evm/chain_writer.go b/core/services/relay/evm/chain_writer.go index fe1b55fa8d9..217ac05d4e2 100644 --- a/core/services/relay/evm/chain_writer.go +++ b/core/services/relay/evm/chain_writer.go @@ -36,8 +36,8 @@ type chainWriter struct { config config.ChainWriter } -func (w *chainWriter) SubmitSignedTransaction(ctx context.Context, payload []byte, signature map[string]any, transactionID uuid.UUID, toAddress string, meta *types.TxMeta, value big.Int) (int64, error) { - return 0, fmt.Errorf("not implemented") +func (w *chainWriter) SubmitSignedTransaction(ctx context.Context, payload []byte, signature map[string]any, transactionID uuid.UUID, toAddress string, meta *types.TxMeta, value big.Int) error { + return fmt.Errorf("not implemented") } func (w *chainWriter) GetTransactionStatus(ctx context.Context, transactionID uuid.UUID) (types.TransactionStatus, error) { diff --git a/go.mod b/go.mod index 1d7ce3d53db..32d7cb9fd72 100644 --- a/go.mod +++ b/go.mod @@ -72,7 +72,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chain-selectors v1.0.10 github.com/smartcontractkit/chainlink-automation v1.0.3 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240523205053-03a5a4e1a596 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240524131122-70f94ee563a6 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240508101745-af1ed7bc8a69 github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 diff --git a/go.sum b/go.sum index 48691e7da74..2f02b525ae5 100644 --- a/go.sum +++ b/go.sum @@ -1175,6 +1175,8 @@ github.com/smartcontractkit/chainlink-common v0.1.7-0.20240522203001-10ea0211efd github.com/smartcontractkit/chainlink-common v0.1.7-0.20240522203001-10ea0211efd7/go.mod h1:cFHRblGbGn/rFYOOGsNbtLicMc1+5YdN0KoebYr93pk= github.com/smartcontractkit/chainlink-common v0.1.7-0.20240523205053-03a5a4e1a596 h1:WI1fUFlMVPUpSQrqd2NOEJ2+UCcflT+HnIKXYOiSXe8= github.com/smartcontractkit/chainlink-common v0.1.7-0.20240523205053-03a5a4e1a596/go.mod h1:s+68EchlrXqHKRW3JJgZLEARvzMSKRI5+cE5Zx7pVJA= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240524131122-70f94ee563a6 h1:ChXBMCAlp2tNdPc6QAv/F+oNPiE/CHfiIjXZcIYPFlo= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240524131122-70f94ee563a6/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240508101745-af1ed7bc8a69 h1:Sec/GpBpUVaTEax1kSHlTvkzF/+d3w5roAQXaj5+SLA= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240508101745-af1ed7bc8a69/go.mod h1:ZQKf+0OLzCLYIisH/OdOIQuFRI6bDuw+jPBTATyHfFM= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= From 65db3227dc09c32b5d44969f38bb6e7e9a74bdc9 Mon Sep 17 00:00:00 2001 From: Nick Corin Date: Fri, 24 May 2024 15:18:40 +0200 Subject: [PATCH 06/36] go.sum: Run gomodtidy --- go.sum | 4 ---- 1 file changed, 4 deletions(-) diff --git a/go.sum b/go.sum index 2f02b525ae5..93610f82adb 100644 --- a/go.sum +++ b/go.sum @@ -1171,10 +1171,6 @@ github.com/smartcontractkit/chain-selectors v1.0.10 h1:t9kJeE6B6G+hKD0GYR4kGJSCq github.com/smartcontractkit/chain-selectors v1.0.10/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.3 h1:h/ijT0NiyV06VxYVgcNfsE3+8OEzT3Q0Z9au0z1BPWs= github.com/smartcontractkit/chainlink-automation v1.0.3/go.mod h1:RjboV0Qd7YP+To+OrzHGXaxUxoSONveCoAK2TQ1INLU= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240522203001-10ea0211efd7 h1:od+11B83s0mQwAMPP3lhtb0nYz63pIKpJEKddfFpu/M= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240522203001-10ea0211efd7/go.mod h1:cFHRblGbGn/rFYOOGsNbtLicMc1+5YdN0KoebYr93pk= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240523205053-03a5a4e1a596 h1:WI1fUFlMVPUpSQrqd2NOEJ2+UCcflT+HnIKXYOiSXe8= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240523205053-03a5a4e1a596/go.mod h1:s+68EchlrXqHKRW3JJgZLEARvzMSKRI5+cE5Zx7pVJA= github.com/smartcontractkit/chainlink-common v0.1.7-0.20240524131122-70f94ee563a6 h1:ChXBMCAlp2tNdPc6QAv/F+oNPiE/CHfiIjXZcIYPFlo= github.com/smartcontractkit/chainlink-common v0.1.7-0.20240524131122-70f94ee563a6/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240508101745-af1ed7bc8a69 h1:Sec/GpBpUVaTEax1kSHlTvkzF/+d3w5roAQXaj5+SLA= From 2377b72ec3d9befc1cd8780f37f4e94a44234359 Mon Sep 17 00:00:00 2001 From: Nick Corin Date: Fri, 24 May 2024 15:26:12 +0200 Subject: [PATCH 07/36] .changeset: Add a changeset --- .changeset/tricky-flowers-exist.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/tricky-flowers-exist.md diff --git a/.changeset/tricky-flowers-exist.md b/.changeset/tricky-flowers-exist.md new file mode 100644 index 00000000000..0b45b116f54 --- /dev/null +++ b/.changeset/tricky-flowers-exist.md @@ -0,0 +1,5 @@ +--- +"chainlink": minor +--- + +#added A ChainWriter implementation in the EVM relay. From ffafc4651b5165cd2504d33c4e327ce990d78362 Mon Sep 17 00:00:00 2001 From: Nick Corin Date: Fri, 24 May 2024 14:43:21 +0200 Subject: [PATCH 08/36] evm: Pseudo-implement the submit transaction method on chainwriter --- core/services/relay/evm/chain_writer.go | 39 ++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/core/services/relay/evm/chain_writer.go b/core/services/relay/evm/chain_writer.go index 217ac05d4e2..eef98c6c094 100644 --- a/core/services/relay/evm/chain_writer.go +++ b/core/services/relay/evm/chain_writer.go @@ -6,10 +6,15 @@ import ( "math/big" "time" + "github.com/ethereum/go-ethereum/common" "github.com/google/uuid" commonservices "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/common/txmgr" + txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" + evmtxmgr "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services" @@ -36,7 +41,39 @@ type chainWriter struct { config config.ChainWriter } -func (w *chainWriter) SubmitSignedTransaction(ctx context.Context, payload []byte, signature map[string]any, transactionID uuid.UUID, toAddress string, meta *types.TxMeta, value big.Int) error { +func (w *chainWriter) SubmitSignedTransaction(ctx context.Context, payload []byte, signatures map[string]any, transactionID uuid.UUID, toAddress string, meta *types.TxMeta, value big.Int) error { + forwarderABI := evmtypes.MustGetABI(w.config.ABI()) + + // TODO(nickcorin): + // Check the required format for the signatures when packing the ABI. The original type was [][]byte, however we'll need strict type assertions + // translating any -> []byte. + // + // Also, figure out what to use for the method name. + calldata, err := forwarderABI.Pack("", common.HexToAddress(toAddress), payload, signatures) + if err != nil { + return fmt.Errorf("pack forwarder abi: %w", err) + } + + // TODO(nickcorin): Change this to be config driven. + sendStrategy := txmgr.SendEveryStrategy{} + + var checker evmtxmgr.TransmitCheckerSpec + if w.config.Checker() != "" { + checker.CheckerType = txmgrtypes.TransmitCheckerType(w.config.Checker()) + } + + req := evmtxmgr.TxRequest{ + FromAddress: w.config.FromAddress().Address(), + ToAddress: w.config.ForwarderAddress().Address(), + EncodedPayload: calldata, + FeeLimit: w.config.GasLimit(), + Meta: nil, // TODO(nickcorin): Add this in once parsed. + Strategy: sendStrategy, + Checker: checker, + } + + // TODO(nickcorin): Send the request to the TXM. + return fmt.Errorf("not implemented") } From 3879bb4aa590506c960f1bc9b086c80b997d6843 Mon Sep 17 00:00:00 2001 From: Nick Corin Date: Fri, 24 May 2024 21:30:20 +0200 Subject: [PATCH 09/36] evm: Add txm dependency to chainwriter --- core/services/relay/evm/chain_writer.go | 16 ++++++++++------ go.mod | 2 +- go.sum | 2 ++ 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/core/services/relay/evm/chain_writer.go b/core/services/relay/evm/chain_writer.go index eef98c6c094..a889f1c5648 100644 --- a/core/services/relay/evm/chain_writer.go +++ b/core/services/relay/evm/chain_writer.go @@ -29,7 +29,7 @@ type ChainWriterService interface { // Compile-time assertion that chainWriter implements the ChainWriterService interface. var _ ChainWriterService = (*chainWriter)(nil) -func NewChainWriterService(config config.ChainWriter, logger logger.Logger, client evmclient.Client) ChainWriterService { +func NewChainWriterService(config config.ChainWriter, logger logger.Logger, client evmclient.Client, txm evmtxmgr.TxManager) ChainWriterService { return &chainWriter{logger: logger, client: client, config: config} } @@ -39,6 +39,7 @@ type chainWriter struct { logger logger.Logger client evmclient.Client config config.ChainWriter + txm evmtxmgr.TxManager } func (w *chainWriter) SubmitSignedTransaction(ctx context.Context, payload []byte, signatures map[string]any, transactionID uuid.UUID, toAddress string, meta *types.TxMeta, value big.Int) error { @@ -48,8 +49,8 @@ func (w *chainWriter) SubmitSignedTransaction(ctx context.Context, payload []byt // Check the required format for the signatures when packing the ABI. The original type was [][]byte, however we'll need strict type assertions // translating any -> []byte. // - // Also, figure out what to use for the method name. - calldata, err := forwarderABI.Pack("", common.HexToAddress(toAddress), payload, signatures) + // Also, double check that the method name is always going to be 'report'? + calldata, err := forwarderABI.Pack("report", common.HexToAddress(toAddress), payload, signatures) if err != nil { return fmt.Errorf("pack forwarder abi: %w", err) } @@ -67,14 +68,17 @@ func (w *chainWriter) SubmitSignedTransaction(ctx context.Context, payload []byt ToAddress: w.config.ForwarderAddress().Address(), EncodedPayload: calldata, FeeLimit: w.config.GasLimit(), - Meta: nil, // TODO(nickcorin): Add this in once parsed. + Meta: &txmgrtypes.TxMeta[common.Address, common.Hash]{WorkflowExecutionID: meta.WorkflowExecutionID}, Strategy: sendStrategy, Checker: checker, } - // TODO(nickcorin): Send the request to the TXM. + _, err = w.txm.CreateTransaction(ctx, req) + if err != nil { + return fmt.Errorf("failed to create tx: %w", err) + } - return fmt.Errorf("not implemented") + return nil } func (w *chainWriter) GetTransactionStatus(ctx context.Context, transactionID uuid.UUID) (types.TransactionStatus, error) { diff --git a/go.mod b/go.mod index 32d7cb9fd72..0db0a9a59e5 100644 --- a/go.mod +++ b/go.mod @@ -72,7 +72,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chain-selectors v1.0.10 github.com/smartcontractkit/chainlink-automation v1.0.3 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240524131122-70f94ee563a6 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240524171454-44f28c464ec1 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240508101745-af1ed7bc8a69 github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 diff --git a/go.sum b/go.sum index 93610f82adb..45b89f702ba 100644 --- a/go.sum +++ b/go.sum @@ -1173,6 +1173,8 @@ github.com/smartcontractkit/chainlink-automation v1.0.3 h1:h/ijT0NiyV06VxYVgcNfs github.com/smartcontractkit/chainlink-automation v1.0.3/go.mod h1:RjboV0Qd7YP+To+OrzHGXaxUxoSONveCoAK2TQ1INLU= github.com/smartcontractkit/chainlink-common v0.1.7-0.20240524131122-70f94ee563a6 h1:ChXBMCAlp2tNdPc6QAv/F+oNPiE/CHfiIjXZcIYPFlo= github.com/smartcontractkit/chainlink-common v0.1.7-0.20240524131122-70f94ee563a6/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240524171454-44f28c464ec1 h1:c6seVNI/kgh0+G27xQMWnnC/8umrkLQTEOtpFPA/LyE= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240524171454-44f28c464ec1/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240508101745-af1ed7bc8a69 h1:Sec/GpBpUVaTEax1kSHlTvkzF/+d3w5roAQXaj5+SLA= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240508101745-af1ed7bc8a69/go.mod h1:ZQKf+0OLzCLYIisH/OdOIQuFRI6bDuw+jPBTATyHfFM= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= From 0ffbe3748f2ffd41069a636543aad9ea08cf292c Mon Sep 17 00:00:00 2001 From: Nick Corin Date: Fri, 24 May 2024 21:33:41 +0200 Subject: [PATCH 10/36] evm: Use the txm param properly --- core/services/relay/evm/chain_writer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/services/relay/evm/chain_writer.go b/core/services/relay/evm/chain_writer.go index a889f1c5648..77e67750d80 100644 --- a/core/services/relay/evm/chain_writer.go +++ b/core/services/relay/evm/chain_writer.go @@ -30,7 +30,7 @@ type ChainWriterService interface { var _ ChainWriterService = (*chainWriter)(nil) func NewChainWriterService(config config.ChainWriter, logger logger.Logger, client evmclient.Client, txm evmtxmgr.TxManager) ChainWriterService { - return &chainWriter{logger: logger, client: client, config: config} + return &chainWriter{logger: logger, client: client, config: config, txm: txm} } type chainWriter struct { From 6a8e7a1af293500c8ee813f9f1d1755820c32e54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Wed, 29 May 2024 14:11:04 +0900 Subject: [PATCH 11/36] Update code to use the new interface --- core/services/relay/evm/chain_writer.go | 51 +++++++++++++++---------- core/services/relay/evm/types/types.go | 18 +++++++++ 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/core/services/relay/evm/chain_writer.go b/core/services/relay/evm/chain_writer.go index 77e67750d80..8bb703f8c10 100644 --- a/core/services/relay/evm/chain_writer.go +++ b/core/services/relay/evm/chain_writer.go @@ -12,24 +12,24 @@ import ( "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" evmtxmgr "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" - "github.com/smartcontractkit/chainlink-common/pkg/types" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" ) type ChainWriterService interface { services.ServiceCtx - types.ChainWriter + commontypes.ChainWriter } // Compile-time assertion that chainWriter implements the ChainWriterService interface. var _ ChainWriterService = (*chainWriter)(nil) -func NewChainWriterService(config config.ChainWriter, logger logger.Logger, client evmclient.Client, txm evmtxmgr.TxManager) ChainWriterService { +func NewChainWriterService(logger logger.Logger, client evmclient.Client, txm evmtxmgr.TxManager, config types.ChainWriterConfig) ChainWriterService { return &chainWriter{logger: logger, client: client, config: config, txm: txm} } @@ -38,19 +38,28 @@ type chainWriter struct { logger logger.Logger client evmclient.Client - config config.ChainWriter + config types.ChainWriterConfig txm evmtxmgr.TxManager } -func (w *chainWriter) SubmitSignedTransaction(ctx context.Context, payload []byte, signatures map[string]any, transactionID uuid.UUID, toAddress string, meta *types.TxMeta, value big.Int) error { - forwarderABI := evmtypes.MustGetABI(w.config.ABI()) +func (w *chainWriter) SubmitTransaction(ctx context.Context, contractName, method string, args []any, transactionID uuid.UUID, toAddress string, meta *commontypes.TxMeta, value big.Int) error { + if !common.IsHexAddress(toAddress) { + return fmt.Errorf("toAddress is not a valid ethereum address: %v", toAddress) + } + + contractConfig, ok := w.config.Contracts[contractName] + if !ok { + return fmt.Errorf("contract config not found: %v", contractName) + } + methodConfig, ok := contractConfig.Configs[method] + if !ok { + return fmt.Errorf("method config not found: %v", method) + } + + // TODO(archseer): do this at init time once + forwarderABI := evmtypes.MustGetABI(contractConfig.ContractABI) - // TODO(nickcorin): - // Check the required format for the signatures when packing the ABI. The original type was [][]byte, however we'll need strict type assertions - // translating any -> []byte. - // - // Also, double check that the method name is always going to be 'report'? - calldata, err := forwarderABI.Pack("report", common.HexToAddress(toAddress), payload, signatures) + calldata, err := forwarderABI.Pack(methodConfig.ChainSpecificName, args...) if err != nil { return fmt.Errorf("pack forwarder abi: %w", err) } @@ -59,15 +68,15 @@ func (w *chainWriter) SubmitSignedTransaction(ctx context.Context, payload []byt sendStrategy := txmgr.SendEveryStrategy{} var checker evmtxmgr.TransmitCheckerSpec - if w.config.Checker() != "" { - checker.CheckerType = txmgrtypes.TransmitCheckerType(w.config.Checker()) + if methodConfig.Checker != "" { + checker.CheckerType = txmgrtypes.TransmitCheckerType(methodConfig.Checker) } req := evmtxmgr.TxRequest{ - FromAddress: w.config.FromAddress().Address(), - ToAddress: w.config.ForwarderAddress().Address(), + FromAddress: methodConfig.FromAddress, + ToAddress: common.HexToAddress(toAddress), EncodedPayload: calldata, - FeeLimit: w.config.GasLimit(), + FeeLimit: methodConfig.GasLimit, Meta: &txmgrtypes.TxMeta[common.Address, common.Hash]{WorkflowExecutionID: meta.WorkflowExecutionID}, Strategy: sendStrategy, Checker: checker, @@ -81,11 +90,11 @@ func (w *chainWriter) SubmitSignedTransaction(ctx context.Context, payload []byt return nil } -func (w *chainWriter) GetTransactionStatus(ctx context.Context, transactionID uuid.UUID) (types.TransactionStatus, error) { - return types.Unknown, fmt.Errorf("not implemented") +func (w *chainWriter) GetTransactionStatus(ctx context.Context, transactionID uuid.UUID) (commontypes.TransactionStatus, error) { + return commontypes.Unknown, fmt.Errorf("not implemented") } -func (w *chainWriter) GetFeeComponents(ctx context.Context) (*types.ChainFeeComponents, error) { +func (w *chainWriter) GetFeeComponents(ctx context.Context) (*commontypes.ChainFeeComponents, error) { return nil, fmt.Errorf("not implemented") } diff --git a/core/services/relay/evm/types/types.go b/core/services/relay/evm/types/types.go index 08eaba3fe1a..42c26cd9b63 100644 --- a/core/services/relay/evm/types/types.go +++ b/core/services/relay/evm/types/types.go @@ -20,6 +20,24 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) +type ChainWriterConfig struct { + Contracts map[string]ChainWriter +} + +type ChainWriter struct { + ContractABI string `json:"contractABI" toml:"contractABI"` + // key is genericName from config + Configs map[string]*ChainWriterDefinition `json:"configs" toml:"configs"` +} + +type ChainWriterDefinition struct { + // chain specific contract method name or event type. + ChainSpecificName string `json:"chainSpecificName"` + Checker string `json:"checker"` + FromAddress common.Address `json:"fromAddress"` + GasLimit uint64 `json:"gasLimit"` // TODO(archseer): what if this has to be configured per call? +} + type ChainReaderConfig struct { // Contracts key is contract name Contracts map[string]ChainContractReader `json:"contracts" toml:"contracts"` From 2dba80e5923bb03e936c22ae24039b37d9a41cd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Fri, 24 May 2024 15:33:41 +0900 Subject: [PATCH 12/36] nix: use monthly foundry branch that's persistent --- flake.lock | 7 ++++--- flake.nix | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/flake.lock b/flake.lock index 260506ff892..da3a69cd248 100644 --- a/flake.lock +++ b/flake.lock @@ -26,15 +26,16 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1715073011, - "narHash": "sha256-fwTWvaOgAUrQwaCcGfeRn1D+n0G4ltr+I+FPb05RPeY=", + "lastModified": 1714727549, + "narHash": "sha256-CWXRTxxcgMfQubJugpeg3yVWIfm70MYTtgaKWKgD60U=", "owner": "shazow", "repo": "foundry.nix", - "rev": "5d2761d546b8712e3faaa416bacc6567007d757a", + "rev": "47cf189ec395eda4b3e0623179d1075c8027ca97", "type": "github" }, "original": { "owner": "shazow", + "ref": "monthly", "repo": "foundry.nix", "type": "github" } diff --git a/flake.nix b/flake.nix index 7ae9a5435bd..f65847455b2 100644 --- a/flake.nix +++ b/flake.nix @@ -3,7 +3,7 @@ inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - foundry.url = "github:shazow/foundry.nix"; + foundry.url = "github:shazow/foundry.nix/monthly"; flake-utils.url = "github:numtide/flake-utils"; foundry.inputs.flake-utils.follows = "flake-utils"; }; From 4ffdb33a32385a4800c9ab5934880bfef7d2c9d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Mon, 27 May 2024 15:09:39 +0900 Subject: [PATCH 13/36] capabilities: Add config validation to write_target --- core/capabilities/targets/write_target.go | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/core/capabilities/targets/write_target.go b/core/capabilities/targets/write_target.go index 7dd8a3cca89..ead03fab9c9 100644 --- a/core/capabilities/targets/write_target.go +++ b/core/capabilities/targets/write_target.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" - "github.com/mitchellh/mapstructure" chainselectors "github.com/smartcontractkit/chain-selectors" @@ -68,20 +67,17 @@ func NewEvmWrite(chain legacyevm.Chain, lggr logger.Logger) *EvmWrite { } type EvmConfig struct { - ChainID uint Address string } -// TODO: enforce required key presence - -func parseConfig(rawConfig *values.Map) (EvmConfig, error) { - var config EvmConfig - configAny, err := rawConfig.Unwrap() - if err != nil { +func parseConfig(rawConfig *values.Map) (config EvmConfig, err error) { + if err := rawConfig.UnwrapTo(&config); err != nil { return config, err } - err = mapstructure.Decode(configAny, &config) - return config, err + if !common.IsHexAddress(config.Address) { + return config, fmt.Errorf("'%v' is not a valid address", config.Address) + } + return config, nil } func (cap *EvmWrite) Execute(ctx context.Context, request capabilities.CapabilityRequest) (<-chan capabilities.CapabilityResponse, error) { From 32fb7997775eab939515d260bfcb2f013ac74ad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Mon, 27 May 2024 15:18:03 +0900 Subject: [PATCH 14/36] capabilities: Pass context into InitializeWrite --- core/capabilities/targets/write_target.go | 4 ++-- core/services/workflows/delegate.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/capabilities/targets/write_target.go b/core/capabilities/targets/write_target.go index ead03fab9c9..8db79a20463 100644 --- a/core/capabilities/targets/write_target.go +++ b/core/capabilities/targets/write_target.go @@ -21,10 +21,10 @@ import ( var forwardABI = evmtypes.MustGetABI(forwarder.KeystoneForwarderMetaData.ABI) -func InitializeWrite(registry core.CapabilitiesRegistry, legacyEVMChains legacyevm.LegacyChainContainer, lggr logger.Logger) error { +func InitializeWrite(ctx context.Context, registry core.CapabilitiesRegistry, legacyEVMChains legacyevm.LegacyChainContainer, lggr logger.Logger) error { for _, chain := range legacyEVMChains.Slice() { capability := NewEvmWrite(chain, lggr) - if err := registry.Add(context.TODO(), capability); err != nil { + if err := registry.Add(ctx, capability); err != nil { return err } } diff --git a/core/services/workflows/delegate.go b/core/services/workflows/delegate.go index 95d2f0ca29d..7dbd4f109a2 100644 --- a/core/services/workflows/delegate.go +++ b/core/services/workflows/delegate.go @@ -43,7 +43,7 @@ func (d *Delegate) OnDeleteJob(context.Context, job.Job) error { return nil } // ServicesForSpec satisfies the job.Delegate interface. func (d *Delegate) ServicesForSpec(ctx context.Context, spec job.Job) ([]job.ServiceCtx, error) { // NOTE: we temporarily do registration inside ServicesForSpec, this will be moved out of job specs in the future - err := targets.InitializeWrite(d.registry, d.legacyEVMChains, d.logger) + err := targets.InitializeWrite(ctx, d.registry, d.legacyEVMChains, d.logger) if err != nil { d.logger.Errorw("could not initialize writes", err) } From 2ef22ccd30bbbe3e636231738eed827cbf1910d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Mon, 27 May 2024 15:18:20 +0900 Subject: [PATCH 15/36] minor: Resolve some inapplicable TODOs --- core/capabilities/targets/write_target.go | 52 +++++++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/core/capabilities/targets/write_target.go b/core/capabilities/targets/write_target.go index 8db79a20463..b45897e3b3e 100644 --- a/core/capabilities/targets/write_target.go +++ b/core/capabilities/targets/write_target.go @@ -80,6 +80,15 @@ func parseConfig(rawConfig *values.Map) (config EvmConfig, err error) { return config, nil } +func success() <-chan capabilities.CapabilityResponse { + callback := make(chan capabilities.CapabilityResponse) + go func() { + callback <- capabilities.CapabilityResponse{} + close(callback) + }() + return callback +} + func (cap *EvmWrite) Execute(ctx context.Context, request capabilities.CapabilityRequest) (<-chan capabilities.CapabilityResponse, error) { cap.lggr.Debugw("Execute", "request", request) // TODO: idempotency @@ -118,6 +127,42 @@ func (cap *EvmWrite) Execute(ctx context.Context, request capabilities.Capabilit // TODO: validate encoded report is prefixed with workflowID and executionID that match the request meta + // Check whether value was already transmitted on chain + cr, err := evm.NewChainReaderService(ctx, cap.lggr, cap.chain.LogPoller(), cap.chain.Client(), relayevmtypes.ChainReaderConfig{ + Contracts: map[string]relayevmtypes.ChainContractReader{ + "forwarder": { + ContractABI: forwarder.KeystoneForwarderABI, + Configs: map[string]*relayevmtypes.ChainReaderDefinition{ + "getTransmitter": { + ChainSpecificName: "getTransmitter", + }, + }, + }, + }, + }) + if err != nil { + return nil, err + } + var transmitter common.Address + cr.Bind(ctx, []commontypes.BoundContract{{ + Address: config.ForwarderAddress().String(), + Name: "forwarder", + }}) + queryInputs := struct { + Receiver string + WorkflowExecutionID []byte + }{ + Receiver: reqConfig.Address, + WorkflowExecutionID: []byte(request.Metadata.WorkflowExecutionID), + } + if err := cr.GetLatestValue(ctx, "forwarder", "getTransmitter", queryInputs, &transmitter); err != nil { + return nil, err + } + if transmitter != common.HexToAddress("0x0") { + // report already transmitted, early return + return success(), nil + } + // construct forwarder payload calldata, err := forwardABI.Pack("report", common.HexToAddress(reqConfig.Address), inputs.Report, inputs.Signatures) if err != nil { @@ -139,9 +184,10 @@ func (cap *EvmWrite) Execute(ctx context.Context, request capabilities.Capabilit EncodedPayload: calldata, FeeLimit: uint64(defaultGasLimit), Meta: txMeta, - Strategy: strategy, - Checker: checker, - // SignalCallback: true, TODO: add code that checks if a workflow id is present, if so, route callback to chainwriter rather than pipeline + Strategy: txmgrcommon.NewSendEveryStrategy(), + Checker: txmgr.TransmitCheckerSpec{ + CheckerType: txmgr.TransmitCheckerTypeSimulate, + }, } tx, err := txm.CreateTransaction(ctx, req) if err != nil { From 5bd4350a6bd98ff2bad744a4851f4b5ebc1185b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Tue, 28 May 2024 16:31:30 +0900 Subject: [PATCH 16/36] capabilities: Refactor write target by extracting commmon bits --- core/capabilities/targets/write_target.go | 175 ++++++++++-------- .../capabilities/targets/write_target_test.go | 16 +- core/services/chainlink/application.go | 2 +- core/services/workflows/delegate.go | 51 ++++- 4 files changed, 153 insertions(+), 91 deletions(-) diff --git a/core/capabilities/targets/write_target.go b/core/capabilities/targets/write_target.go index b45897e3b3e..4584be26b5a 100644 --- a/core/capabilities/targets/write_target.go +++ b/core/capabilities/targets/write_target.go @@ -2,6 +2,7 @@ package targets import ( "context" + "encoding/json" "fmt" "github.com/ethereum/go-ethereum/common" @@ -9,41 +10,33 @@ import ( chainselectors "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/chainlink-common/pkg/capabilities" - "github.com/smartcontractkit/chainlink-common/pkg/types/core" + "github.com/smartcontractkit/chainlink-common/pkg/loop" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/values" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" + evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/forwarder" "github.com/smartcontractkit/chainlink/v2/core/logger" + relayevmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) -var forwardABI = evmtypes.MustGetABI(forwarder.KeystoneForwarderMetaData.ABI) - -func InitializeWrite(ctx context.Context, registry core.CapabilitiesRegistry, legacyEVMChains legacyevm.LegacyChainContainer, lggr logger.Logger) error { - for _, chain := range legacyEVMChains.Slice() { - capability := NewEvmWrite(chain, lggr) - if err := registry.Add(ctx, capability); err != nil { - return err - } - } - return nil -} - var ( - _ capabilities.ActionCapability = &EvmWrite{} + _ capabilities.ActionCapability = &WriteTarget{} ) -const defaultGasLimit = 200000 - -type EvmWrite struct { - chain legacyevm.Chain +type WriteTarget struct { + relayer loop.Relayer + config evmconfig.ChainScopedConfig + cr commontypes.ContractReader + cw EvmChainWriter capabilities.CapabilityInfo lggr logger.Logger } -func NewEvmWrite(chain legacyevm.Chain, lggr logger.Logger) *EvmWrite { +func NewWriteTarget(ctx context.Context, relayer loop.Relayer, chain legacyevm.Chain, lggr logger.Logger) (*WriteTarget, error) { // generate ID based on chain selector name := fmt.Sprintf("write_%v", chain.ID()) chainName, err := chainselectors.NameFromChainId(chain.ID().Uint64()) @@ -59,11 +52,42 @@ func NewEvmWrite(chain legacyevm.Chain, lggr logger.Logger) *EvmWrite { nil, ) - return &EvmWrite{ + // EVM-specific init + config := chain.Config() + + // Initialize a reader to check whether a value was already transmitted on chain + contractReaderConfigEncoded, err := json.Marshal(relayevmtypes.ChainReaderConfig{ + Contracts: map[string]relayevmtypes.ChainContractReader{ + "forwarder": { + ContractABI: forwarder.KeystoneForwarderABI, + Configs: map[string]*relayevmtypes.ChainReaderDefinition{ + "getTransmitter": { + ChainSpecificName: "getTransmitter", + }, + }, + }, + }, + }) + if err != nil { + return nil, fmt.Errorf("failed to marshal contract reader config %v", err) + } + cr, err := relayer.NewContractReader(ctx, contractReaderConfigEncoded) + if err != nil { + return nil, err + } + + cw := EvmChainWriter{ chain, - info, - lggr.Named("EvmWrite"), } + + return &WriteTarget{ + relayer, + config, + cr, + cw, + info, + lggr.Named("WriteTarget"), + }, nil } type EvmConfig struct { @@ -80,6 +104,11 @@ func parseConfig(rawConfig *values.Map) (config EvmConfig, err error) { return config, nil } +var inputs struct { + Report []byte + Signatures [][]byte +} + func success() <-chan capabilities.CapabilityResponse { callback := make(chan capabilities.CapabilityResponse) go func() { @@ -89,23 +118,50 @@ func success() <-chan capabilities.CapabilityResponse { return callback } -func (cap *EvmWrite) Execute(ctx context.Context, request capabilities.CapabilityRequest) (<-chan capabilities.CapabilityResponse, error) { - cap.lggr.Debugw("Execute", "request", request) - // TODO: idempotency +type EvmChainWriter struct { + chain legacyevm.Chain +} - txm := cap.chain.TxManager() +func (cw *EvmChainWriter) CreateTransaction(ctx context.Context, reqConfig EvmConfig) (tx txmgr.Tx, err error) { + txm := cw.chain.TxManager() + config := cw.chain.Config().EVM().ChainWriter() - config := cap.chain.Config().EVM().ChainWriter() + // construct forwarder payload + calldata, err := forwardABI.Pack("report", common.HexToAddress(reqConfig.Address), inputs.Report, inputs.Signatures) + if err != nil { + return tx, err + } + + txMeta := &txmgr.TxMeta{ + // FwdrDestAddress could also be set for better logging but it's used for various purposes around Operator Forwarders + // WorkflowExecutionID: &request.Metadata.WorkflowExecutionID, // TODO: remove? + } + req := txmgr.TxRequest{ + FromAddress: config.FromAddress().Address(), + ToAddress: config.ForwarderAddress().Address(), + EncodedPayload: calldata, + FeeLimit: uint64(defaultGasLimit), + Meta: txMeta, + Strategy: txmgrcommon.NewSendEveryStrategy(), + Checker: txmgr.TransmitCheckerSpec{ + CheckerType: txmgr.TransmitCheckerTypeSimulate, + }, + } + return txm.CreateTransaction(ctx, req) +} + +var forwardABI = evmtypes.MustGetABI(forwarder.KeystoneForwarderMetaData.ABI) + +const defaultGasLimit = 200000 + +func (cap *WriteTarget) Execute(ctx context.Context, request capabilities.CapabilityRequest) (<-chan capabilities.CapabilityResponse, error) { + cap.lggr.Debugw("Execute", "request", request) + // TODO: idempotency reqConfig, err := parseConfig(request.Config) if err != nil { return nil, err } - - var inputs struct { - Report []byte - Signatures [][]byte - } if err = request.Inputs.UnwrapTo(&inputs); err != nil { return nil, err } @@ -127,24 +183,10 @@ func (cap *EvmWrite) Execute(ctx context.Context, request capabilities.Capabilit // TODO: validate encoded report is prefixed with workflowID and executionID that match the request meta + config := cap.config.EVM().ChainWriter() + // Check whether value was already transmitted on chain - cr, err := evm.NewChainReaderService(ctx, cap.lggr, cap.chain.LogPoller(), cap.chain.Client(), relayevmtypes.ChainReaderConfig{ - Contracts: map[string]relayevmtypes.ChainContractReader{ - "forwarder": { - ContractABI: forwarder.KeystoneForwarderABI, - Configs: map[string]*relayevmtypes.ChainReaderDefinition{ - "getTransmitter": { - ChainSpecificName: "getTransmitter", - }, - }, - }, - }, - }) - if err != nil { - return nil, err - } - var transmitter common.Address - cr.Bind(ctx, []commontypes.BoundContract{{ + cap.cr.Bind(ctx, []commontypes.BoundContract{{ Address: config.ForwarderAddress().String(), Name: "forwarder", }}) @@ -155,7 +197,8 @@ func (cap *EvmWrite) Execute(ctx context.Context, request capabilities.Capabilit Receiver: reqConfig.Address, WorkflowExecutionID: []byte(request.Metadata.WorkflowExecutionID), } - if err := cr.GetLatestValue(ctx, "forwarder", "getTransmitter", queryInputs, &transmitter); err != nil { + var transmitter common.Address + if err := cap.cr.GetLatestValue(ctx, "forwarder", "getTransmitter", queryInputs, &transmitter); err != nil { return nil, err } if transmitter != common.HexToAddress("0x0") { @@ -163,33 +206,7 @@ func (cap *EvmWrite) Execute(ctx context.Context, request capabilities.Capabilit return success(), nil } - // construct forwarder payload - calldata, err := forwardABI.Pack("report", common.HexToAddress(reqConfig.Address), inputs.Report, inputs.Signatures) - if err != nil { - return nil, err - } - - txMeta := &txmgr.TxMeta{ - // FwdrDestAddress could also be set for better logging but it's used for various purposes around Operator Forwarders - WorkflowExecutionID: &request.Metadata.WorkflowExecutionID, - } - strategy := txmgrcommon.NewSendEveryStrategy() - - checker := txmgr.TransmitCheckerSpec{ - CheckerType: txmgr.TransmitCheckerTypeSimulate, - } - req := txmgr.TxRequest{ - FromAddress: config.FromAddress().Address(), - ToAddress: config.ForwarderAddress().Address(), - EncodedPayload: calldata, - FeeLimit: uint64(defaultGasLimit), - Meta: txMeta, - Strategy: txmgrcommon.NewSendEveryStrategy(), - Checker: txmgr.TransmitCheckerSpec{ - CheckerType: txmgr.TransmitCheckerTypeSimulate, - }, - } - tx, err := txm.CreateTransaction(ctx, req) + tx, err := cap.cw.CreateTransaction(ctx, reqConfig) if err != nil { return nil, err } @@ -207,10 +224,10 @@ func (cap *EvmWrite) Execute(ctx context.Context, request capabilities.Capabilit return callback, nil } -func (cap *EvmWrite) RegisterToWorkflow(ctx context.Context, request capabilities.RegisterToWorkflowRequest) error { +func (cap *WriteTarget) RegisterToWorkflow(ctx context.Context, request capabilities.RegisterToWorkflowRequest) error { return nil } -func (cap *EvmWrite) UnregisterFromWorkflow(ctx context.Context, request capabilities.UnregisterFromWorkflowRequest) error { +func (cap *WriteTarget) UnregisterFromWorkflow(ctx context.Context, request capabilities.UnregisterFromWorkflowRequest) error { return nil } diff --git a/core/capabilities/targets/write_target_test.go b/core/capabilities/targets/write_target_test.go index f994623ede3..acd61f7cdf1 100644 --- a/core/capabilities/targets/write_target_test.go +++ b/core/capabilities/targets/write_target_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/smartcontractkit/chainlink-common/pkg/capabilities" + relayermocks "github.com/smartcontractkit/chainlink-common/pkg/types/core/mocks" "github.com/smartcontractkit/chainlink-common/pkg/values" "github.com/smartcontractkit/chainlink/v2/core/capabilities/targets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" @@ -17,6 +18,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" + relayevm "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -31,6 +33,14 @@ func TestEvmWrite(t *testing.T) { chain.On("ID").Return(big.NewInt(11155111)) chain.On("TxManager").Return(txManager) + lggr := logger.TestLogger(t) + relayevm.NewRelayer(lggr, chain, evmrelayer.RelayerOpts{ + DS: db, + CSAETHKeystore: keyStore, + CapabilitiesRegistry: capabilities.NewRegistry(lggr), + }) + relayer := relayermocks.NewRelayer(lggr) + cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { a := testutils.NewAddress() addr, err := types.NewEIP55Address(a.Hex()) @@ -45,8 +55,9 @@ func TestEvmWrite(t *testing.T) { evmcfg := evmtest.NewChainScopedConfig(t, cfg) chain.On("Config").Return(evmcfg) - capability := targets.NewEvmWrite(chain, logger.TestLogger(t)) ctx := testutils.Context(t) + capability, err := targets.NewWriteTarget(ctx, relayer, chain, lggr) + require.NoError(t, err) config, err := values.NewMap(map[string]any{}) require.NoError(t, err) @@ -103,8 +114,9 @@ func TestEvmWrite_EmptyReport(t *testing.T) { evmcfg := evmtest.NewChainScopedConfig(t, cfg) chain.On("Config").Return(evmcfg) - capability := targets.NewEvmWrite(chain, logger.TestLogger(t)) ctx := testutils.Context(t) + capability, err := targets.NewWriteTarget(ctx, relayer, chain, logger.TestLogger(t)) + require.NoError(t, err) config, err := values.NewMap(map[string]any{ "abi": "receive(report bytes)", diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index f847a032d81..c0ed37a7b5d 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -395,7 +395,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { delegates[job.Workflow] = workflows.NewDelegate( globalLogger, opts.CapabilitiesRegistry, - legacyEVMChains, + opts.RelayerChainInteroperators, workflowORM, func() *p2ptypes.PeerID { if externalPeerWrapper == nil { diff --git a/core/services/workflows/delegate.go b/core/services/workflows/delegate.go index 7dbd4f109a2..ae555746303 100644 --- a/core/services/workflows/delegate.go +++ b/core/services/workflows/delegate.go @@ -9,6 +9,8 @@ import ( "github.com/pelletier/go-toml" "github.com/smartcontractkit/chainlink-common/pkg/capabilities" + "github.com/smartcontractkit/chainlink-common/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/types/core" "github.com/smartcontractkit/chainlink/v2/core/capabilities/targets" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" @@ -18,12 +20,17 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/workflows/store" ) +type RelayGetter interface { + GetIDToRelayerMap() (map[types.RelayID]loop.Relayer, error) + LegacyEVMChains() legacyevm.LegacyChainContainer +} + type Delegate struct { - registry core.CapabilitiesRegistry - logger logger.Logger - legacyEVMChains legacyevm.LegacyChainContainer - peerID func() *p2ptypes.PeerID - store store.Store + registry core.CapabilitiesRegistry + logger logger.Logger + relayGetter RelayGetter + peerID func() *p2ptypes.PeerID + store store.Store } var _ job.Delegate = (*Delegate)(nil) @@ -40,11 +47,36 @@ func (d *Delegate) BeforeJobDeleted(spec job.Job) {} func (d *Delegate) OnDeleteJob(context.Context, job.Job) error { return nil } +func InitializeWriteTargets(ctx context.Context, registry core.CapabilitiesRegistry, relayGetter RelayGetter, lggr logger.Logger) error { + legacyChains := relayGetter.LegacyEVMChains() + relayers, err := relayGetter.GetIDToRelayerMap() + if err != nil { + return fmt.Errorf("failed to get relayers: %w", err) + } + + for id, relayer := range relayers { + if id.Network != types.NetworkEVM { + continue // only EVM supported for now + } + chain, err := legacyChains.Get(id.ChainID) + if err != nil { + return fmt.Errorf("failed to get chain: %w", err) + } + capability, err := targets.NewWriteTarget(ctx, relayer, chain, lggr) + if err != nil { + return fmt.Errorf("failed to initialize write target: %w", err) + } + if err := registry.Add(ctx, capability); err != nil { + return err + } + } + return nil +} + // ServicesForSpec satisfies the job.Delegate interface. func (d *Delegate) ServicesForSpec(ctx context.Context, spec job.Job) ([]job.ServiceCtx, error) { // NOTE: we temporarily do registration inside ServicesForSpec, this will be moved out of job specs in the future - err := targets.InitializeWrite(ctx, d.registry, d.legacyEVMChains, d.logger) - if err != nil { + if err := InitializeWriteTargets(ctx, d.registry, d.relayGetter, d.logger); err != nil { d.logger.Errorw("could not initialize writes", err) } @@ -104,10 +136,11 @@ func initializeDONInfo(lggr logger.Logger) (*capabilities.DON, error) { SharedSecret: key, }, }, nil + } -func NewDelegate(logger logger.Logger, registry core.CapabilitiesRegistry, legacyEVMChains legacyevm.LegacyChainContainer, store store.Store, peerID func() *p2ptypes.PeerID) *Delegate { - return &Delegate{logger: logger, registry: registry, legacyEVMChains: legacyEVMChains, store: store, peerID: peerID} +func NewDelegate(logger logger.Logger, registry core.CapabilitiesRegistry, relayGetter RelayGetter, store store.Store, peerID func() *p2ptypes.PeerID) *Delegate { + return &Delegate{logger: logger, registry: registry, relayGetter: relayGetter, store: store, peerID: peerID} } func ValidatedWorkflowSpec(tomlString string) (job.Job, error) { From b43daf3aca71d99d31e3b8bbf0754991cdca9aba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Wed, 29 May 2024 15:05:39 +0900 Subject: [PATCH 17/36] Refactor WriteTarget to use ChainWriter --- core/capabilities/targets/write_target.go | 122 ++++++++-------------- 1 file changed, 42 insertions(+), 80 deletions(-) diff --git a/core/capabilities/targets/write_target.go b/core/capabilities/targets/write_target.go index 4584be26b5a..0363f4ba23a 100644 --- a/core/capabilities/targets/write_target.go +++ b/core/capabilities/targets/write_target.go @@ -4,8 +4,10 @@ import ( "context" "encoding/json" "fmt" + "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/google/uuid" chainselectors "github.com/smartcontractkit/chain-selectors" @@ -13,13 +15,10 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/values" - txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" - evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" - evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/forwarder" "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" relayevmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) @@ -28,15 +27,15 @@ var ( ) type WriteTarget struct { - relayer loop.Relayer - config evmconfig.ChainScopedConfig - cr commontypes.ContractReader - cw EvmChainWriter + relayer loop.Relayer + cr commontypes.ContractReader + cw commontypes.ChainWriter + forwarderAddress string capabilities.CapabilityInfo lggr logger.Logger } -func NewWriteTarget(ctx context.Context, relayer loop.Relayer, chain legacyevm.Chain, lggr logger.Logger) (*WriteTarget, error) { +func NewEvmWriteTarget(ctx context.Context, relayer loop.Relayer, chain legacyevm.Chain, lggr logger.Logger) (*WriteTarget, error) { // generate ID based on chain selector name := fmt.Sprintf("write_%v", chain.ID()) chainName, err := chainselectors.NameFromChainId(chain.ID().Uint64()) @@ -53,7 +52,7 @@ func NewWriteTarget(ctx context.Context, relayer loop.Relayer, chain legacyevm.C ) // EVM-specific init - config := chain.Config() + config := chain.Config().EVM().ChainWriter() // Initialize a reader to check whether a value was already transmitted on chain contractReaderConfigEncoded, err := json.Marshal(relayevmtypes.ChainReaderConfig{ @@ -75,18 +74,37 @@ func NewWriteTarget(ctx context.Context, relayer loop.Relayer, chain legacyevm.C if err != nil { return nil, err } + cr.Bind(ctx, []commontypes.BoundContract{{ + Address: config.ForwarderAddress().String(), + Name: "forwarder", + }}) - cw := EvmChainWriter{ - chain, + logger := lggr.Named("WriteTarget") + + chainWriterConfig := relayevmtypes.ChainWriterConfig{ + Contracts: map[string]relayevmtypes.ChainWriter{ + "forwarder": { + ContractABI: forwarder.KeystoneForwarderABI, + Configs: map[string]*relayevmtypes.ChainWriterDefinition{ + "report": { + ChainSpecificName: "report", + Checker: "simulate", + FromAddress: config.FromAddress().Address(), + GasLimit: 200_000, + }, + }, + }, + }, } + cw := evm.NewChainWriterService(logger, chain.Client(), chain.TxManager(), chainWriterConfig) return &WriteTarget{ relayer, - config, cr, cw, + config.ForwarderAddress().String(), info, - lggr.Named("WriteTarget"), + logger, }, nil } @@ -118,45 +136,8 @@ func success() <-chan capabilities.CapabilityResponse { return callback } -type EvmChainWriter struct { - chain legacyevm.Chain -} - -func (cw *EvmChainWriter) CreateTransaction(ctx context.Context, reqConfig EvmConfig) (tx txmgr.Tx, err error) { - txm := cw.chain.TxManager() - config := cw.chain.Config().EVM().ChainWriter() - - // construct forwarder payload - calldata, err := forwardABI.Pack("report", common.HexToAddress(reqConfig.Address), inputs.Report, inputs.Signatures) - if err != nil { - return tx, err - } - - txMeta := &txmgr.TxMeta{ - // FwdrDestAddress could also be set for better logging but it's used for various purposes around Operator Forwarders - // WorkflowExecutionID: &request.Metadata.WorkflowExecutionID, // TODO: remove? - } - req := txmgr.TxRequest{ - FromAddress: config.FromAddress().Address(), - ToAddress: config.ForwarderAddress().Address(), - EncodedPayload: calldata, - FeeLimit: uint64(defaultGasLimit), - Meta: txMeta, - Strategy: txmgrcommon.NewSendEveryStrategy(), - Checker: txmgr.TransmitCheckerSpec{ - CheckerType: txmgr.TransmitCheckerTypeSimulate, - }, - } - return txm.CreateTransaction(ctx, req) -} - -var forwardABI = evmtypes.MustGetABI(forwarder.KeystoneForwarderMetaData.ABI) - -const defaultGasLimit = 200000 - func (cap *WriteTarget) Execute(ctx context.Context, request capabilities.CapabilityRequest) (<-chan capabilities.CapabilityResponse, error) { cap.lggr.Debugw("Execute", "request", request) - // TODO: idempotency reqConfig, err := parseConfig(request.Config) if err != nil { @@ -169,27 +150,12 @@ func (cap *WriteTarget) Execute(ctx context.Context, request capabilities.Capabi if inputs.Report == nil { // We received any empty report -- this means we should skip transmission. cap.lggr.Debugw("Skipping empty report", "request", request) - callback := make(chan capabilities.CapabilityResponse) - go func() { - // TODO: cast tx.Error to Err (or Value to Value?) - callback <- capabilities.CapabilityResponse{ - Value: nil, - Err: nil, - } - close(callback) - }() - return callback, nil + return success(), nil } // TODO: validate encoded report is prefixed with workflowID and executionID that match the request meta - config := cap.config.EVM().ChainWriter() - // Check whether value was already transmitted on chain - cap.cr.Bind(ctx, []commontypes.BoundContract{{ - Address: config.ForwarderAddress().String(), - Name: "forwarder", - }}) queryInputs := struct { Receiver string WorkflowExecutionID []byte @@ -206,22 +172,18 @@ func (cap *WriteTarget) Execute(ctx context.Context, request capabilities.Capabi return success(), nil } - tx, err := cap.cw.CreateTransaction(ctx, reqConfig) + txID, err := uuid.NewUUID() // TODO(archseer): it seems odd that CW expects us to generate an ID, rather than return one if err != nil { return nil, err } - cap.lggr.Debugw("Transaction submitted", "request", request, "transaction", tx) - - callback := make(chan capabilities.CapabilityResponse) - go func() { - // TODO: cast tx.Error to Err (or Value to Value?) - callback <- capabilities.CapabilityResponse{ - Value: nil, - Err: nil, - } - close(callback) - }() - return callback, nil + args := []any{common.HexToAddress(reqConfig.Address), inputs.Report, inputs.Signatures} + meta := commontypes.TxMeta{WorkflowExecutionID: &request.Metadata.WorkflowExecutionID} + value := big.NewInt(0) + if err := cap.cw.SubmitTransaction(ctx, "forwarder", "report", args, txID, cap.forwarderAddress, &meta, *value); err != nil { + return nil, err + } + cap.lggr.Debugw("Transaction submitted", "request", request, "transaction", txID) + return success(), nil } func (cap *WriteTarget) RegisterToWorkflow(ctx context.Context, request capabilities.RegisterToWorkflowRequest) error { From dc86d98c55b7df3ba7596b9bd41c06b6604105a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Wed, 29 May 2024 15:25:01 +0900 Subject: [PATCH 18/36] capabilities: Move evm specific code inside the relayer --- core/capabilities/targets/write_target.go | 70 ++------------------- core/services/chainlink/application.go | 1 - core/services/chainlink/relayer_factory.go | 9 +++ core/services/relay/evm/write_target.go | 72 ++++++++++++++++++++++ core/services/workflows/delegate.go | 53 ++-------------- 5 files changed, 92 insertions(+), 113 deletions(-) create mode 100644 core/services/relay/evm/write_target.go diff --git a/core/capabilities/targets/write_target.go b/core/capabilities/targets/write_target.go index 0363f4ba23a..d71ee5dd4b2 100644 --- a/core/capabilities/targets/write_target.go +++ b/core/capabilities/targets/write_target.go @@ -2,32 +2,25 @@ package targets import ( "context" - "encoding/json" "fmt" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/google/uuid" - chainselectors "github.com/smartcontractkit/chain-selectors" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities" - "github.com/smartcontractkit/chainlink-common/pkg/loop" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/values" - "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/forwarder" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" - relayevmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) var ( _ capabilities.ActionCapability = &WriteTarget{} ) +// NOTE: this code will be moved into chainlink-common once fully abstracted + type WriteTarget struct { - relayer loop.Relayer cr commontypes.ContractReader cw commontypes.ChainWriter forwarderAddress string @@ -35,14 +28,7 @@ type WriteTarget struct { lggr logger.Logger } -func NewEvmWriteTarget(ctx context.Context, relayer loop.Relayer, chain legacyevm.Chain, lggr logger.Logger) (*WriteTarget, error) { - // generate ID based on chain selector - name := fmt.Sprintf("write_%v", chain.ID()) - chainName, err := chainselectors.NameFromChainId(chain.ID().Uint64()) - if err == nil { - name = fmt.Sprintf("write_%v", chainName) - } - +func NewWriteTarget(lggr logger.Logger, name string, cr commontypes.ContractReader, cw commontypes.ChainWriter, forwarderAddress string) *WriteTarget { info := capabilities.MustNewCapabilityInfo( name, capabilities.CapabilityTypeTarget, @@ -51,61 +37,15 @@ func NewEvmWriteTarget(ctx context.Context, relayer loop.Relayer, chain legacyev nil, ) - // EVM-specific init - config := chain.Config().EVM().ChainWriter() - - // Initialize a reader to check whether a value was already transmitted on chain - contractReaderConfigEncoded, err := json.Marshal(relayevmtypes.ChainReaderConfig{ - Contracts: map[string]relayevmtypes.ChainContractReader{ - "forwarder": { - ContractABI: forwarder.KeystoneForwarderABI, - Configs: map[string]*relayevmtypes.ChainReaderDefinition{ - "getTransmitter": { - ChainSpecificName: "getTransmitter", - }, - }, - }, - }, - }) - if err != nil { - return nil, fmt.Errorf("failed to marshal contract reader config %v", err) - } - cr, err := relayer.NewContractReader(ctx, contractReaderConfigEncoded) - if err != nil { - return nil, err - } - cr.Bind(ctx, []commontypes.BoundContract{{ - Address: config.ForwarderAddress().String(), - Name: "forwarder", - }}) - logger := lggr.Named("WriteTarget") - chainWriterConfig := relayevmtypes.ChainWriterConfig{ - Contracts: map[string]relayevmtypes.ChainWriter{ - "forwarder": { - ContractABI: forwarder.KeystoneForwarderABI, - Configs: map[string]*relayevmtypes.ChainWriterDefinition{ - "report": { - ChainSpecificName: "report", - Checker: "simulate", - FromAddress: config.FromAddress().Address(), - GasLimit: 200_000, - }, - }, - }, - }, - } - cw := evm.NewChainWriterService(logger, chain.Client(), chain.TxManager(), chainWriterConfig) - return &WriteTarget{ - relayer, cr, cw, - config.ForwarderAddress().String(), + forwarderAddress, info, logger, - }, nil + } } type EvmConfig struct { diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index c0ed37a7b5d..3eeaaa880ed 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -395,7 +395,6 @@ func NewApplication(opts ApplicationOpts) (Application, error) { delegates[job.Workflow] = workflows.NewDelegate( globalLogger, opts.CapabilitiesRegistry, - opts.RelayerChainInteroperators, workflowORM, func() *p2ptypes.PeerID { if externalPeerWrapper == nil { diff --git a/core/services/chainlink/relayer_factory.go b/core/services/chainlink/relayer_factory.go index bcdb08b8026..7eeec781b03 100644 --- a/core/services/chainlink/relayer_factory.go +++ b/core/services/chainlink/relayer_factory.go @@ -84,6 +84,15 @@ func (r *RelayerFactory) NewEVM(ctx context.Context, config EVMFactoryConfig) (m } relayers[relayID] = evmrelay.NewLoopRelayServerAdapter(relayer, ext) + + // Initialize write target capability + capability, err := evmrelay.NewWriteTarget(ctx, relayer, chain, lggr) + if err != nil { + return nil, fmt.Errorf("failed to initialize write target: %w", err) + } + if err := r.CapabilitiesRegistry.Add(ctx, capability); err != nil { + return nil, err + } } // always return err because it is accumulating individual errors diff --git a/core/services/relay/evm/write_target.go b/core/services/relay/evm/write_target.go new file mode 100644 index 00000000000..def575390be --- /dev/null +++ b/core/services/relay/evm/write_target.go @@ -0,0 +1,72 @@ +package evm + +import ( + "context" + "encoding/json" + "fmt" + + chainselectors "github.com/smartcontractkit/chain-selectors" + + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/capabilities/targets" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/forwarder" + "github.com/smartcontractkit/chainlink/v2/core/logger" + relayevmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" +) + +func NewWriteTarget(ctx context.Context, relayer *Relayer, chain legacyevm.Chain, lggr logger.Logger) (*targets.WriteTarget, error) { + // generate ID based on chain selector + name := fmt.Sprintf("write_%v", chain.ID()) + chainName, err := chainselectors.NameFromChainId(chain.ID().Uint64()) + if err == nil { + name = fmt.Sprintf("write_%v", chainName) + } + + // EVM-specific init + config := chain.Config().EVM().ChainWriter() + + // Initialize a reader to check whether a value was already transmitted on chain + contractReaderConfigEncoded, err := json.Marshal(relayevmtypes.ChainReaderConfig{ + Contracts: map[string]relayevmtypes.ChainContractReader{ + "forwarder": { + ContractABI: forwarder.KeystoneForwarderABI, + Configs: map[string]*relayevmtypes.ChainReaderDefinition{ + "getTransmitter": { + ChainSpecificName: "getTransmitter", + }, + }, + }, + }, + }) + if err != nil { + return nil, fmt.Errorf("failed to marshal contract reader config %v", err) + } + cr, err := relayer.NewContractReader(contractReaderConfigEncoded) + if err != nil { + return nil, err + } + cr.Bind(ctx, []commontypes.BoundContract{{ + Address: config.ForwarderAddress().String(), + Name: "forwarder", + }}) + + chainWriterConfig := relayevmtypes.ChainWriterConfig{ + Contracts: map[string]relayevmtypes.ChainWriter{ + "forwarder": { + ContractABI: forwarder.KeystoneForwarderABI, + Configs: map[string]*relayevmtypes.ChainWriterDefinition{ + "report": { + ChainSpecificName: "report", + Checker: "simulate", + FromAddress: config.FromAddress().Address(), + GasLimit: 200_000, + }, + }, + }, + }, + } + cw := NewChainWriterService(lggr.Named("ChainWriter"), chain.Client(), chain.TxManager(), chainWriterConfig) + + return targets.NewWriteTarget(lggr, name, cr, cw, config.ForwarderAddress().String()), nil +} diff --git a/core/services/workflows/delegate.go b/core/services/workflows/delegate.go index ae555746303..ad51ee41cb2 100644 --- a/core/services/workflows/delegate.go +++ b/core/services/workflows/delegate.go @@ -9,28 +9,18 @@ import ( "github.com/pelletier/go-toml" "github.com/smartcontractkit/chainlink-common/pkg/capabilities" - "github.com/smartcontractkit/chainlink-common/pkg/loop" - "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/types/core" - "github.com/smartcontractkit/chainlink/v2/core/capabilities/targets" - "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" "github.com/smartcontractkit/chainlink/v2/core/services/workflows/store" ) -type RelayGetter interface { - GetIDToRelayerMap() (map[types.RelayID]loop.Relayer, error) - LegacyEVMChains() legacyevm.LegacyChainContainer -} - type Delegate struct { - registry core.CapabilitiesRegistry - logger logger.Logger - relayGetter RelayGetter - peerID func() *p2ptypes.PeerID - store store.Store + registry core.CapabilitiesRegistry + logger logger.Logger + peerID func() *p2ptypes.PeerID + store store.Store } var _ job.Delegate = (*Delegate)(nil) @@ -47,39 +37,8 @@ func (d *Delegate) BeforeJobDeleted(spec job.Job) {} func (d *Delegate) OnDeleteJob(context.Context, job.Job) error { return nil } -func InitializeWriteTargets(ctx context.Context, registry core.CapabilitiesRegistry, relayGetter RelayGetter, lggr logger.Logger) error { - legacyChains := relayGetter.LegacyEVMChains() - relayers, err := relayGetter.GetIDToRelayerMap() - if err != nil { - return fmt.Errorf("failed to get relayers: %w", err) - } - - for id, relayer := range relayers { - if id.Network != types.NetworkEVM { - continue // only EVM supported for now - } - chain, err := legacyChains.Get(id.ChainID) - if err != nil { - return fmt.Errorf("failed to get chain: %w", err) - } - capability, err := targets.NewWriteTarget(ctx, relayer, chain, lggr) - if err != nil { - return fmt.Errorf("failed to initialize write target: %w", err) - } - if err := registry.Add(ctx, capability); err != nil { - return err - } - } - return nil -} - // ServicesForSpec satisfies the job.Delegate interface. func (d *Delegate) ServicesForSpec(ctx context.Context, spec job.Job) ([]job.ServiceCtx, error) { - // NOTE: we temporarily do registration inside ServicesForSpec, this will be moved out of job specs in the future - if err := InitializeWriteTargets(ctx, d.registry, d.relayGetter, d.logger); err != nil { - d.logger.Errorw("could not initialize writes", err) - } - dinfo, err := initializeDONInfo(d.logger) if err != nil { d.logger.Errorw("could not add initialize don info", err) @@ -139,8 +98,8 @@ func initializeDONInfo(lggr logger.Logger) (*capabilities.DON, error) { } -func NewDelegate(logger logger.Logger, registry core.CapabilitiesRegistry, relayGetter RelayGetter, store store.Store, peerID func() *p2ptypes.PeerID) *Delegate { - return &Delegate{logger: logger, registry: registry, relayGetter: relayGetter, store: store, peerID: peerID} +func NewDelegate(logger logger.Logger, registry core.CapabilitiesRegistry, store store.Store, peerID func() *p2ptypes.PeerID) *Delegate { + return &Delegate{logger: logger, registry: registry, store: store, peerID: peerID} } func ValidatedWorkflowSpec(tomlString string) (job.Job, error) { From 1496941d9296878b6663f33f21febef1f8da4c99 Mon Sep 17 00:00:00 2001 From: Silas Lenihan <32529249+silaslenihan@users.noreply.github.com> Date: Thu, 30 May 2024 04:15:42 -0400 Subject: [PATCH 19/36] Chainwriter tests (#13360) * Started relayer evm tests * Evm Relay tests * Added generic tests and additional evm tests for WriteTarget * added changeset * Update real-tools-tap.md * lint * small fix --- .changeset/real-tools-tap.md | 5 + .../targets/mocks/chain_reader.go | 189 ++++++++++++++++ .../targets/mocks/chain_writer.go | 109 ++++++++++ .../capabilities/targets/write_target_test.go | 192 ++++++++-------- core/services/relay/evm/write_target_test.go | 205 ++++++++++++++++++ 5 files changed, 600 insertions(+), 100 deletions(-) create mode 100644 .changeset/real-tools-tap.md create mode 100644 core/capabilities/targets/mocks/chain_reader.go create mode 100644 core/capabilities/targets/mocks/chain_writer.go create mode 100644 core/services/relay/evm/write_target_test.go diff --git a/.changeset/real-tools-tap.md b/.changeset/real-tools-tap.md new file mode 100644 index 00000000000..37a3cf5e581 --- /dev/null +++ b/.changeset/real-tools-tap.md @@ -0,0 +1,5 @@ +--- +"chainlink": minor +--- + +#internal added tests for Chainwriter diff --git a/core/capabilities/targets/mocks/chain_reader.go b/core/capabilities/targets/mocks/chain_reader.go new file mode 100644 index 00000000000..306a305b8e5 --- /dev/null +++ b/core/capabilities/targets/mocks/chain_reader.go @@ -0,0 +1,189 @@ +// Code generated by mockery v2.43.0. DO NOT EDIT. + +package mocks + +import ( + context "context" + + query "github.com/smartcontractkit/chainlink-common/pkg/types/query" + mock "github.com/stretchr/testify/mock" + + types "github.com/smartcontractkit/chainlink-common/pkg/types" +) + +// ChainReader is an autogenerated mock type for the ChainReader type +type ChainReader struct { + mock.Mock +} + +// Bind provides a mock function with given fields: ctx, bindings +func (_m *ChainReader) Bind(ctx context.Context, bindings []types.BoundContract) error { + ret := _m.Called(ctx, bindings) + + if len(ret) == 0 { + panic("no return value specified for Bind") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, []types.BoundContract) error); ok { + r0 = rf(ctx, bindings) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Close provides a mock function with given fields: +func (_m *ChainReader) Close() error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Close") + } + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// GetLatestValue provides a mock function with given fields: ctx, contractName, method, params, returnVal +func (_m *ChainReader) GetLatestValue(ctx context.Context, contractName string, method string, params interface{}, returnVal interface{}) error { + ret := _m.Called(ctx, contractName, method, params, returnVal) + + if len(ret) == 0 { + panic("no return value specified for GetLatestValue") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, interface{}, interface{}) error); ok { + r0 = rf(ctx, contractName, method, params, returnVal) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// HealthReport provides a mock function with given fields: +func (_m *ChainReader) HealthReport() map[string]error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for HealthReport") + } + + var r0 map[string]error + if rf, ok := ret.Get(0).(func() map[string]error); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(map[string]error) + } + } + + return r0 +} + +// Name provides a mock function with given fields: +func (_m *ChainReader) Name() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Name") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// QueryKey provides a mock function with given fields: ctx, contractName, filter, limitAndSort, sequenceDataType +func (_m *ChainReader) QueryKey(ctx context.Context, contractName string, filter query.KeyFilter, limitAndSort query.LimitAndSort, sequenceDataType interface{}) ([]types.Sequence, error) { + ret := _m.Called(ctx, contractName, filter, limitAndSort, sequenceDataType) + + if len(ret) == 0 { + panic("no return value specified for QueryKey") + } + + var r0 []types.Sequence + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, query.KeyFilter, query.LimitAndSort, interface{}) ([]types.Sequence, error)); ok { + return rf(ctx, contractName, filter, limitAndSort, sequenceDataType) + } + if rf, ok := ret.Get(0).(func(context.Context, string, query.KeyFilter, query.LimitAndSort, interface{}) []types.Sequence); ok { + r0 = rf(ctx, contractName, filter, limitAndSort, sequenceDataType) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]types.Sequence) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, query.KeyFilter, query.LimitAndSort, interface{}) error); ok { + r1 = rf(ctx, contractName, filter, limitAndSort, sequenceDataType) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Ready provides a mock function with given fields: +func (_m *ChainReader) Ready() error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Ready") + } + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Start provides a mock function with given fields: _a0 +func (_m *ChainReader) Start(_a0 context.Context) error { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for Start") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// NewChainReader creates a new instance of ChainReader. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewChainReader(t interface { + mock.TestingT + Cleanup(func()) +}) *ChainReader { + mock := &ChainReader{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/capabilities/targets/mocks/chain_writer.go b/core/capabilities/targets/mocks/chain_writer.go new file mode 100644 index 00000000000..379fbb87752 --- /dev/null +++ b/core/capabilities/targets/mocks/chain_writer.go @@ -0,0 +1,109 @@ +// Code generated by mockery v2.43.0. DO NOT EDIT. + +package mocks + +import ( + context "context" + big "math/big" + + mock "github.com/stretchr/testify/mock" + + types "github.com/smartcontractkit/chainlink-common/pkg/types" + + uuid "github.com/google/uuid" +) + +// ChainWriter is an autogenerated mock type for the ChainWriter type +type ChainWriter struct { + mock.Mock +} + +// GetFeeComponents provides a mock function with given fields: ctx +func (_m *ChainWriter) GetFeeComponents(ctx context.Context) (*types.ChainFeeComponents, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetFeeComponents") + } + + var r0 *types.ChainFeeComponents + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (*types.ChainFeeComponents, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) *types.ChainFeeComponents); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.ChainFeeComponents) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetTransactionStatus provides a mock function with given fields: ctx, transactionID +func (_m *ChainWriter) GetTransactionStatus(ctx context.Context, transactionID uuid.UUID) (types.TransactionStatus, error) { + ret := _m.Called(ctx, transactionID) + + if len(ret) == 0 { + panic("no return value specified for GetTransactionStatus") + } + + var r0 types.TransactionStatus + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID) (types.TransactionStatus, error)); ok { + return rf(ctx, transactionID) + } + if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID) types.TransactionStatus); ok { + r0 = rf(ctx, transactionID) + } else { + r0 = ret.Get(0).(types.TransactionStatus) + } + + if rf, ok := ret.Get(1).(func(context.Context, uuid.UUID) error); ok { + r1 = rf(ctx, transactionID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SubmitTransaction provides a mock function with given fields: ctx, contractName, method, args, transactionID, toAddress, meta, value +func (_m *ChainWriter) SubmitTransaction(ctx context.Context, contractName string, method string, args []interface{}, transactionID uuid.UUID, toAddress string, meta *types.TxMeta, value big.Int) error { + ret := _m.Called(ctx, contractName, method, args, transactionID, toAddress, meta, value) + + if len(ret) == 0 { + panic("no return value specified for SubmitTransaction") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, []interface{}, uuid.UUID, string, *types.TxMeta, big.Int) error); ok { + r0 = rf(ctx, contractName, method, args, transactionID, toAddress, meta, value) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// NewChainWriter creates a new instance of ChainWriter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewChainWriter(t interface { + mock.TestingT + Cleanup(func()) +}) *ChainWriter { + mock := &ChainWriter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/capabilities/targets/write_target_test.go b/core/capabilities/targets/write_target_test.go index acd61f7cdf1..d5b6c8feeeb 100644 --- a/core/capabilities/targets/write_target_test.go +++ b/core/capabilities/targets/write_target_test.go @@ -1,145 +1,137 @@ package targets_test import ( - "math/big" + "context" + "errors" + "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "testing" "github.com/smartcontractkit/chainlink-common/pkg/capabilities" - relayermocks "github.com/smartcontractkit/chainlink-common/pkg/types/core/mocks" "github.com/smartcontractkit/chainlink-common/pkg/values" "github.com/smartcontractkit/chainlink/v2/core/capabilities/targets" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" - txmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr/mocks" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - evmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm/mocks" + "github.com/smartcontractkit/chainlink/v2/core/capabilities/targets/mocks" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/forwarder" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - relayevm "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) var forwardABI = types.MustGetABI(forwarder.KeystoneForwarderMetaData.ABI) -func TestEvmWrite(t *testing.T) { - chain := evmmocks.NewChain(t) - - txManager := txmmocks.NewMockEvmTxManager(t) - chain.On("ID").Return(big.NewInt(11155111)) - chain.On("TxManager").Return(txManager) +//go:generate mockery --quiet --name ChainWriter --srcpkg=github.com/smartcontractkit/chainlink-common/pkg/types --output ./mocks/ --case=underscore +//go:generate mockery --quiet --name ChainReader --srcpkg=github.com/smartcontractkit/chainlink-common/pkg/types --output ./mocks/ --case=underscore +func TestWriteTarget(t *testing.T) { lggr := logger.TestLogger(t) - relayevm.NewRelayer(lggr, chain, evmrelayer.RelayerOpts{ - DS: db, - CSAETHKeystore: keyStore, - CapabilitiesRegistry: capabilities.NewRegistry(lggr), - }) - relayer := relayermocks.NewRelayer(lggr) + ctx := context.Background() - cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - a := testutils.NewAddress() - addr, err := types.NewEIP55Address(a.Hex()) - require.NoError(t, err) - c.EVM[0].ChainWriter.FromAddress = &addr + cw := mocks.NewChainWriter(t) + cr := mocks.NewChainReader(t) - forwarderA := testutils.NewAddress() - forwarderAddr, err := types.NewEIP55Address(forwarderA.Hex()) - require.NoError(t, err) - c.EVM[0].ChainWriter.ForwarderAddress = &forwarderAddr - }) - evmcfg := evmtest.NewChainScopedConfig(t, cfg) - chain.On("Config").Return(evmcfg) + forwarderA := testutils.NewAddress() + forwarderAddr := forwarderA.Hex() - ctx := testutils.Context(t) - capability, err := targets.NewWriteTarget(ctx, relayer, chain, lggr) - require.NoError(t, err) + writeTarget := targets.NewWriteTarget(lggr, "Test", cr, cw, forwarderAddr) + require.NotNil(t, writeTarget) - config, err := values.NewMap(map[string]any{}) + config, err := values.NewMap(map[string]any{ + "Address": forwarderAddr, + }) require.NoError(t, err) - inputs, err := values.NewMap(map[string]any{ + validInputs, err := values.NewMap(map[string]any{ "report": []byte{1, 2, 3}, "signatures": [][]byte{}, }) require.NoError(t, err) - req := capabilities.CapabilityRequest{ - Metadata: capabilities.RequestMetadata{ - WorkflowID: "hello", - }, - Config: config, - Inputs: inputs, - } - - txManager.On("CreateTransaction", mock.Anything, mock.Anything).Return(txmgr.Tx{}, nil).Run(func(args mock.Arguments) { - req := args.Get(1).(txmgr.TxRequest) - payload := make(map[string]any) - method := forwardABI.Methods["report"] - err = method.Inputs.UnpackIntoMap(payload, req.EncodedPayload[4:]) - require.NoError(t, err) - require.Equal(t, []byte{0x1, 0x2, 0x3}, payload["rawReport"]) - require.Equal(t, [][]byte{}, payload["signatures"]) - }) + cr.On("GetLatestValue", mock.Anything, "forwarder", "getTransmitter", mock.Anything, mock.Anything).Return(nil).Run(func(args mock.Arguments) { + transmitter := args.Get(4).(*common.Address) + *transmitter = common.HexToAddress("0x0") + }).Twice() - ch, err := capability.Execute(ctx, req) - require.NoError(t, err) + cw.On("SubmitTransaction", mock.Anything, "forwarder", "report", mock.Anything, mock.Anything, forwarderAddr, mock.Anything, mock.Anything).Return(nil).Twice() - response := <-ch - require.Nil(t, response.Err) -} + t.Run("succeeds with valid report", func(t *testing.T) { + req := capabilities.CapabilityRequest{ + Metadata: capabilities.RequestMetadata{ + WorkflowID: "test-id", + }, + Config: config, + Inputs: validInputs, + } -func TestEvmWrite_EmptyReport(t *testing.T) { - chain := evmmocks.NewChain(t) + ch, err := writeTarget.Execute(ctx, req) + require.NoError(t, err) + response := <-ch + require.NotNil(t, response) + }) - txManager := txmmocks.NewMockEvmTxManager(t) - chain.On("ID").Return(big.NewInt(11155111)) - chain.On("TxManager").Return(txManager) + t.Run("succeeds with empty report", func(t *testing.T) { + emptyInputs, err := values.NewMap(map[string]any{ + "report": nil, + "signatures": [][]byte{}, + }) - cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - a := testutils.NewAddress() - addr, err := types.NewEIP55Address(a.Hex()) require.NoError(t, err) - c.EVM[0].ChainWriter.FromAddress = &addr - - forwarderA := testutils.NewAddress() - forwarderAddr, err := types.NewEIP55Address(forwarderA.Hex()) + req := capabilities.CapabilityRequest{ + Metadata: capabilities.RequestMetadata{ + WorkflowExecutionID: "test-id", + }, + Config: config, + Inputs: emptyInputs, + } + + ch, err := writeTarget.Execute(ctx, req) require.NoError(t, err) - c.EVM[0].ChainWriter.ForwarderAddress = &forwarderAddr + response := <-ch + require.Nil(t, response.Value) }) - evmcfg := evmtest.NewChainScopedConfig(t, cfg) - chain.On("Config").Return(evmcfg) - ctx := testutils.Context(t) - capability, err := targets.NewWriteTarget(ctx, relayer, chain, logger.TestLogger(t)) - require.NoError(t, err) - - config, err := values.NewMap(map[string]any{ - "abi": "receive(report bytes)", - "params": []any{"$(report)"}, + t.Run("fails when ChainReader's GetLatestValue returns error", func(t *testing.T) { + req := capabilities.CapabilityRequest{ + Metadata: capabilities.RequestMetadata{ + WorkflowID: "test-id", + }, + Config: config, + Inputs: validInputs, + } + cr.On("GetLatestValue", mock.Anything, "forwarder", "getTransmitter", mock.Anything, mock.Anything).Return(errors.New("reader error")) + + _, err = writeTarget.Execute(ctx, req) + require.Error(t, err) }) - require.NoError(t, err) - inputs, err := values.NewMap(map[string]any{ - "report": nil, + t.Run("fails when ChainWriter's SubmitTransaction returns error", func(t *testing.T) { + req := capabilities.CapabilityRequest{ + Metadata: capabilities.RequestMetadata{ + WorkflowID: "test-id", + }, + Config: config, + Inputs: validInputs, + } + cw.On("SubmitTransaction", mock.Anything, "forwarder", "report", mock.Anything, mock.Anything, forwarderAddr, mock.Anything, mock.Anything).Return(errors.New("writer error")) + + _, err = writeTarget.Execute(ctx, req) + require.Error(t, err) }) - require.NoError(t, err) - - req := capabilities.CapabilityRequest{ - Metadata: capabilities.RequestMetadata{ - WorkflowID: "hello", - }, - Config: config, - Inputs: inputs, - } - ch, err := capability.Execute(ctx, req) - require.NoError(t, err) + t.Run("fails with invalid config", func(t *testing.T) { + invalidConfig, err := values.NewMap(map[string]any{ + "Address": "invalid-address", + }) + require.NoError(t, err) - response := <-ch - require.Nil(t, response.Err) + req := capabilities.CapabilityRequest{ + Metadata: capabilities.RequestMetadata{ + WorkflowID: "test-id", + }, + Config: invalidConfig, + Inputs: validInputs, + } + _, err = writeTarget.Execute(ctx, req) + require.Error(t, err) + }) } diff --git a/core/services/relay/evm/write_target_test.go b/core/services/relay/evm/write_target_test.go new file mode 100644 index 00000000000..73e9b5e8ad9 --- /dev/null +++ b/core/services/relay/evm/write_target_test.go @@ -0,0 +1,205 @@ +package evm_test + +import ( + "errors" + "fmt" + "github.com/smartcontractkit/chainlink-common/pkg/values" + "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" + "math/big" + "testing" + + "github.com/smartcontractkit/chainlink-common/pkg/capabilities" + evmcapabilities "github.com/smartcontractkit/chainlink/v2/core/capabilities" + evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + txmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr/mocks" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + evmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm/mocks" + relayevm "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" + + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/forwarder" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +var forwardABI = types.MustGetABI(forwarder.KeystoneForwarderMetaData.ABI) + +func TestEvmWrite(t *testing.T) { + chain := evmmocks.NewChain(t) + txManager := txmmocks.NewMockEvmTxManager(t) + evmClient := evmclimocks.NewClient(t) + + // This probably isn't the best way to do this, but couldn't find a simpler way to mock the CallContract response + var mockCall []byte + for i := 0; i < 32; i++ { + mockCall = append(mockCall, byte(0)) + } + evmClient.On("CallContract", mock.Anything, mock.Anything, mock.Anything).Return(mockCall, nil) + + chain.On("ID").Return(big.NewInt(11155111)) + chain.On("TxManager").Return(txManager) + chain.On("LogPoller").Return(nil) + chain.On("Client").Return(evmClient) + + db := pgtest.NewSqlxDB(t) + keyStore := cltest.NewKeyStore(t, db) + + lggr := logger.TestLogger(t) + relayer, err := relayevm.NewRelayer(lggr, chain, relayevm.RelayerOpts{ + DS: db, + CSAETHKeystore: keyStore, + CapabilitiesRegistry: evmcapabilities.NewRegistry(lggr), + }) + require.NoError(t, err) + + cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + a := testutils.NewAddress() + addr, err := types.NewEIP55Address(a.Hex()) + require.NoError(t, err) + c.EVM[0].ChainWriter.FromAddress = &addr + + forwarderA := testutils.NewAddress() + forwarderAddr, err := types.NewEIP55Address(forwarderA.Hex()) + require.NoError(t, err) + c.EVM[0].ChainWriter.ForwarderAddress = &forwarderAddr + }) + evmCfg := evmtest.NewChainScopedConfig(t, cfg) + fmt.Println(evmCfg) + chain.On("Config").Return(evmCfg) + + txManager.On("CreateTransaction", mock.Anything, mock.Anything).Return(txmgr.Tx{}, nil).Run(func(args mock.Arguments) { + req := args.Get(1).(txmgr.TxRequest) + payload := make(map[string]any) + method := forwardABI.Methods["report"] + err = method.Inputs.UnpackIntoMap(payload, req.EncodedPayload[4:]) + require.NoError(t, err) + require.Equal(t, []byte{0x1, 0x2, 0x3}, payload["rawReport"]) + require.Equal(t, [][]byte{}, payload["signatures"]) + }).Twice() + + t.Run("succeeds with valid report", func(t *testing.T) { + ctx := testutils.Context(t) + capability, err := evm.NewWriteTarget(ctx, relayer, chain, lggr) + require.NoError(t, err) + + config, err := values.NewMap(map[string]any{ + "Address": evmCfg.EVM().ChainWriter().ForwarderAddress().String(), + }) + require.NoError(t, err) + + inputs, err := values.NewMap(map[string]any{ + "report": []byte{1, 2, 3}, + "signatures": [][]byte{}, + }) + require.NoError(t, err) + + req := capabilities.CapabilityRequest{ + Metadata: capabilities.RequestMetadata{ + WorkflowID: "test-id", + }, + Config: config, + Inputs: inputs, + } + + ch, err := capability.Execute(ctx, req) + require.NoError(t, err) + + response := <-ch + require.Nil(t, response.Err) + }) + + t.Run("succeeds with empty report", func(t *testing.T) { + ctx := testutils.Context(t) + capability, err := evm.NewWriteTarget(ctx, relayer, chain, logger.TestLogger(t)) + require.NoError(t, err) + + config, err := values.NewMap(map[string]any{ + "abi": "receive(report bytes)", + "params": []any{"$(report)"}, + "Address": evmCfg.EVM().ChainWriter().ForwarderAddress().String(), + }) + require.NoError(t, err) + + inputs, err := values.NewMap(map[string]any{ + "report": nil, + }) + require.NoError(t, err) + + req := capabilities.CapabilityRequest{ + Metadata: capabilities.RequestMetadata{ + WorkflowID: "test-id", + }, + Config: config, + Inputs: inputs, + } + + ch, err := capability.Execute(ctx, req) + require.NoError(t, err) + + response := <-ch + require.Nil(t, response.Err) + }) + + t.Run("fails with invalid config", func(t *testing.T) { + ctx := testutils.Context(t) + capability, err := evm.NewWriteTarget(ctx, relayer, chain, logger.TestLogger(t)) + + invalidConfig, err := values.NewMap(map[string]any{ + "Address": "invalid-address", + }) + require.NoError(t, err) + + inputs, err := values.NewMap(map[string]any{ + "report": nil, + }) + require.NoError(t, err) + + req := capabilities.CapabilityRequest{ + Metadata: capabilities.RequestMetadata{ + WorkflowID: "test-id", + }, + Config: invalidConfig, + Inputs: inputs, + } + + _, err = capability.Execute(ctx, req) + require.Error(t, err) + }) + + t.Run("fails when TXM CreateTransaction returns error", func(t *testing.T) { + ctx := testutils.Context(t) + capability, err := evm.NewWriteTarget(ctx, relayer, chain, logger.TestLogger(t)) + require.NoError(t, err) + + config, err := values.NewMap(map[string]any{ + "Address": evmCfg.EVM().ChainWriter().ForwarderAddress().String(), + }) + require.NoError(t, err) + + inputs, err := values.NewMap(map[string]any{ + "report": []byte{1, 2, 3}, + "signatures": [][]byte{}, + }) + require.NoError(t, err) + + req := capabilities.CapabilityRequest{ + Metadata: capabilities.RequestMetadata{ + WorkflowID: "test-id", + }, + Config: config, + Inputs: inputs, + } + + txManager.On("CreateTransaction", mock.Anything, mock.Anything).Return(txmgr.Tx{}, errors.New("TXM error")) + + _, err = capability.Execute(ctx, req) + require.Error(t, err) + }) +} From a5758e704bf24ce318c31a57b3e14c996023a1cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Thu, 30 May 2024 17:28:50 +0900 Subject: [PATCH 20/36] Update chainlink-common to include the new interface --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 58cf2d797df..7d7c28304dc 100644 --- a/go.mod +++ b/go.mod @@ -72,7 +72,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chain-selectors v1.0.10 github.com/smartcontractkit/chainlink-automation v1.0.3 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240528165711-19b9064a1d7e + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240530025940-13f9b3b3d81f github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 diff --git a/go.sum b/go.sum index 2085e3c3fd6..ea6c3a0dd5d 100644 --- a/go.sum +++ b/go.sum @@ -1173,6 +1173,8 @@ github.com/smartcontractkit/chainlink-automation v1.0.3 h1:h/ijT0NiyV06VxYVgcNfs github.com/smartcontractkit/chainlink-automation v1.0.3/go.mod h1:RjboV0Qd7YP+To+OrzHGXaxUxoSONveCoAK2TQ1INLU= github.com/smartcontractkit/chainlink-common v0.1.7-0.20240528165711-19b9064a1d7e h1:+lMjCyABWYAEr0ueTKheYHe9YbUx27UP+zpUOi+7Jz4= github.com/smartcontractkit/chainlink-common v0.1.7-0.20240528165711-19b9064a1d7e/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240530025940-13f9b3b3d81f h1:bwMjZUYYi57vGHah0oVT+JTiiTUjRNRK1nhR57Dd3G4= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240530025940-13f9b3b3d81f/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d h1:5tgMC5Gi2UAOKZ+m28W8ubjLeR0pQCAcrz6eQ0rW510= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d/go.mod h1:0UNuO3nDt9MFsZPaHJBEUolxVkN0iC69j1ccDp95e8k= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= From 912039f65ccc475f9ff97a2e99f8e2500eb83ff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Thu, 30 May 2024 17:38:54 +0900 Subject: [PATCH 21/36] write_target: Fix tests --- core/capabilities/targets/write_target_test.go | 6 +++--- core/services/relay/evm/write_target_test.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/capabilities/targets/write_target_test.go b/core/capabilities/targets/write_target_test.go index 5f65cf65532..9d801bc9cec 100644 --- a/core/capabilities/targets/write_target_test.go +++ b/core/capabilities/targets/write_target_test.go @@ -52,9 +52,9 @@ func TestWriteTarget(t *testing.T) { cr.On("GetLatestValue", mock.Anything, "forwarder", "getTransmitter", mock.Anything, mock.Anything).Return(nil).Run(func(args mock.Arguments) { transmitter := args.Get(4).(*common.Address) *transmitter = common.HexToAddress("0x0") - }).Twice() + }).Once() - cw.On("SubmitTransaction", mock.Anything, "forwarder", "report", mock.Anything, mock.Anything, forwarderAddr, mock.Anything, mock.Anything).Return(nil).Twice() + cw.On("SubmitTransaction", mock.Anything, "forwarder", "report", mock.Anything, mock.Anything, forwarderAddr, mock.Anything, mock.Anything).Return(nil).Once() t.Run("succeeds with valid report", func(t *testing.T) { req := capabilities.CapabilityRequest{ @@ -74,7 +74,7 @@ func TestWriteTarget(t *testing.T) { t.Run("succeeds with empty report", func(t *testing.T) { emptyInputs, err := values.NewMap(map[string]any{ "signed_report": map[string]any{ - "report": nil, + "report": nil, }, "signatures": [][]byte{}, }) diff --git a/core/services/relay/evm/write_target_test.go b/core/services/relay/evm/write_target_test.go index 73e9b5e8ad9..d21c9cd1e7d 100644 --- a/core/services/relay/evm/write_target_test.go +++ b/core/services/relay/evm/write_target_test.go @@ -121,8 +121,6 @@ func TestEvmWrite(t *testing.T) { require.NoError(t, err) config, err := values.NewMap(map[string]any{ - "abi": "receive(report bytes)", - "params": []any{"$(report)"}, "Address": evmCfg.EVM().ChainWriter().ForwarderAddress().String(), }) require.NoError(t, err) @@ -157,7 +155,9 @@ func TestEvmWrite(t *testing.T) { require.NoError(t, err) inputs, err := values.NewMap(map[string]any{ - "report": nil, + "signed_report": map[string]any{ + "report": nil, + }, }) require.NoError(t, err) From a0a74d4b4d1fc78ea06bd15dc6e821e9f77a8313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Thu, 30 May 2024 17:41:57 +0900 Subject: [PATCH 22/36] Move target capability init inside evm.NewRelayer() --- core/services/chainlink/relayer_factory.go | 9 --------- core/services/relay/evm/evm.go | 16 ++++++++++++++-- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/core/services/chainlink/relayer_factory.go b/core/services/chainlink/relayer_factory.go index 7eeec781b03..bcdb08b8026 100644 --- a/core/services/chainlink/relayer_factory.go +++ b/core/services/chainlink/relayer_factory.go @@ -84,15 +84,6 @@ func (r *RelayerFactory) NewEVM(ctx context.Context, config EVMFactoryConfig) (m } relayers[relayID] = evmrelay.NewLoopRelayServerAdapter(relayer, ext) - - // Initialize write target capability - capability, err := evmrelay.NewWriteTarget(ctx, relayer, chain, lggr) - if err != nil { - return nil, fmt.Errorf("failed to initialize write target: %w", err) - } - if err := r.CapabilitiesRegistry.Add(ctx, capability); err != nil { - return nil, err - } } // always return err because it is accumulating individual errors diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index 3f965931596..d3a12715567 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -132,7 +132,7 @@ func NewRelayer(lggr logger.Logger, chain legacyevm.Chain, opts RelayerOpts) (*R mercuryORM := mercury.NewORM(opts.DS) lloORM := llo.NewORM(opts.DS, chain.ID()) cdcFactory := llo.NewChannelDefinitionCacheFactory(lggr, lloORM, chain.LogPoller()) - return &Relayer{ + relayer := &Relayer{ ds: opts.DS, chain: chain, lggr: lggr, @@ -143,7 +143,19 @@ func NewRelayer(lggr logger.Logger, chain legacyevm.Chain, opts RelayerOpts) (*R mercuryORM: mercuryORM, transmitterCfg: opts.TransmitterConfig, capabilitiesRegistry: opts.CapabilitiesRegistry, - }, nil + } + + // Initialize write target capability + ctx := context.Background() + capability, err := NewWriteTarget(ctx, relayer, chain, lggr) + if err != nil { + return nil, fmt.Errorf("failed to initialize write target: %w", err) + } + if err := relayer.capabilitiesRegistry.Add(ctx, capability); err != nil { + return nil, err + } + + return relayer, nil } func (r *Relayer) Name() string { From c5c89ec3a10c1c4987e089a0c2bc3bf1269f243e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Thu, 30 May 2024 17:42:35 +0900 Subject: [PATCH 23/36] go mod tidy --- go.sum | 2 -- 1 file changed, 2 deletions(-) diff --git a/go.sum b/go.sum index ea6c3a0dd5d..331cd0d4545 100644 --- a/go.sum +++ b/go.sum @@ -1171,8 +1171,6 @@ github.com/smartcontractkit/chain-selectors v1.0.10 h1:t9kJeE6B6G+hKD0GYR4kGJSCq github.com/smartcontractkit/chain-selectors v1.0.10/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.3 h1:h/ijT0NiyV06VxYVgcNfsE3+8OEzT3Q0Z9au0z1BPWs= github.com/smartcontractkit/chainlink-automation v1.0.3/go.mod h1:RjboV0Qd7YP+To+OrzHGXaxUxoSONveCoAK2TQ1INLU= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240528165711-19b9064a1d7e h1:+lMjCyABWYAEr0ueTKheYHe9YbUx27UP+zpUOi+7Jz4= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240528165711-19b9064a1d7e/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= github.com/smartcontractkit/chainlink-common v0.1.7-0.20240530025940-13f9b3b3d81f h1:bwMjZUYYi57vGHah0oVT+JTiiTUjRNRK1nhR57Dd3G4= github.com/smartcontractkit/chainlink-common v0.1.7-0.20240530025940-13f9b3b3d81f/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d h1:5tgMC5Gi2UAOKZ+m28W8ubjLeR0pQCAcrz6eQ0rW510= From 5d1f080637cc28255d8c680cf22c9dfa3db73a90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Thu, 30 May 2024 17:53:29 +0900 Subject: [PATCH 24/36] Address lints --- core/capabilities/targets/write_target.go | 2 +- .../capabilities/targets/write_target_test.go | 24 +++++++++---------- core/internal/cltest/factories.go | 7 +++--- core/services/relay/evm/chain_writer.go | 1 + core/services/relay/evm/write_target.go | 5 +++- core/services/relay/evm/write_target_test.go | 19 ++++++++------- 6 files changed, 32 insertions(+), 26 deletions(-) diff --git a/core/capabilities/targets/write_target.go b/core/capabilities/targets/write_target.go index 8ed7fa8df94..1e73d25961b 100644 --- a/core/capabilities/targets/write_target.go +++ b/core/capabilities/targets/write_target.go @@ -110,7 +110,7 @@ func (cap *WriteTarget) Execute(ctx context.Context, request capabilities.Capabi WorkflowExecutionID: []byte(request.Metadata.WorkflowExecutionID), } var transmitter common.Address - if err := cap.cr.GetLatestValue(ctx, "forwarder", "getTransmitter", queryInputs, &transmitter); err != nil { + if err = cap.cr.GetLatestValue(ctx, "forwarder", "getTransmitter", queryInputs, &transmitter); err != nil { return nil, err } if transmitter != common.HexToAddress("0x0") { diff --git a/core/capabilities/targets/write_target_test.go b/core/capabilities/targets/write_target_test.go index 9d801bc9cec..6964a9617ea 100644 --- a/core/capabilities/targets/write_target_test.go +++ b/core/capabilities/targets/write_target_test.go @@ -3,23 +3,21 @@ package targets_test import ( "context" "errors" - "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "testing" + "github.com/ethereum/go-ethereum/common" + + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/capabilities" "github.com/smartcontractkit/chainlink-common/pkg/values" "github.com/smartcontractkit/chainlink/v2/core/capabilities/targets" "github.com/smartcontractkit/chainlink/v2/core/capabilities/targets/mocks" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/forwarder" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" ) -var forwardABI = types.MustGetABI(forwarder.KeystoneForwarderMetaData.ABI) - //go:generate mockery --quiet --name ChainWriter --srcpkg=github.com/smartcontractkit/chainlink-common/pkg/types --output ./mocks/ --case=underscore //go:generate mockery --quiet --name ChainReader --srcpkg=github.com/smartcontractkit/chainlink-common/pkg/types --output ./mocks/ --case=underscore @@ -65,21 +63,21 @@ func TestWriteTarget(t *testing.T) { Inputs: validInputs, } - ch, err := writeTarget.Execute(ctx, req) - require.NoError(t, err) + ch, err2 := writeTarget.Execute(ctx, req) + require.NoError(t, err2) response := <-ch require.NotNil(t, response) }) t.Run("succeeds with empty report", func(t *testing.T) { - emptyInputs, err := values.NewMap(map[string]any{ + emptyInputs, err2 := values.NewMap(map[string]any{ "signed_report": map[string]any{ "report": nil, }, "signatures": [][]byte{}, }) - require.NoError(t, err) + require.NoError(t, err2) req := capabilities.CapabilityRequest{ Metadata: capabilities.RequestMetadata{ WorkflowExecutionID: "test-id", @@ -88,8 +86,8 @@ func TestWriteTarget(t *testing.T) { Inputs: emptyInputs, } - ch, err := writeTarget.Execute(ctx, req) - require.NoError(t, err) + ch, err2 := writeTarget.Execute(ctx, req) + require.NoError(t, err2) response := <-ch require.Nil(t, response.Value) }) diff --git a/core/internal/cltest/factories.go b/core/internal/cltest/factories.go index cd2fa9d9f63..c488dca94a9 100644 --- a/core/internal/cltest/factories.go +++ b/core/internal/cltest/factories.go @@ -262,14 +262,15 @@ type RandomKey struct { func (r RandomKey) MustInsert(t testing.TB, keystore keystore.Eth) (ethkey.KeyV2, common.Address) { ctx := testutils.Context(t) - if r.chainIDs == nil { - r.chainIDs = []ubig.Big{*ubig.New(&FixtureChainID)} + chainIDs := r.chainIDs + if chainIDs == nil { + chainIDs = []ubig.Big{*ubig.New(&FixtureChainID)} } key := MustGenerateRandomKey(t) keystore.XXXTestingOnlyAdd(ctx, key) - for _, cid := range r.chainIDs { + for _, cid := range chainIDs { require.NoError(t, keystore.Add(ctx, key.Address, cid.ToInt())) require.NoError(t, keystore.Enable(ctx, key.Address, cid.ToInt())) if r.Disabled { diff --git a/core/services/relay/evm/chain_writer.go b/core/services/relay/evm/chain_writer.go index 8bb703f8c10..38b8d31e388 100644 --- a/core/services/relay/evm/chain_writer.go +++ b/core/services/relay/evm/chain_writer.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/google/uuid" + commonservices "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" diff --git a/core/services/relay/evm/write_target.go b/core/services/relay/evm/write_target.go index def575390be..29447aacf32 100644 --- a/core/services/relay/evm/write_target.go +++ b/core/services/relay/evm/write_target.go @@ -46,10 +46,13 @@ func NewWriteTarget(ctx context.Context, relayer *Relayer, chain legacyevm.Chain if err != nil { return nil, err } - cr.Bind(ctx, []commontypes.BoundContract{{ + err = cr.Bind(ctx, []commontypes.BoundContract{{ Address: config.ForwarderAddress().String(), Name: "forwarder", }}) + if err != nil { + return nil, err + } chainWriterConfig := relayevmtypes.ChainWriterConfig{ Contracts: map[string]relayevmtypes.ChainWriter{ diff --git a/core/services/relay/evm/write_target_test.go b/core/services/relay/evm/write_target_test.go index d21c9cd1e7d..7303b54656e 100644 --- a/core/services/relay/evm/write_target_test.go +++ b/core/services/relay/evm/write_target_test.go @@ -3,12 +3,13 @@ package evm_test import ( "errors" "fmt" + "math/big" + "testing" + "github.com/smartcontractkit/chainlink-common/pkg/values" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" - "math/big" - "testing" "github.com/smartcontractkit/chainlink-common/pkg/capabilities" evmcapabilities "github.com/smartcontractkit/chainlink/v2/core/capabilities" @@ -19,14 +20,15 @@ import ( evmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm/mocks" relayevm "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/forwarder" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" ) var forwardABI = types.MustGetABI(forwarder.KeystoneForwarderMetaData.ABI) @@ -61,13 +63,13 @@ func TestEvmWrite(t *testing.T) { cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { a := testutils.NewAddress() - addr, err := types.NewEIP55Address(a.Hex()) - require.NoError(t, err) + addr, err2 := types.NewEIP55Address(a.Hex()) + require.NoError(t, err2) c.EVM[0].ChainWriter.FromAddress = &addr forwarderA := testutils.NewAddress() - forwarderAddr, err := types.NewEIP55Address(forwarderA.Hex()) - require.NoError(t, err) + forwarderAddr, err2 := types.NewEIP55Address(forwarderA.Hex()) + require.NoError(t, err2) c.EVM[0].ChainWriter.ForwarderAddress = &forwarderAddr }) evmCfg := evmtest.NewChainScopedConfig(t, cfg) @@ -148,6 +150,7 @@ func TestEvmWrite(t *testing.T) { t.Run("fails with invalid config", func(t *testing.T) { ctx := testutils.Context(t) capability, err := evm.NewWriteTarget(ctx, relayer, chain, logger.TestLogger(t)) + require.NoError(t, err) invalidConfig, err := values.NewMap(map[string]any{ "Address": "invalid-address", From d9912d46dbb38a7199081bc097109932eda79eca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Thu, 30 May 2024 17:55:50 +0900 Subject: [PATCH 25/36] scripts: go mod tidy --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index f9537449089..421cd18d7a6 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -21,7 +21,7 @@ require ( github.com/prometheus/client_golang v1.17.0 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chainlink-automation v1.0.3 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240528165711-19b9064a1d7e + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240530025940-13f9b3b3d81f github.com/smartcontractkit/chainlink-vrf v0.0.0-20240222010609-cd67d123c772 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20240419185742-fd3cab206b2c diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 8788867bc8f..9fa479ae13c 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1185,8 +1185,8 @@ github.com/smartcontractkit/chain-selectors v1.0.10 h1:t9kJeE6B6G+hKD0GYR4kGJSCq github.com/smartcontractkit/chain-selectors v1.0.10/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.3 h1:h/ijT0NiyV06VxYVgcNfsE3+8OEzT3Q0Z9au0z1BPWs= github.com/smartcontractkit/chainlink-automation v1.0.3/go.mod h1:RjboV0Qd7YP+To+OrzHGXaxUxoSONveCoAK2TQ1INLU= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240528165711-19b9064a1d7e h1:+lMjCyABWYAEr0ueTKheYHe9YbUx27UP+zpUOi+7Jz4= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240528165711-19b9064a1d7e/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240530025940-13f9b3b3d81f h1:bwMjZUYYi57vGHah0oVT+JTiiTUjRNRK1nhR57Dd3G4= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240530025940-13f9b3b3d81f/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d h1:5tgMC5Gi2UAOKZ+m28W8ubjLeR0pQCAcrz6eQ0rW510= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d/go.mod h1:0UNuO3nDt9MFsZPaHJBEUolxVkN0iC69j1ccDp95e8k= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= From 5fdd136600fcd8919c5823d25e4d45bd72c556dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Thu, 30 May 2024 18:03:28 +0900 Subject: [PATCH 26/36] integration-tests: go mod tidy --- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 3d6814244c9..a2e2fde3651 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -27,7 +27,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.3 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240528165711-19b9064a1d7e + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240530025940-13f9b3b3d81f github.com/smartcontractkit/chainlink-testing-framework v1.28.17 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 4340728e653..ee4c7c33e05 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1512,8 +1512,8 @@ github.com/smartcontractkit/chain-selectors v1.0.10 h1:t9kJeE6B6G+hKD0GYR4kGJSCq github.com/smartcontractkit/chain-selectors v1.0.10/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.3 h1:h/ijT0NiyV06VxYVgcNfsE3+8OEzT3Q0Z9au0z1BPWs= github.com/smartcontractkit/chainlink-automation v1.0.3/go.mod h1:RjboV0Qd7YP+To+OrzHGXaxUxoSONveCoAK2TQ1INLU= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240528165711-19b9064a1d7e h1:+lMjCyABWYAEr0ueTKheYHe9YbUx27UP+zpUOi+7Jz4= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240528165711-19b9064a1d7e/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240530025940-13f9b3b3d81f h1:bwMjZUYYi57vGHah0oVT+JTiiTUjRNRK1nhR57Dd3G4= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240530025940-13f9b3b3d81f/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d h1:5tgMC5Gi2UAOKZ+m28W8ubjLeR0pQCAcrz6eQ0rW510= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d/go.mod h1:0UNuO3nDt9MFsZPaHJBEUolxVkN0iC69j1ccDp95e8k= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= From 4daba0f652b12ea179a68f0a99b3e2e3b58316b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Thu, 30 May 2024 19:35:35 +0900 Subject: [PATCH 27/36] evm: Only initialize write target if config actually present --- core/services/relay/evm/evm.go | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index d3a12715567..39160e2f8ac 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -145,14 +145,17 @@ func NewRelayer(lggr logger.Logger, chain legacyevm.Chain, opts RelayerOpts) (*R capabilitiesRegistry: opts.CapabilitiesRegistry, } - // Initialize write target capability - ctx := context.Background() - capability, err := NewWriteTarget(ctx, relayer, chain, lggr) - if err != nil { - return nil, fmt.Errorf("failed to initialize write target: %w", err) - } - if err := relayer.capabilitiesRegistry.Add(ctx, capability); err != nil { - return nil, err + // Initialize write target capability if configuration is defined + if chain.Config().EVM().ChainWriter().ForwarderAddress() != nil { + ctx := context.Background() + capability, err := NewWriteTarget(ctx, relayer, chain, lggr) + if err != nil { + return nil, fmt.Errorf("failed to initialize write target: %w", err) + } + if err := relayer.capabilitiesRegistry.Add(ctx, capability); err != nil { + return nil, err + } + lggr.Infow("Registered write target", "chain_id", chain.ID()) } return relayer, nil From 8b683f6a755a413c9684897716e14b395a38126d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Thu, 30 May 2024 19:49:50 +0900 Subject: [PATCH 28/36] more tidy, fix last lint --- core/services/workflows/delegate.go | 1 - integration-tests/load/go.mod | 2 +- integration-tests/load/go.sum | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/core/services/workflows/delegate.go b/core/services/workflows/delegate.go index ad51ee41cb2..cafc2274770 100644 --- a/core/services/workflows/delegate.go +++ b/core/services/workflows/delegate.go @@ -95,7 +95,6 @@ func initializeDONInfo(lggr logger.Logger) (*capabilities.DON, error) { SharedSecret: key, }, }, nil - } func NewDelegate(logger logger.Logger, registry core.CapabilitiesRegistry, store store.Store, peerID func() *p2ptypes.PeerID) *Delegate { diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index 5762df219e4..32658570454 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -16,7 +16,7 @@ require ( github.com/rs/zerolog v1.30.0 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.3 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240528165711-19b9064a1d7e + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240530025940-13f9b3b3d81f github.com/smartcontractkit/chainlink-testing-framework v1.28.17 github.com/smartcontractkit/chainlink/integration-tests v0.0.0-20240214231432-4ad5eb95178c github.com/smartcontractkit/chainlink/v2 v2.9.0-beta0.0.20240216210048-da02459ddad8 diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index c0ca0e3049c..e9e792fa7b6 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1502,8 +1502,8 @@ github.com/smartcontractkit/chain-selectors v1.0.10 h1:t9kJeE6B6G+hKD0GYR4kGJSCq github.com/smartcontractkit/chain-selectors v1.0.10/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.3 h1:h/ijT0NiyV06VxYVgcNfsE3+8OEzT3Q0Z9au0z1BPWs= github.com/smartcontractkit/chainlink-automation v1.0.3/go.mod h1:RjboV0Qd7YP+To+OrzHGXaxUxoSONveCoAK2TQ1INLU= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240528165711-19b9064a1d7e h1:+lMjCyABWYAEr0ueTKheYHe9YbUx27UP+zpUOi+7Jz4= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240528165711-19b9064a1d7e/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240530025940-13f9b3b3d81f h1:bwMjZUYYi57vGHah0oVT+JTiiTUjRNRK1nhR57Dd3G4= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240530025940-13f9b3b3d81f/go.mod h1:DUZccDEW98n+J1mhdWGO7wr/Njad9p9Fzks839JN7Rs= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d h1:5tgMC5Gi2UAOKZ+m28W8ubjLeR0pQCAcrz6eQ0rW510= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240524214833-c362c2ebbd2d/go.mod h1:0UNuO3nDt9MFsZPaHJBEUolxVkN0iC69j1ccDp95e8k= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= From 1125a9d977f3915cdedc4169596d5b9a9489c78f Mon Sep 17 00:00:00 2001 From: Nick Corin Date: Thu, 30 May 2024 17:55:52 +0200 Subject: [PATCH 29/36] evm: Move send strategy to be config driven --- core/services/relay/evm/chain_writer.go | 20 +++++++++----------- core/services/relay/evm/types/types.go | 4 +++- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/core/services/relay/evm/chain_writer.go b/core/services/relay/evm/chain_writer.go index 38b8d31e388..4734a7f45d8 100644 --- a/core/services/relay/evm/chain_writer.go +++ b/core/services/relay/evm/chain_writer.go @@ -43,21 +43,20 @@ type chainWriter struct { txm evmtxmgr.TxManager } -func (w *chainWriter) SubmitTransaction(ctx context.Context, contractName, method string, args []any, transactionID uuid.UUID, toAddress string, meta *commontypes.TxMeta, value big.Int) error { +func (w *chainWriter) SubmitTransaction(ctx context.Context, contract, method string, args []any, transactionID uuid.UUID, toAddress string, meta *commontypes.TxMeta, value big.Int) error { if !common.IsHexAddress(toAddress) { return fmt.Errorf("toAddress is not a valid ethereum address: %v", toAddress) } - contractConfig, ok := w.config.Contracts[contractName] + contractConfig, ok := w.config.Contracts[contract] if !ok { - return fmt.Errorf("contract config not found: %v", contractName) + return fmt.Errorf("contract config not found: %v", contract) } methodConfig, ok := contractConfig.Configs[method] if !ok { return fmt.Errorf("method config not found: %v", method) } - // TODO(archseer): do this at init time once forwarderABI := evmtypes.MustGetABI(contractConfig.ContractABI) calldata, err := forwarderABI.Pack(methodConfig.ChainSpecificName, args...) @@ -65,14 +64,16 @@ func (w *chainWriter) SubmitTransaction(ctx context.Context, contractName, metho return fmt.Errorf("pack forwarder abi: %w", err) } - // TODO(nickcorin): Change this to be config driven. - sendStrategy := txmgr.SendEveryStrategy{} - var checker evmtxmgr.TransmitCheckerSpec if methodConfig.Checker != "" { checker.CheckerType = txmgrtypes.TransmitCheckerType(methodConfig.Checker) } + var sendStrategy txmgrtypes.TxStrategy = txmgr.SendEveryStrategy{} + if w.config.SendStrategy != nil { + sendStrategy = w.config.SendStrategy + } + req := evmtxmgr.TxRequest{ FromAddress: methodConfig.FromAddress, ToAddress: common.HexToAddress(toAddress), @@ -104,7 +105,6 @@ func (w *chainWriter) Close() error { _, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - // TODO(nickcorin): Add shutdown steps here. return nil }) } @@ -120,13 +120,11 @@ func (w *chainWriter) Name() string { } func (w *chainWriter) Ready() error { - // TODO(nickcorin): Return nil here once the implementation is done. - return fmt.Errorf("not fully implemented") + return nil } func (w *chainWriter) Start(ctx context.Context) error { return w.StartOnce(w.Name(), func() error { - // TODO(nickcorin): Add startup steps here. return nil }) } diff --git a/core/services/relay/evm/types/types.go b/core/services/relay/evm/types/types.go index 0a95ec2abe6..47f5a2765ff 100644 --- a/core/services/relay/evm/types/types.go +++ b/core/services/relay/evm/types/types.go @@ -11,6 +11,7 @@ import ( "github.com/lib/pq" "gopkg.in/guregu/null.v4" + txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/chainlink-common/pkg/codec" @@ -21,7 +22,8 @@ import ( ) type ChainWriterConfig struct { - Contracts map[string]ChainWriter + SendStrategy txmgrtypes.TxStrategy + Contracts map[string]ChainWriter } type ChainWriter struct { From 7da8ad5b925a53d07f91c16bb6a2504c4f53c3cb Mon Sep 17 00:00:00 2001 From: Nick Corin Date: Thu, 30 May 2024 23:02:42 +0200 Subject: [PATCH 30/36] evm: Add some todos back --- core/services/relay/evm/chain_writer.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/services/relay/evm/chain_writer.go b/core/services/relay/evm/chain_writer.go index 4734a7f45d8..3b73164c902 100644 --- a/core/services/relay/evm/chain_writer.go +++ b/core/services/relay/evm/chain_writer.go @@ -48,10 +48,12 @@ func (w *chainWriter) SubmitTransaction(ctx context.Context, contract, method st return fmt.Errorf("toAddress is not a valid ethereum address: %v", toAddress) } + // TODO(nickcorin): Pre-process the contracts when initialising the chain writer. contractConfig, ok := w.config.Contracts[contract] if !ok { return fmt.Errorf("contract config not found: %v", contract) } + methodConfig, ok := contractConfig.Configs[method] if !ok { return fmt.Errorf("method config not found: %v", method) @@ -105,6 +107,7 @@ func (w *chainWriter) Close() error { _, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() + // TODO(nickcorin): Add shutdown steps here. return nil }) } From 3d0bf8bd6e5ce7e012f1fb5946b0c872ea7090d8 Mon Sep 17 00:00:00 2001 From: Nick Corin Date: Thu, 30 May 2024 23:19:00 +0200 Subject: [PATCH 31/36] .changeset: Add a changeset --- .changeset/large-plants-count.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/large-plants-count.md diff --git a/.changeset/large-plants-count.md b/.changeset/large-plants-count.md new file mode 100644 index 00000000000..e300a33d3e4 --- /dev/null +++ b/.changeset/large-plants-count.md @@ -0,0 +1,5 @@ +--- +"chainlink": minor +--- + +#internal Added a configuration option to chain writer to set the tx send strategy. From 514a7fe6f5586e1450e77bf83e614e2dc4c25d06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Fri, 31 May 2024 00:37:35 +0900 Subject: [PATCH 32/36] write_target: Mock out Config before NewRelayer is called --- core/services/relay/evm/write_target_test.go | 25 ++++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/core/services/relay/evm/write_target_test.go b/core/services/relay/evm/write_target_test.go index 7303b54656e..ee52e755672 100644 --- a/core/services/relay/evm/write_target_test.go +++ b/core/services/relay/evm/write_target_test.go @@ -2,7 +2,6 @@ package evm_test import ( "errors" - "fmt" "math/big" "testing" @@ -50,17 +49,6 @@ func TestEvmWrite(t *testing.T) { chain.On("LogPoller").Return(nil) chain.On("Client").Return(evmClient) - db := pgtest.NewSqlxDB(t) - keyStore := cltest.NewKeyStore(t, db) - - lggr := logger.TestLogger(t) - relayer, err := relayevm.NewRelayer(lggr, chain, relayevm.RelayerOpts{ - DS: db, - CSAETHKeystore: keyStore, - CapabilitiesRegistry: evmcapabilities.NewRegistry(lggr), - }) - require.NoError(t, err) - cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { a := testutils.NewAddress() addr, err2 := types.NewEIP55Address(a.Hex()) @@ -73,9 +61,20 @@ func TestEvmWrite(t *testing.T) { c.EVM[0].ChainWriter.ForwarderAddress = &forwarderAddr }) evmCfg := evmtest.NewChainScopedConfig(t, cfg) - fmt.Println(evmCfg) + chain.On("Config").Return(evmCfg) + db := pgtest.NewSqlxDB(t) + keyStore := cltest.NewKeyStore(t, db) + + lggr := logger.TestLogger(t) + relayer, err := relayevm.NewRelayer(lggr, chain, relayevm.RelayerOpts{ + DS: db, + CSAETHKeystore: keyStore, + CapabilitiesRegistry: evmcapabilities.NewRegistry(lggr), + }) + require.NoError(t, err) + txManager.On("CreateTransaction", mock.Anything, mock.Anything).Return(txmgr.Tx{}, nil).Run(func(args mock.Arguments) { req := args.Get(1).(txmgr.TxRequest) payload := make(map[string]any) From 3abb386d5efa686188bcc8406457bfd0da8658fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Fri, 31 May 2024 00:40:27 +0900 Subject: [PATCH 33/36] Regenerate mocks with right version --- core/capabilities/targets/mocks/chain_reader.go | 2 +- core/capabilities/targets/mocks/chain_writer.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/capabilities/targets/mocks/chain_reader.go b/core/capabilities/targets/mocks/chain_reader.go index 306a305b8e5..14748c16bb8 100644 --- a/core/capabilities/targets/mocks/chain_reader.go +++ b/core/capabilities/targets/mocks/chain_reader.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.43.0. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package mocks diff --git a/core/capabilities/targets/mocks/chain_writer.go b/core/capabilities/targets/mocks/chain_writer.go index 379fbb87752..3d8efae8451 100644 --- a/core/capabilities/targets/mocks/chain_writer.go +++ b/core/capabilities/targets/mocks/chain_writer.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.43.0. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package mocks From 8c504d157b77ec5024c871e07736c931462e015f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Fri, 31 May 2024 11:00:21 +0900 Subject: [PATCH 34/36] fix evm tests --- core/services/relay/evm/write_target_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/services/relay/evm/write_target_test.go b/core/services/relay/evm/write_target_test.go index ee52e755672..551a68beb0f 100644 --- a/core/services/relay/evm/write_target_test.go +++ b/core/services/relay/evm/write_target_test.go @@ -83,7 +83,7 @@ func TestEvmWrite(t *testing.T) { require.NoError(t, err) require.Equal(t, []byte{0x1, 0x2, 0x3}, payload["rawReport"]) require.Equal(t, [][]byte{}, payload["signatures"]) - }).Twice() + }).Once() t.Run("succeeds with valid report", func(t *testing.T) { ctx := testutils.Context(t) @@ -127,7 +127,9 @@ func TestEvmWrite(t *testing.T) { require.NoError(t, err) inputs, err := values.NewMap(map[string]any{ - "report": nil, + "signed_report": map[string]any{ + "report": nil, + }, }) require.NoError(t, err) From 17b34f1ac0314c3d9e4453bd96701634383d0fed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Fri, 31 May 2024 11:00:55 +0900 Subject: [PATCH 35/36] goimports --- core/services/relay/evm/types/types.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/services/relay/evm/types/types.go b/core/services/relay/evm/types/types.go index 47f5a2765ff..8f4a2db864c 100644 --- a/core/services/relay/evm/types/types.go +++ b/core/services/relay/evm/types/types.go @@ -11,9 +11,10 @@ import ( "github.com/lib/pq" "gopkg.in/guregu/null.v4" - txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" + "github.com/smartcontractkit/chainlink-common/pkg/codec" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/types" From a38547ab4b94b30cc4e2d62e6b3e1a084e982022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Fri, 31 May 2024 12:28:47 +0900 Subject: [PATCH 36/36] fix evm tests --- core/services/relay/evm/write_target_test.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/core/services/relay/evm/write_target_test.go b/core/services/relay/evm/write_target_test.go index 551a68beb0f..331a30606cc 100644 --- a/core/services/relay/evm/write_target_test.go +++ b/core/services/relay/evm/write_target_test.go @@ -42,7 +42,7 @@ func TestEvmWrite(t *testing.T) { for i := 0; i < 32; i++ { mockCall = append(mockCall, byte(0)) } - evmClient.On("CallContract", mock.Anything, mock.Anything, mock.Anything).Return(mockCall, nil) + evmClient.On("CallContract", mock.Anything, mock.Anything, mock.Anything).Return(mockCall, nil).Maybe() chain.On("ID").Return(big.NewInt(11155111)) chain.On("TxManager").Return(txManager) @@ -96,8 +96,10 @@ func TestEvmWrite(t *testing.T) { require.NoError(t, err) inputs, err := values.NewMap(map[string]any{ - "report": []byte{1, 2, 3}, - "signatures": [][]byte{}, + "signed_report": map[string]any{ + "report": []byte{1, 2, 3}, + "signatures": [][]byte{}, + }, }) require.NoError(t, err) @@ -188,8 +190,10 @@ func TestEvmWrite(t *testing.T) { require.NoError(t, err) inputs, err := values.NewMap(map[string]any{ - "report": []byte{1, 2, 3}, - "signatures": [][]byte{}, + "signed_report": map[string]any{ + "report": []byte{1, 2, 3}, + "signatures": [][]byte{}, + }, }) require.NoError(t, err)