diff --git a/common/client/mock_node_selector_test.go b/common/client/mock_node_selector_test.go index 6420cdef2f2..c01895a0ee7 100644 --- a/common/client/mock_node_selector_test.go +++ b/common/client/mock_node_selector_test.go @@ -25,7 +25,7 @@ func (_m *mockNodeSelector[CHAIN_ID, RPC]) Name() string { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -38,14 +38,14 @@ func (_m *mockNodeSelector[CHAIN_ID, RPC]) Name() string { return r0 } -// mockNodeSelector_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// mockNodeSelector_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type mockNodeSelector_Name_Call[CHAIN_ID types.ID, RPC any] struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *mockNodeSelector_Expecter[CHAIN_ID, RPC]) Name() *mockNodeSelector_Name_Call[CHAIN_ID, RPC] { - return &mockNodeSelector_Name_Call[CHAIN_ID, RPC]{Call: _e.mock.On("String")} + return &mockNodeSelector_Name_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Name")} } func (_c *mockNodeSelector_Name_Call[CHAIN_ID, RPC]) Run(run func()) *mockNodeSelector_Name_Call[CHAIN_ID, RPC] { diff --git a/common/client/mock_node_test.go b/common/client/mock_node_test.go index 90c158d31cd..e5b090ab641 100644 --- a/common/client/mock_node_test.go +++ b/common/client/mock_node_test.go @@ -162,7 +162,7 @@ func (_m *mockNode[CHAIN_ID, RPC]) Name() string { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -175,14 +175,14 @@ func (_m *mockNode[CHAIN_ID, RPC]) Name() string { return r0 } -// mockNode_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// mockNode_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type mockNode_Name_Call[CHAIN_ID types.ID, RPC any] struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *mockNode_Expecter[CHAIN_ID, RPC]) Name() *mockNode_Name_Call[CHAIN_ID, RPC] { - return &mockNode_Name_Call[CHAIN_ID, RPC]{Call: _e.mock.On("String")} + return &mockNode_Name_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Name")} } func (_c *mockNode_Name_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_Name_Call[CHAIN_ID, RPC] { diff --git a/common/client/mock_send_only_node_test.go b/common/client/mock_send_only_node_test.go index 162d64c553f..98a6f0ba2fb 100644 --- a/common/client/mock_send_only_node_test.go +++ b/common/client/mock_send_only_node_test.go @@ -117,7 +117,7 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Name() string { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -130,14 +130,14 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Name() string { return r0 } -// mockSendOnlyNode_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// mockSendOnlyNode_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type mockSendOnlyNode_Name_Call[CHAIN_ID types.ID, RPC any] struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *mockSendOnlyNode_Expecter[CHAIN_ID, RPC]) Name() *mockSendOnlyNode_Name_Call[CHAIN_ID, RPC] { - return &mockSendOnlyNode_Name_Call[CHAIN_ID, RPC]{Call: _e.mock.On("String")} + return &mockSendOnlyNode_Name_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Name")} } func (_c *mockSendOnlyNode_Name_Call[CHAIN_ID, RPC]) Run(run func()) *mockSendOnlyNode_Name_Call[CHAIN_ID, RPC] { diff --git a/common/headtracker/mocks/head_broadcaster.go b/common/headtracker/mocks/head_broadcaster.go index 204e5a0d435..87ac609e605 100644 --- a/common/headtracker/mocks/head_broadcaster.go +++ b/common/headtracker/mocks/head_broadcaster.go @@ -154,7 +154,7 @@ func (_m *HeadBroadcaster[H, BLOCK_HASH]) Name() string { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -167,14 +167,14 @@ func (_m *HeadBroadcaster[H, BLOCK_HASH]) Name() string { return r0 } -// HeadBroadcaster_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// HeadBroadcaster_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type HeadBroadcaster_Name_Call[H types.Head[BLOCK_HASH], BLOCK_HASH types.Hashable] struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *HeadBroadcaster_Expecter[H, BLOCK_HASH]) Name() *HeadBroadcaster_Name_Call[H, BLOCK_HASH] { - return &HeadBroadcaster_Name_Call[H, BLOCK_HASH]{Call: _e.mock.On("String")} + return &HeadBroadcaster_Name_Call[H, BLOCK_HASH]{Call: _e.mock.On("Name")} } func (_c *HeadBroadcaster_Name_Call[H, BLOCK_HASH]) Run(run func()) *HeadBroadcaster_Name_Call[H, BLOCK_HASH] { diff --git a/common/headtracker/mocks/head_tracker.go b/common/headtracker/mocks/head_tracker.go index 59985aa7942..e0541bdd786 100644 --- a/common/headtracker/mocks/head_tracker.go +++ b/common/headtracker/mocks/head_tracker.go @@ -275,7 +275,7 @@ func (_m *HeadTracker[H, BLOCK_HASH]) Name() string { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -288,14 +288,14 @@ func (_m *HeadTracker[H, BLOCK_HASH]) Name() string { return r0 } -// HeadTracker_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// HeadTracker_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type HeadTracker_Name_Call[H types.Head[BLOCK_HASH], BLOCK_HASH types.Hashable] struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *HeadTracker_Expecter[H, BLOCK_HASH]) Name() *HeadTracker_Name_Call[H, BLOCK_HASH] { - return &HeadTracker_Name_Call[H, BLOCK_HASH]{Call: _e.mock.On("String")} + return &HeadTracker_Name_Call[H, BLOCK_HASH]{Call: _e.mock.On("Name")} } func (_c *HeadTracker_Name_Call[H, BLOCK_HASH]) Run(run func()) *HeadTracker_Name_Call[H, BLOCK_HASH] { diff --git a/common/txmgr/mocks/tx_manager.go b/common/txmgr/mocks/tx_manager.go index b03cf6b9acf..a296158a005 100644 --- a/common/txmgr/mocks/tx_manager.go +++ b/common/txmgr/mocks/tx_manager.go @@ -773,7 +773,7 @@ func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Name() ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -786,14 +786,14 @@ func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Name() return r0 } -// TxManager_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// TxManager_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type TxManager_Name_Call[CHAIN_ID types.ID, HEAD types.Head[BLOCK_HASH], ADDR types.Hashable, TX_HASH types.Hashable, BLOCK_HASH types.Hashable, SEQ types.Sequence, FEE feetypes.Fee] struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *TxManager_Expecter[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Name() *TxManager_Name_Call[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] { - return &TxManager_Name_Call[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]{Call: _e.mock.On("String")} + return &TxManager_Name_Call[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]{Call: _e.mock.On("Name")} } func (_c *TxManager_Name_Call[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Run(run func()) *TxManager_Name_Call[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] { diff --git a/common/txmgr/types/mocks/forwarder_manager.go b/common/txmgr/types/mocks/forwarder_manager.go index ba934252fcd..4582e4bac07 100644 --- a/common/txmgr/types/mocks/forwarder_manager.go +++ b/common/txmgr/types/mocks/forwarder_manager.go @@ -294,7 +294,7 @@ func (_m *ForwarderManager[ADDR]) Name() string { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -307,14 +307,14 @@ func (_m *ForwarderManager[ADDR]) Name() string { return r0 } -// ForwarderManager_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// ForwarderManager_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type ForwarderManager_Name_Call[ADDR types.Hashable] struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *ForwarderManager_Expecter[ADDR]) Name() *ForwarderManager_Name_Call[ADDR] { - return &ForwarderManager_Name_Call[ADDR]{Call: _e.mock.On("String")} + return &ForwarderManager_Name_Call[ADDR]{Call: _e.mock.On("Name")} } func (_c *ForwarderManager_Name_Call[ADDR]) Run(run func()) *ForwarderManager_Name_Call[ADDR] { diff --git a/common/txmgr/types/mocks/tx_attempt_builder.go b/common/txmgr/types/mocks/tx_attempt_builder.go index 3371828fc90..746e580413c 100644 --- a/common/txmgr/types/mocks/tx_attempt_builder.go +++ b/common/txmgr/types/mocks/tx_attempt_builder.go @@ -125,7 +125,7 @@ func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -138,14 +138,14 @@ func (_m *TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) return r0 } -// TxAttemptBuilder_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// TxAttemptBuilder_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type TxAttemptBuilder_Name_Call[CHAIN_ID types.ID, HEAD types.Head[BLOCK_HASH], ADDR types.Hashable, TX_HASH types.Hashable, BLOCK_HASH types.Hashable, SEQ types.Sequence, FEE feetypes.Fee] struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *TxAttemptBuilder_Expecter[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Name() *TxAttemptBuilder_Name_Call[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] { - return &TxAttemptBuilder_Name_Call[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]{Call: _e.mock.On("String")} + return &TxAttemptBuilder_Name_Call[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]{Call: _e.mock.On("Name")} } func (_c *TxAttemptBuilder_Name_Call[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Run(run func()) *TxAttemptBuilder_Name_Call[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] { diff --git a/core/capabilities/ccip/oraclecreator/plugin.go b/core/capabilities/ccip/oraclecreator/plugin.go index f8868b5d9b9..1b8c6344349 100644 --- a/core/capabilities/ccip/oraclecreator/plugin.go +++ b/core/capabilities/ccip/oraclecreator/plugin.go @@ -22,6 +22,7 @@ import ( cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr3/promwrapper" "github.com/smartcontractkit/libocr/commontypes" libocr3 "github.com/smartcontractkit/libocr/offchainreporting2plus" @@ -229,6 +230,12 @@ func (i *pluginOracleCreator) createFactoryAndTransmitter( ) (ocr3types.ReportingPluginFactory[[]byte], ocr3types.ContractTransmitter[[]byte], error) { var factory ocr3types.ReportingPluginFactory[[]byte] var transmitter ocr3types.ContractTransmitter[[]byte] + + chainID, err := chainsel.GetChainIDFromSelector(uint64(config.Config.ChainSelector)) + if err != nil { + return nil, nil, fmt.Errorf("unsupported chain selector %d %w", config.Config.ChainSelector, err) + } + if config.Config.PluginType == uint8(cctypes.PluginTypeCCIPCommit) { if !i.peerWrapper.IsStarted() { return nil, nil, fmt.Errorf("peer wrapper is not started") @@ -263,6 +270,7 @@ func (i *pluginOracleCreator) createFactoryAndTransmitter( rmnPeerClient, rmnCrypto, ) + factory = promwrapper.NewReportingPluginFactory[[]byte](factory, chainID, "CCIPCommit") transmitter = ocrimpls.NewCommitContractTransmitter[[]byte](destChainWriter, ocrtypes.Account(destFromAccounts[0]), hexutil.Encode(config.Config.OfframpAddress), // TODO: this works for evm only, how about non-evm? @@ -283,6 +291,7 @@ func (i *pluginOracleCreator) createFactoryAndTransmitter( contractReaders, chainWriters, ) + factory = promwrapper.NewReportingPluginFactory[[]byte](factory, chainID, "CCIPExec") transmitter = ocrimpls.NewExecContractTransmitter[[]byte](destChainWriter, ocrtypes.Account(destFromAccounts[0]), hexutil.Encode(config.Config.OfframpAddress), // TODO: this works for evm only, how about non-evm? diff --git a/core/capabilities/compute/compute.go b/core/capabilities/compute/compute.go index 32e43e8d62e..316e4f00eea 100644 --- a/core/capabilities/compute/compute.go +++ b/core/capabilities/compute/compute.go @@ -273,9 +273,19 @@ func (c *Compute) worker(ctx context.Context) { } func (c *Compute) Close() error { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + c.modules.close() close(c.stopCh) + + err := c.registry.Remove(ctx, CapabilityIDCompute) + if err != nil { + return err + } + c.wg.Wait() + return nil } diff --git a/core/capabilities/registry.go b/core/capabilities/registry.go index 47285505805..7038dcdb4b7 100644 --- a/core/capabilities/registry.go +++ b/core/capabilities/registry.go @@ -193,6 +193,21 @@ func (r *Registry) Add(ctx context.Context, c capabilities.BaseCapability) error return nil } +// Add adds a capability to the registry. +func (r *Registry) Remove(ctx context.Context, id string) error { + r.mu.Lock() + defer r.mu.Unlock() + + _, ok := r.m[id] + if !ok { + return fmt.Errorf("unable to remove, capability not found: %s", id) + } + + delete(r.m, id) + r.lggr.Infow("capability removed", "id", id) + return nil +} + // NewRegistry returns a new Registry. func NewRegistry(lggr logger.Logger) *Registry { return &Registry{ diff --git a/core/capabilities/remote/types/mocks/dispatcher.go b/core/capabilities/remote/types/mocks/dispatcher.go index 8cdefda707d..0948698b935 100644 --- a/core/capabilities/remote/types/mocks/dispatcher.go +++ b/core/capabilities/remote/types/mocks/dispatcher.go @@ -121,7 +121,7 @@ func (_m *Dispatcher) Name() string { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -134,14 +134,14 @@ func (_m *Dispatcher) Name() string { return r0 } -// Dispatcher_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// Dispatcher_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type Dispatcher_Name_Call struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *Dispatcher_Expecter) Name() *Dispatcher_Name_Call { - return &Dispatcher_Name_Call{Call: _e.mock.On("String")} + return &Dispatcher_Name_Call{Call: _e.mock.On("Name")} } func (_c *Dispatcher_Name_Call) Run(run func()) *Dispatcher_Name_Call { diff --git a/core/capabilities/targets/mocks/contract_writer.go b/core/capabilities/targets/mocks/contract_writer.go index aed43499eb8..c6128e68fc7 100644 --- a/core/capabilities/targets/mocks/contract_writer.go +++ b/core/capabilities/targets/mocks/contract_writer.go @@ -236,7 +236,7 @@ func (_m *ContractWriter) Name() string { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -249,14 +249,14 @@ func (_m *ContractWriter) Name() string { return r0 } -// ContractWriter_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// ContractWriter_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type ContractWriter_Name_Call struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *ContractWriter_Expecter) Name() *ContractWriter_Name_Call { - return &ContractWriter_Name_Call{Call: _e.mock.On("String")} + return &ContractWriter_Name_Call{Call: _e.mock.On("Name")} } func (_c *ContractWriter_Name_Call) Run(run func()) *ContractWriter_Name_Call { diff --git a/core/capabilities/webapi/target/target.go b/core/capabilities/webapi/target/target.go index 4576f95a54e..b211e0fe837 100644 --- a/core/capabilities/webapi/target/target.go +++ b/core/capabilities/webapi/target/target.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "strings" + "time" "github.com/smartcontractkit/chainlink-common/pkg/capabilities" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -57,6 +58,12 @@ func (c *Capability) Start(ctx context.Context) error { } func (c *Capability) Close() error { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + err := c.registry.Remove(ctx, c.capabilityInfo.ID) + if err != nil { + return err + } return nil } diff --git a/core/capabilities/webapi/trigger/trigger.go b/core/capabilities/webapi/trigger/trigger.go index c607f0dbb6f..712cf38a4cc 100644 --- a/core/capabilities/webapi/trigger/trigger.go +++ b/core/capabilities/webapi/trigger/trigger.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "sync" + "time" ethCommon "github.com/ethereum/go-ethereum/common" @@ -256,6 +257,12 @@ func (h *triggerConnectorHandler) Start(ctx context.Context) error { } func (h *triggerConnectorHandler) Close() error { return h.StopOnce("GatewayConnectorServiceWrapper", func() error { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + err := h.registry.Remove(ctx, h.ID) + if err != nil { + return err + } return nil }) } diff --git a/core/chains/evm/gas/mocks/evm_estimator.go b/core/chains/evm/gas/mocks/evm_estimator.go index 872c651afb3..159adb1a037 100644 --- a/core/chains/evm/gas/mocks/evm_estimator.go +++ b/core/chains/evm/gas/mocks/evm_estimator.go @@ -443,7 +443,7 @@ func (_m *EvmEstimator) Name() string { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -456,14 +456,14 @@ func (_m *EvmEstimator) Name() string { return r0 } -// EvmEstimator_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// EvmEstimator_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type EvmEstimator_Name_Call struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *EvmEstimator_Expecter) Name() *EvmEstimator_Name_Call { - return &EvmEstimator_Name_Call{Call: _e.mock.On("String")} + return &EvmEstimator_Name_Call{Call: _e.mock.On("Name")} } func (_c *EvmEstimator_Name_Call) Run(run func()) *EvmEstimator_Name_Call { diff --git a/core/chains/evm/gas/mocks/evm_fee_estimator.go b/core/chains/evm/gas/mocks/evm_fee_estimator.go index fd9125a671e..d0802f81ebd 100644 --- a/core/chains/evm/gas/mocks/evm_fee_estimator.go +++ b/core/chains/evm/gas/mocks/evm_fee_estimator.go @@ -408,7 +408,7 @@ func (_m *EvmFeeEstimator) Name() string { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -421,14 +421,14 @@ func (_m *EvmFeeEstimator) Name() string { return r0 } -// EvmFeeEstimator_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// EvmFeeEstimator_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type EvmFeeEstimator_Name_Call struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *EvmFeeEstimator_Expecter) Name() *EvmFeeEstimator_Name_Call { - return &EvmFeeEstimator_Name_Call{Call: _e.mock.On("String")} + return &EvmFeeEstimator_Name_Call{Call: _e.mock.On("Name")} } func (_c *EvmFeeEstimator_Name_Call) Run(run func()) *EvmFeeEstimator_Name_Call { diff --git a/core/chains/evm/gas/rollups/mocks/l1_oracle.go b/core/chains/evm/gas/rollups/mocks/l1_oracle.go index 6aa2d332e6f..4ed067663e2 100644 --- a/core/chains/evm/gas/rollups/mocks/l1_oracle.go +++ b/core/chains/evm/gas/rollups/mocks/l1_oracle.go @@ -178,7 +178,7 @@ func (_m *L1Oracle) Name() string { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -191,14 +191,14 @@ func (_m *L1Oracle) Name() string { return r0 } -// L1Oracle_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// L1Oracle_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type L1Oracle_Name_Call struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *L1Oracle_Expecter) Name() *L1Oracle_Name_Call { - return &L1Oracle_Name_Call{Call: _e.mock.On("String")} + return &L1Oracle_Name_Call{Call: _e.mock.On("Name")} } func (_c *L1Oracle_Name_Call) Run(run func()) *L1Oracle_Name_Call { diff --git a/core/chains/evm/log/mocks/broadcaster.go b/core/chains/evm/log/mocks/broadcaster.go index fe86ec2acd1..1545624a83d 100644 --- a/core/chains/evm/log/mocks/broadcaster.go +++ b/core/chains/evm/log/mocks/broadcaster.go @@ -328,7 +328,7 @@ func (_m *Broadcaster) Name() string { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -341,14 +341,14 @@ func (_m *Broadcaster) Name() string { return r0 } -// Broadcaster_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// Broadcaster_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type Broadcaster_Name_Call struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *Broadcaster_Expecter) Name() *Broadcaster_Name_Call { - return &Broadcaster_Name_Call{Call: _e.mock.On("String")} + return &Broadcaster_Name_Call{Call: _e.mock.On("Name")} } func (_c *Broadcaster_Name_Call) Run(run func()) *Broadcaster_Name_Call { diff --git a/core/chains/evm/logpoller/mocks/log_poller.go b/core/chains/evm/logpoller/mocks/log_poller.go index 71ae46333ac..592157efb1c 100644 --- a/core/chains/evm/logpoller/mocks/log_poller.go +++ b/core/chains/evm/logpoller/mocks/log_poller.go @@ -1551,7 +1551,7 @@ func (_m *LogPoller) Name() string { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -1564,14 +1564,14 @@ func (_m *LogPoller) Name() string { return r0 } -// LogPoller_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// LogPoller_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type LogPoller_Name_Call struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *LogPoller_Expecter) Name() *LogPoller_Name_Call { - return &LogPoller_Name_Call{Call: _e.mock.On("String")} + return &LogPoller_Name_Call{Call: _e.mock.On("Name")} } func (_c *LogPoller_Name_Call) Run(run func()) *LogPoller_Name_Call { diff --git a/core/chains/evm/mocks/balance_monitor.go b/core/chains/evm/mocks/balance_monitor.go index e5842b375a4..95382541179 100644 --- a/core/chains/evm/mocks/balance_monitor.go +++ b/core/chains/evm/mocks/balance_monitor.go @@ -171,7 +171,7 @@ func (_m *BalanceMonitor) Name() string { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -184,14 +184,14 @@ func (_m *BalanceMonitor) Name() string { return r0 } -// BalanceMonitor_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// BalanceMonitor_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type BalanceMonitor_Name_Call struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *BalanceMonitor_Expecter) Name() *BalanceMonitor_Name_Call { - return &BalanceMonitor_Name_Call{Call: _e.mock.On("String")} + return &BalanceMonitor_Name_Call{Call: _e.mock.On("Name")} } func (_c *BalanceMonitor_Name_Call) Run(run func()) *BalanceMonitor_Name_Call { diff --git a/core/chains/legacyevm/mocks/chain.go b/core/chains/legacyevm/mocks/chain.go index 648fd9c49dd..14a89027323 100644 --- a/core/chains/legacyevm/mocks/chain.go +++ b/core/chains/legacyevm/mocks/chain.go @@ -795,7 +795,7 @@ func (_m *Chain) Name() string { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -808,14 +808,14 @@ func (_m *Chain) Name() string { return r0 } -// Chain_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// Chain_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type Chain_Name_Call struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *Chain_Expecter) Name() *Chain_Name_Call { - return &Chain_Name_Call{Call: _e.mock.On("String")} + return &Chain_Name_Call{Call: _e.mock.On("Name")} } func (_c *Chain_Name_Call) Run(run func()) *Chain_Name_Call { diff --git a/core/gethwrappers/ccip/mocks/link_token_interface.go b/core/gethwrappers/ccip/mocks/link_token_interface.go index 26716489752..59e9d7c8543 100644 --- a/core/gethwrappers/ccip/mocks/link_token_interface.go +++ b/core/gethwrappers/ccip/mocks/link_token_interface.go @@ -559,7 +559,7 @@ func (_m *LinkTokenInterface) Name(opts *bind.CallOpts) (string, error) { ret := _m.Called(opts) if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -582,7 +582,7 @@ func (_m *LinkTokenInterface) Name(opts *bind.CallOpts) (string, error) { return r0, r1 } -// LinkTokenInterface_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// LinkTokenInterface_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type LinkTokenInterface_Name_Call struct { *mock.Call } @@ -590,7 +590,7 @@ type LinkTokenInterface_Name_Call struct { // Name is a helper method to define mock.On call // - opts *bind.CallOpts func (_e *LinkTokenInterface_Expecter) Name(opts interface{}) *LinkTokenInterface_Name_Call { - return &LinkTokenInterface_Name_Call{Call: _e.mock.On("String", opts)} + return &LinkTokenInterface_Name_Call{Call: _e.mock.On("Name", opts)} } func (_c *LinkTokenInterface_Name_Call) Run(run func(opts *bind.CallOpts)) *LinkTokenInterface_Name_Call { diff --git a/core/logger/logger_mocks.go b/core/logger/logger_mocks.go index 545870f287c..643f5ff141f 100644 --- a/core/logger/logger_mocks.go +++ b/core/logger/logger_mocks.go @@ -723,7 +723,7 @@ func (_m *MockLogger) Name() string { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -736,14 +736,14 @@ func (_m *MockLogger) Name() string { return r0 } -// MockLogger_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// MockLogger_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type MockLogger_Name_Call struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *MockLogger_Expecter) Name() *MockLogger_Name_Call { - return &MockLogger_Name_Call{Call: _e.mock.On("String")} + return &MockLogger_Name_Call{Call: _e.mock.On("Name")} } func (_c *MockLogger_Name_Call) Run(run func()) *MockLogger_Name_Call { diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 94eea2b4b3a..65d63f2a1df 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -2,6 +2,8 @@ module github.com/smartcontractkit/chainlink/core/scripts go 1.23.3 +toolchain go1.23.4 + // Make sure we're working with the latest chainlink libs replace github.com/smartcontractkit/chainlink/v2 => ../../ @@ -24,7 +26,7 @@ require ( github.com/prometheus/client_golang v1.20.5 github.com/shopspring/decimal v1.4.0 github.com/smartcontractkit/chainlink-automation v0.8.1 - github.com/smartcontractkit/chainlink-common v0.3.1-0.20241127162636-07aa781ee1f4 + github.com/smartcontractkit/chainlink-common v0.3.1-0.20241204184525-29871ced7b4d github.com/smartcontractkit/chainlink/deployment v0.0.0-00010101000000-000000000000 github.com/smartcontractkit/chainlink/v2 v2.14.0-mercury-20240807.0.20241106193309-5560cd76211a github.com/smartcontractkit/libocr v0.0.0-20241007185508-adbe57025f12 @@ -304,6 +306,7 @@ require ( github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.2 // indirect github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241204153209-c3a71b0eef99 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8 // indirect + github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.13 // indirect github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20241009055228-33d0c0bf38de // indirect github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20241009055228-33d0c0bf38de // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index e312f248ceb..1f1374bc11d 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1142,8 +1142,8 @@ github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgB github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= github.com/smartcontractkit/chainlink-ccip v0.0.0-20241204015713-8956bb614e9e h1:GnM6ZWV6vlk2+n6c6o+v/R1LtXzBGVVx7r37nt/h6Uc= github.com/smartcontractkit/chainlink-ccip v0.0.0-20241204015713-8956bb614e9e/go.mod h1:80vGBbOfertJig0xFKsRfm+i17FkjdKkk1dAaGE45Os= -github.com/smartcontractkit/chainlink-common v0.3.1-0.20241127162636-07aa781ee1f4 h1:atCZ1jol7a+tdtgU/wNqXgliBun5H7BjGBicGL8Tj6o= -github.com/smartcontractkit/chainlink-common v0.3.1-0.20241127162636-07aa781ee1f4/go.mod h1:bQktEJf7sJ0U3SmIcXvbGUox7SmXcnSEZ4kUbT8R5Nk= +github.com/smartcontractkit/chainlink-common v0.3.1-0.20241204184525-29871ced7b4d h1:5XKarlliHXVVAhpCeOAx/TRU7eWsJ3tkqRI3I6Cc0SU= +github.com/smartcontractkit/chainlink-common v0.3.1-0.20241204184525-29871ced7b4d/go.mod h1:bQktEJf7sJ0U3SmIcXvbGUox7SmXcnSEZ4kUbT8R5Nk= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e h1:PRoeby6ZlTuTkv2f+7tVU4+zboTfRzI+beECynF4JQ0= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241202195413-82468150ac1e/go.mod h1:mUh5/woemsVaHgTorA080hrYmO3syBCmPdnWc/5dOqk= github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241202141438-a90db35252db h1:N1RH1hSr2ACzOFc9hkCcjE8pRBTdcU3p8nsTJByaLes= @@ -1158,6 +1158,8 @@ github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241204153209-c3a71b0eef9 github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241204153209-c3a71b0eef99/go.mod h1:p8aUDfJeley6oer7y+Ucd3edOtRlMTnWg3mN6rhaLWo= github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8 h1:tNS7U9lrxkFvEuyxQv11HHOiV9LPDGC9wYEy+yM/Jv4= github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241202202529-2033490e77b8/go.mod h1:EBrEgcdIbwepqguClkv8Ohy7CbyWSJaE4EC9aBJlQK0= +github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.13 h1:T0kbw07Vb6xUyA9MIJZfErMgWseWi1zf7cYvRpoq7ug= +github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.13/go.mod h1:1CKUOzoK+Ga19WuhRH9pxZ+qUUnrlIx108VEA6qSzeQ= github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 h1:12ijqMM9tvYVEm+nR826WsrNi6zCKpwBhuApq127wHs= github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7/go.mod h1:FX7/bVdoep147QQhsOPkYsPEXhGZjeYx6lBSaSXtZOA= github.com/smartcontractkit/libocr v0.0.0-20241007185508-adbe57025f12 h1:NzZGjaqez21I3DU7objl3xExTH4fxYvzTqar8DC6360= diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 863c5d915e9..29473c4d932 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -300,18 +300,19 @@ func NewApplication(opts ApplicationOpts) (Application, error) { return nil, fmt.Errorf("expected 1 key, got %d", len(keys)) } - fetcher := syncer.NewFetcherService(globalLogger, gatewayConnectorWrapper) + lggr := globalLogger.Named("WorkflowRegistrySyncer") + fetcher := syncer.NewFetcherService(lggr, gatewayConnectorWrapper) - eventHandler := syncer.NewEventHandler(globalLogger, syncer.NewWorkflowRegistryDS(opts.DS, globalLogger), - fetcher.Fetch, workflowstore.NewDBStore(opts.DS, globalLogger, clockwork.NewRealClock()), opts.CapabilitiesRegistry, + eventHandler := syncer.NewEventHandler(lggr, syncer.NewWorkflowRegistryDS(opts.DS, globalLogger), + fetcher.Fetch, workflowstore.NewDBStore(opts.DS, lggr, clockwork.NewRealClock()), opts.CapabilitiesRegistry, custmsg.NewLabeler(), clockwork.NewRealClock(), keys[0]) - loader := syncer.NewWorkflowRegistryContractLoader(globalLogger, cfg.Capabilities().WorkflowRegistry().Address(), func(ctx context.Context, bytes []byte) (syncer.ContractReader, error) { + loader := syncer.NewWorkflowRegistryContractLoader(lggr, cfg.Capabilities().WorkflowRegistry().Address(), func(ctx context.Context, bytes []byte) (syncer.ContractReader, error) { return relayer.NewContractReader(ctx, bytes) }, eventHandler) globalLogger.Debugw("Creating WorkflowRegistrySyncer") - wfSyncer := syncer.NewWorkflowRegistry(globalLogger, func(ctx context.Context, bytes []byte) (syncer.ContractReader, error) { + wfSyncer := syncer.NewWorkflowRegistry(lggr, func(ctx context.Context, bytes []byte) (syncer.ContractReader, error) { return relayer.NewContractReader(ctx, bytes) }, cfg.Capabilities().WorkflowRegistry().Address(), syncer.WorkflowEventPollerConfig{ diff --git a/core/services/gateway/connector/mocks/gateway_connector.go b/core/services/gateway/connector/mocks/gateway_connector.go index a069e1d156e..183fc949cd5 100644 --- a/core/services/gateway/connector/mocks/gateway_connector.go +++ b/core/services/gateway/connector/mocks/gateway_connector.go @@ -321,7 +321,7 @@ func (_m *GatewayConnector) Name() string { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -334,14 +334,14 @@ func (_m *GatewayConnector) Name() string { return r0 } -// GatewayConnector_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// GatewayConnector_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type GatewayConnector_Name_Call struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *GatewayConnector_Expecter) Name() *GatewayConnector_Name_Call { - return &GatewayConnector_Name_Call{Call: _e.mock.On("String")} + return &GatewayConnector_Name_Call{Call: _e.mock.On("Name")} } func (_c *GatewayConnector_Name_Call) Run(run func()) *GatewayConnector_Name_Call { diff --git a/core/services/job/mocks/spawner.go b/core/services/job/mocks/spawner.go index 99a4d97aa04..aa40522f358 100644 --- a/core/services/job/mocks/spawner.go +++ b/core/services/job/mocks/spawner.go @@ -264,7 +264,7 @@ func (_m *Spawner) Name() string { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -277,14 +277,14 @@ func (_m *Spawner) Name() string { return r0 } -// Spawner_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// Spawner_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type Spawner_Name_Call struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *Spawner_Expecter) Name() *Spawner_Name_Call { - return &Spawner_Name_Call{Call: _e.mock.On("String")} + return &Spawner_Name_Call{Call: _e.mock.On("Name")} } func (_c *Spawner_Name_Call) Run(run func()) *Spawner_Name_Call { diff --git a/core/services/ocr3/promwrapper/factory.go b/core/services/ocr3/promwrapper/factory.go new file mode 100644 index 00000000000..0dabd346112 --- /dev/null +++ b/core/services/ocr3/promwrapper/factory.go @@ -0,0 +1,42 @@ +package promwrapper + +import ( + "context" + + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" +) + +var _ ocr3types.ReportingPluginFactory[any] = &ReportingPluginFactory[any]{} + +type ReportingPluginFactory[RI any] struct { + origin ocr3types.ReportingPluginFactory[RI] + chainID string + plugin string +} + +func NewReportingPluginFactory[RI any]( + origin ocr3types.ReportingPluginFactory[RI], + chainID string, + plugin string, +) *ReportingPluginFactory[RI] { + return &ReportingPluginFactory[RI]{ + origin: origin, + chainID: chainID, + plugin: plugin, + } +} + +func (r ReportingPluginFactory[RI]) NewReportingPlugin(ctx context.Context, config ocr3types.ReportingPluginConfig) (ocr3types.ReportingPlugin[RI], ocr3types.ReportingPluginInfo, error) { + plugin, info, err := r.origin.NewReportingPlugin(ctx, config) + if err != nil { + return nil, ocr3types.ReportingPluginInfo{}, err + } + wrapped := newReportingPlugin( + plugin, + r.chainID, + r.plugin, + promOCR3ReportsGenerated, + promOCR3Durations, + ) + return wrapped, info, err +} diff --git a/core/services/ocr3/promwrapper/factory_test.go b/core/services/ocr3/promwrapper/factory_test.go new file mode 100644 index 00000000000..72f35aad172 --- /dev/null +++ b/core/services/ocr3/promwrapper/factory_test.go @@ -0,0 +1,41 @@ +package promwrapper + +import ( + "context" + "errors" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" + + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" +) + +func Test_WrapperFactory(t *testing.T) { + validFactory := NewReportingPluginFactory(fakeFactory[uint]{}, "solana", "plugin") + failingFactory := NewReportingPluginFactory(fakeFactory[uint]{err: errors.New("error")}, "123", "plugin") + + plugin, _, err := validFactory.NewReportingPlugin(tests.Context(t), ocr3types.ReportingPluginConfig{}) + require.NoError(t, err) + + _, err = plugin.Outcome(tests.Context(t), ocr3types.OutcomeContext{}, nil, nil) + require.NoError(t, err) + + require.Equal(t, 1, counterFromHistogramByLabels(t, promOCR3Durations, "solana", "plugin", "outcome", "true")) + require.Equal(t, 0, counterFromHistogramByLabels(t, promOCR3Durations, "solana", "plugin", "outcome", "false")) + + _, _, err = failingFactory.NewReportingPlugin(tests.Context(t), ocr3types.ReportingPluginConfig{}) + require.Error(t, err) +} + +type fakeFactory[RI any] struct { + err error +} + +func (f fakeFactory[RI]) NewReportingPlugin(context.Context, ocr3types.ReportingPluginConfig) (ocr3types.ReportingPlugin[RI], ocr3types.ReportingPluginInfo, error) { + if f.err != nil { + return nil, ocr3types.ReportingPluginInfo{}, f.err + } + return fakePlugin[RI]{}, ocr3types.ReportingPluginInfo{}, nil +} diff --git a/core/services/ocr3/promwrapper/plugin.go b/core/services/ocr3/promwrapper/plugin.go new file mode 100644 index 00000000000..e4e0c3d35d5 --- /dev/null +++ b/core/services/ocr3/promwrapper/plugin.go @@ -0,0 +1,122 @@ +package promwrapper + +import ( + "context" + "strconv" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" +) + +var _ ocr3types.ReportingPlugin[any] = &reportingPlugin[any]{} + +type reportingPlugin[RI any] struct { + ocr3types.ReportingPlugin[RI] + chainID string + plugin string + + // Prometheus components for tracking metrics + reportsGenerated *prometheus.CounterVec + durations *prometheus.HistogramVec +} + +func newReportingPlugin[RI any]( + origin ocr3types.ReportingPlugin[RI], + chainID string, + plugin string, + reportsGenerated *prometheus.CounterVec, + durations *prometheus.HistogramVec, +) *reportingPlugin[RI] { + return &reportingPlugin[RI]{ + ReportingPlugin: origin, + chainID: chainID, + plugin: plugin, + reportsGenerated: reportsGenerated, + durations: durations, + } +} + +func (p *reportingPlugin[RI]) Query(ctx context.Context, outctx ocr3types.OutcomeContext) (ocrtypes.Query, error) { + return withObservedExecution(p, query, func() (ocrtypes.Query, error) { + return p.ReportingPlugin.Query(ctx, outctx) + }) +} + +func (p *reportingPlugin[RI]) Observation(ctx context.Context, outctx ocr3types.OutcomeContext, query ocrtypes.Query) (ocrtypes.Observation, error) { + return withObservedExecution(p, observation, func() (ocrtypes.Observation, error) { + return p.ReportingPlugin.Observation(ctx, outctx, query) + }) +} + +func (p *reportingPlugin[RI]) ValidateObservation(ctx context.Context, outctx ocr3types.OutcomeContext, query ocrtypes.Query, ao ocrtypes.AttributedObservation) error { + _, err := withObservedExecution(p, validateObservation, func() (any, error) { + err := p.ReportingPlugin.ValidateObservation(ctx, outctx, query, ao) + return nil, err + }) + return err +} + +func (p *reportingPlugin[RI]) Outcome(ctx context.Context, outctx ocr3types.OutcomeContext, query ocrtypes.Query, aos []ocrtypes.AttributedObservation) (ocr3types.Outcome, error) { + return withObservedExecution(p, outcome, func() (ocr3types.Outcome, error) { + return p.ReportingPlugin.Outcome(ctx, outctx, query, aos) + }) +} + +func (p *reportingPlugin[RI]) Reports(ctx context.Context, seqNr uint64, outcome ocr3types.Outcome) ([]ocr3types.ReportPlus[RI], error) { + result, err := withObservedExecution(p, reports, func() ([]ocr3types.ReportPlus[RI], error) { + return p.ReportingPlugin.Reports(ctx, seqNr, outcome) + }) + p.trackReports(reports, len(result)) + return result, err +} + +func (p *reportingPlugin[RI]) ShouldAcceptAttestedReport(ctx context.Context, seqNr uint64, reportWithInfo ocr3types.ReportWithInfo[RI]) (bool, error) { + result, err := withObservedExecution(p, shouldAccept, func() (bool, error) { + return p.ReportingPlugin.ShouldAcceptAttestedReport(ctx, seqNr, reportWithInfo) + }) + p.trackReports(shouldAccept, boolToInt(result)) + return result, err +} + +func (p *reportingPlugin[RI]) ShouldTransmitAcceptedReport(ctx context.Context, seqNr uint64, reportWithInfo ocr3types.ReportWithInfo[RI]) (bool, error) { + result, err := withObservedExecution(p, shouldTransmit, func() (bool, error) { + return p.ReportingPlugin.ShouldTransmitAcceptedReport(ctx, seqNr, reportWithInfo) + }) + p.trackReports(shouldTransmit, boolToInt(result)) + return result, err +} + +func (p *reportingPlugin[RI]) trackReports( + function functionType, + count int, +) { + p.reportsGenerated. + WithLabelValues(p.chainID, p.plugin, string(function)). + Add(float64(count)) +} + +func boolToInt(arg bool) int { + if arg { + return 1 + } + return 0 +} + +func withObservedExecution[RI, R any]( + p *reportingPlugin[RI], + function functionType, + exec func() (R, error), +) (R, error) { + start := time.Now() + result, err := exec() + + success := err == nil + + p.durations. + WithLabelValues(p.chainID, p.plugin, string(function), strconv.FormatBool(success)). + Observe(float64(time.Since(start))) + + return result, err +} diff --git a/core/services/ocr3/promwrapper/plugin_test.go b/core/services/ocr3/promwrapper/plugin_test.go new file mode 100644 index 00000000000..35a97d109aa --- /dev/null +++ b/core/services/ocr3/promwrapper/plugin_test.go @@ -0,0 +1,171 @@ +package promwrapper + +import ( + "context" + "errors" + "testing" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/testutil" + io_prometheus_client "github.com/prometheus/client_model/go" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" +) + +func Test_ReportsGeneratedGauge(t *testing.T) { + plugin1 := newReportingPlugin( + fakePlugin[uint]{reports: make([]ocr3types.ReportPlus[uint], 2)}, + "123", "empty", promOCR3ReportsGenerated, promOCR3Durations, + ) + plugin2 := newReportingPlugin( + fakePlugin[bool]{reports: make([]ocr3types.ReportPlus[bool], 10)}, + "solana", "different_plugin", promOCR3ReportsGenerated, promOCR3Durations, + ) + plugin3 := newReportingPlugin( + fakePlugin[string]{err: errors.New("error")}, + "1234", "empty", promOCR3ReportsGenerated, promOCR3Durations, + ) + + r1, err := plugin1.Reports(tests.Context(t), 1, nil) + require.NoError(t, err) + require.Len(t, r1, 2) + + for i := 0; i < 10; i++ { + r2, err1 := plugin2.Reports(tests.Context(t), 1, nil) + require.NoError(t, err1) + require.Len(t, r2, 10) + } + + _, err = plugin2.ShouldAcceptAttestedReport(tests.Context(t), 1, ocr3types.ReportWithInfo[bool]{}) + require.NoError(t, err) + + _, err = plugin3.Reports(tests.Context(t), 1, nil) + require.Error(t, err) + + g1 := testutil.ToFloat64(promOCR3ReportsGenerated.WithLabelValues("123", "empty", "reports")) + require.Equal(t, 2, int(g1)) + + g2 := testutil.ToFloat64(promOCR3ReportsGenerated.WithLabelValues("solana", "different_plugin", "reports")) + require.Equal(t, 100, int(g2)) + + g3 := testutil.ToFloat64(promOCR3ReportsGenerated.WithLabelValues("solana", "different_plugin", "shouldAccept")) + require.Equal(t, 1, int(g3)) + + g4 := testutil.ToFloat64(promOCR3ReportsGenerated.WithLabelValues("1234", "empty", "reports")) + require.Equal(t, 0, int(g4)) +} + +func Test_DurationHistograms(t *testing.T) { + plugin1 := newReportingPlugin( + fakePlugin[uint]{}, + "123", "empty", promOCR3ReportsGenerated, promOCR3Durations, + ) + plugin2 := newReportingPlugin( + fakePlugin[uint]{err: errors.New("error")}, + "123", "empty", promOCR3ReportsGenerated, promOCR3Durations, + ) + plugin3 := newReportingPlugin( + fakePlugin[uint]{}, + "solana", "commit", promOCR3ReportsGenerated, promOCR3Durations, + ) + + for _, p := range []*reportingPlugin[uint]{plugin1, plugin2, plugin3} { + _, _ = p.Query(tests.Context(t), ocr3types.OutcomeContext{}) + for i := 0; i < 2; i++ { + _, _ = p.Observation(tests.Context(t), ocr3types.OutcomeContext{}, nil) + } + _ = p.ValidateObservation(tests.Context(t), ocr3types.OutcomeContext{}, nil, ocrtypes.AttributedObservation{}) + _, _ = p.Outcome(tests.Context(t), ocr3types.OutcomeContext{}, nil, nil) + _, _ = p.Reports(tests.Context(t), 0, nil) + _, _ = p.ShouldAcceptAttestedReport(tests.Context(t), 0, ocr3types.ReportWithInfo[uint]{}) + _, _ = p.ShouldTransmitAcceptedReport(tests.Context(t), 0, ocr3types.ReportWithInfo[uint]{}) + } + + require.Equal(t, 1, counterFromHistogramByLabels(t, promOCR3Durations, "123", "empty", "query", "true")) + require.Equal(t, 1, counterFromHistogramByLabels(t, promOCR3Durations, "123", "empty", "query", "false")) + require.Equal(t, 1, counterFromHistogramByLabels(t, promOCR3Durations, "solana", "commit", "query", "true")) + + require.Equal(t, 2, counterFromHistogramByLabels(t, promOCR3Durations, "123", "empty", "observation", "true")) + require.Equal(t, 2, counterFromHistogramByLabels(t, promOCR3Durations, "123", "empty", "observation", "false")) + require.Equal(t, 2, counterFromHistogramByLabels(t, promOCR3Durations, "solana", "commit", "observation", "true")) +} + +type fakePlugin[RI any] struct { + reports []ocr3types.ReportPlus[RI] + err error +} + +func (f fakePlugin[RI]) Query(context.Context, ocr3types.OutcomeContext) (ocrtypes.Query, error) { + if f.err != nil { + return nil, f.err + } + return ocrtypes.Query{}, nil +} + +func (f fakePlugin[RI]) Observation(context.Context, ocr3types.OutcomeContext, ocrtypes.Query) (ocrtypes.Observation, error) { + if f.err != nil { + return nil, f.err + } + return ocrtypes.Observation{}, nil +} + +func (f fakePlugin[RI]) ValidateObservation(context.Context, ocr3types.OutcomeContext, ocrtypes.Query, ocrtypes.AttributedObservation) error { + return f.err +} + +func (f fakePlugin[RI]) ObservationQuorum(context.Context, ocr3types.OutcomeContext, ocrtypes.Query, []ocrtypes.AttributedObservation) (quorumReached bool, err error) { + return false, f.err +} + +func (f fakePlugin[RI]) Outcome(context.Context, ocr3types.OutcomeContext, ocrtypes.Query, []ocrtypes.AttributedObservation) (ocr3types.Outcome, error) { + if f.err != nil { + return nil, f.err + } + return ocr3types.Outcome{}, nil +} + +func (f fakePlugin[RI]) Reports(context.Context, uint64, ocr3types.Outcome) ([]ocr3types.ReportPlus[RI], error) { + if f.err != nil { + return nil, f.err + } + return f.reports, nil +} + +func (f fakePlugin[RI]) ShouldAcceptAttestedReport(context.Context, uint64, ocr3types.ReportWithInfo[RI]) (bool, error) { + if f.err != nil { + return false, f.err + } + return true, nil +} + +func (f fakePlugin[RI]) ShouldTransmitAcceptedReport(context.Context, uint64, ocr3types.ReportWithInfo[RI]) (bool, error) { + if f.err != nil { + return false, f.err + } + return true, nil +} + +func (f fakePlugin[RI]) Close() error { + return f.err +} + +func counterFromHistogramByLabels(t *testing.T, histogramVec *prometheus.HistogramVec, labels ...string) int { + observer, err := histogramVec.GetMetricWithLabelValues(labels...) + require.NoError(t, err) + + metricCh := make(chan prometheus.Metric, 1) + observer.(prometheus.Histogram).Collect(metricCh) + close(metricCh) + + metric := <-metricCh + pb := &io_prometheus_client.Metric{} + err = metric.Write(pb) + require.NoError(t, err) + + //nolint:gosec // we don't care about that in tests + return int(pb.GetHistogram().GetSampleCount()) +} diff --git a/core/services/ocr3/promwrapper/types.go b/core/services/ocr3/promwrapper/types.go new file mode 100644 index 00000000000..bf6a1b2a39c --- /dev/null +++ b/core/services/ocr3/promwrapper/types.go @@ -0,0 +1,51 @@ +package promwrapper + +import ( + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" +) + +type functionType string + +const ( + query functionType = "query" + observation functionType = "observation" + validateObservation functionType = "validateObservation" + outcome functionType = "outcome" + reports functionType = "reports" + shouldAccept functionType = "shouldAccept" + shouldTransmit functionType = "shouldTransmit" +) + +var ( + buckets = []float64{ + float64(10 * time.Millisecond), + float64(50 * time.Millisecond), + float64(100 * time.Millisecond), + float64(200 * time.Millisecond), + float64(500 * time.Millisecond), + float64(700 * time.Millisecond), + float64(time.Second), + float64(2 * time.Second), + float64(5 * time.Second), + float64(10 * time.Second), + } + + promOCR3ReportsGenerated = promauto.NewCounterVec( + prometheus.CounterOpts{ + Name: "ocr3_reporting_plugin_reports_processed", + Help: "Tracks number of reports processed/generated within by different OCR3 functions", + }, + []string{"chainID", "plugin", "function"}, + ) + promOCR3Durations = promauto.NewHistogramVec( + prometheus.HistogramOpts{ + Name: "ocr3_reporting_plugin_duration", + Help: "The amount of time elapsed during the OCR3 plugin's function", + Buckets: buckets, + }, + []string{"chainID", "plugin", "function", "success"}, + ) +) diff --git a/core/services/p2p/types/mocks/peer.go b/core/services/p2p/types/mocks/peer.go index f81304e5d17..a50bad780b8 100644 --- a/core/services/p2p/types/mocks/peer.go +++ b/core/services/p2p/types/mocks/peer.go @@ -166,7 +166,7 @@ func (_m *Peer) Name() string { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -179,14 +179,14 @@ func (_m *Peer) Name() string { return r0 } -// Peer_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// Peer_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type Peer_Name_Call struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *Peer_Expecter) Name() *Peer_Name_Call { - return &Peer_Name_Call{Call: _e.mock.On("String")} + return &Peer_Name_Call{Call: _e.mock.On("Name")} } func (_c *Peer_Name_Call) Run(run func()) *Peer_Name_Call { diff --git a/core/services/p2p/types/mocks/peer_wrapper.go b/core/services/p2p/types/mocks/peer_wrapper.go index ce3c193f7a8..7d1744cb0c5 100644 --- a/core/services/p2p/types/mocks/peer_wrapper.go +++ b/core/services/p2p/types/mocks/peer_wrapper.go @@ -166,7 +166,7 @@ func (_m *PeerWrapper) Name() string { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -179,14 +179,14 @@ func (_m *PeerWrapper) Name() string { return r0 } -// PeerWrapper_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// PeerWrapper_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type PeerWrapper_Name_Call struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *PeerWrapper_Expecter) Name() *PeerWrapper_Name_Call { - return &PeerWrapper_Name_Call{Call: _e.mock.On("String")} + return &PeerWrapper_Name_Call{Call: _e.mock.On("Name")} } func (_c *PeerWrapper_Name_Call) Run(run func()) *PeerWrapper_Name_Call { diff --git a/core/services/pipeline/mocks/orm.go b/core/services/pipeline/mocks/orm.go index 33da0b3ffcc..d79825fcf10 100644 --- a/core/services/pipeline/mocks/orm.go +++ b/core/services/pipeline/mocks/orm.go @@ -727,7 +727,7 @@ func (_m *ORM) Name() string { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -740,14 +740,14 @@ func (_m *ORM) Name() string { return r0 } -// ORM_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// ORM_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type ORM_Name_Call struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *ORM_Expecter) Name() *ORM_Name_Call { - return &ORM_Name_Call{Call: _e.mock.On("String")} + return &ORM_Name_Call{Call: _e.mock.On("Name")} } func (_c *ORM_Name_Call) Run(run func()) *ORM_Name_Call { diff --git a/core/services/pipeline/mocks/runner.go b/core/services/pipeline/mocks/runner.go index 6d9ad8b5959..7a59569989a 100644 --- a/core/services/pipeline/mocks/runner.go +++ b/core/services/pipeline/mocks/runner.go @@ -416,7 +416,7 @@ func (_m *Runner) Name() string { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -429,14 +429,14 @@ func (_m *Runner) Name() string { return r0 } -// Runner_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// Runner_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type Runner_Name_Call struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *Runner_Expecter) Name() *Runner_Name_Call { - return &Runner_Name_Call{Call: _e.mock.On("String")} + return &Runner_Name_Call{Call: _e.mock.On("Name")} } func (_c *Runner_Name_Call) Run(run func()) *Runner_Name_Call { diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index 847b5bb72d9..e60dbe1bfdb 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -12,6 +12,7 @@ import ( "net/http" "strings" "sync" + "time" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" @@ -259,6 +260,14 @@ func (r *Relayer) Close() error { cs := make([]io.Closer, 0, 2) if r.triggerCapability != nil { cs = append(cs, r.triggerCapability) + + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + err := r.capabilitiesRegistry.Remove(ctx, r.triggerCapability.ID) + if err != nil { + return err + } } cs = append(cs, r.chain) return services.MultiCloser(cs).Close() diff --git a/core/services/relay/evm/types/mocks/log_poller_wrapper.go b/core/services/relay/evm/types/mocks/log_poller_wrapper.go index cec03207888..ee4946b0e4d 100644 --- a/core/services/relay/evm/types/mocks/log_poller_wrapper.go +++ b/core/services/relay/evm/types/mocks/log_poller_wrapper.go @@ -186,7 +186,7 @@ func (_m *LogPollerWrapper) Name() string { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -199,14 +199,14 @@ func (_m *LogPollerWrapper) Name() string { return r0 } -// LogPollerWrapper_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// LogPollerWrapper_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type LogPollerWrapper_Name_Call struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *LogPollerWrapper_Expecter) Name() *LogPollerWrapper_Name_Call { - return &LogPollerWrapper_Name_Call{Call: _e.mock.On("String")} + return &LogPollerWrapper_Name_Call{Call: _e.mock.On("Name")} } func (_c *LogPollerWrapper_Name_Call) Run(run func()) *LogPollerWrapper_Name_Call { diff --git a/core/services/synchronization/mocks/telemetry_service.go b/core/services/synchronization/mocks/telemetry_service.go index 2e9d4989a97..c6450abd7ea 100644 --- a/core/services/synchronization/mocks/telemetry_service.go +++ b/core/services/synchronization/mocks/telemetry_service.go @@ -119,7 +119,7 @@ func (_m *TelemetryService) Name() string { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for String") + panic("no return value specified for Name") } var r0 string @@ -132,14 +132,14 @@ func (_m *TelemetryService) Name() string { return r0 } -// TelemetryService_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +// TelemetryService_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' type TelemetryService_Name_Call struct { *mock.Call } // Name is a helper method to define mock.On call func (_e *TelemetryService_Expecter) Name() *TelemetryService_Name_Call { - return &TelemetryService_Name_Call{Call: _e.mock.On("String")} + return &TelemetryService_Name_Call{Call: _e.mock.On("Name")} } func (_c *TelemetryService_Name_Call) Run(run func()) *TelemetryService_Name_Call { diff --git a/core/services/workflows/syncer/fetcher.go b/core/services/workflows/syncer/fetcher.go index 357f7518635..fdd0134909d 100644 --- a/core/services/workflows/syncer/fetcher.go +++ b/core/services/workflows/syncer/fetcher.go @@ -44,7 +44,7 @@ func (s *FetcherService) Start(ctx context.Context) error { return s.StartOnce("FetcherService", func() error { connector := s.wrapper.GetGatewayConnector() - outgoingConnectorLggr := s.lggr.Named("WorkflowSyncer") + outgoingConnectorLggr := s.lggr.Named("OutgoingConnectorHandler") webAPIConfig := webapi.ServiceConfig{ RateLimiter: common.RateLimiterConfig{ diff --git a/core/services/workflows/syncer/handler.go b/core/services/workflows/syncer/handler.go index 5cfce71d56c..46dcd21ed90 100644 --- a/core/services/workflows/syncer/handler.go +++ b/core/services/workflows/syncer/handler.go @@ -1,8 +1,8 @@ package syncer import ( + "bytes" "context" - "crypto/sha256" "encoding/hex" "encoding/json" "errors" @@ -14,6 +14,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/custmsg" "github.com/smartcontractkit/chainlink-common/pkg/types/core" + pkgworkflows "github.com/smartcontractkit/chainlink-common/pkg/workflows" "github.com/smartcontractkit/chainlink-common/pkg/workflows/secrets" "github.com/smartcontractkit/chainlink-common/pkg/workflows/wasm/host" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -263,6 +264,7 @@ func (h *eventHandler) Handle(ctx context.Context, event Event) error { return err } + h.lggr.Debugw("handled force update secrets events for URL hash", "urlHash", payload.SecretsURLHash) return nil case WorkflowRegisteredEvent: payload, ok := event.GetData().(WorkflowRegistryWorkflowRegisteredV1) @@ -282,7 +284,7 @@ func (h *eventHandler) Handle(ctx context.Context, event Event) error { return err } - h.lggr.Debugf("workflow 0x%x registered and started", wfID) + h.lggr.Debugw("handled workflow registration event", "workflowID", wfID) return nil case WorkflowUpdatedEvent: payload, ok := event.GetData().(WorkflowRegistryWorkflowUpdatedV1) @@ -302,6 +304,7 @@ func (h *eventHandler) Handle(ctx context.Context, event Event) error { return err } + h.lggr.Debugw("handled workflow updated event", "workflowID", newWorkflowID) return nil case WorkflowPausedEvent: payload, ok := event.GetData().(WorkflowRegistryWorkflowPausedV1) @@ -321,6 +324,7 @@ func (h *eventHandler) Handle(ctx context.Context, event Event) error { logCustMsg(ctx, cma, fmt.Sprintf("failed to handle workflow paused event: %v", err), h.lggr) return err } + h.lggr.Debugw("handled workflow paused event", "workflowID", wfID) return nil case WorkflowActivatedEvent: payload, ok := event.GetData().(WorkflowRegistryWorkflowActivatedV1) @@ -340,6 +344,7 @@ func (h *eventHandler) Handle(ctx context.Context, event Event) error { return err } + h.lggr.Debugw("handled workflow activated event", "workflowID", wfID) return nil case WorkflowDeletedEvent: payload, ok := event.GetData().(WorkflowRegistryWorkflowDeletedV1) @@ -360,6 +365,7 @@ func (h *eventHandler) Handle(ctx context.Context, event Event) error { return err } + h.lggr.Debugw("handled workflow deleted event", "workflowID", wfID) return nil default: return fmt.Errorf("event type unsupported: %v", event.GetEventType()) @@ -371,8 +377,6 @@ func (h *eventHandler) workflowRegisteredEvent( ctx context.Context, payload WorkflowRegistryWorkflowRegisteredV1, ) error { - wfID := hex.EncodeToString(payload.WorkflowID[:]) - // Download the contents of binaryURL, configURL and secretsURL and cache them locally. binary, err := h.fetcher(ctx, payload.BinaryURL) if err != nil { @@ -390,11 +394,14 @@ func (h *eventHandler) workflowRegisteredEvent( } // Calculate the hash of the binary and config files - hash := workflowID(binary, config, []byte(payload.SecretsURL)) + hash, err := pkgworkflows.GenerateWorkflowID(payload.Owner, binary, config, payload.SecretsURL) + if err != nil { + return fmt.Errorf("failed to generate workflow id: %w", err) + } // Pre-check: verify that the workflowID matches; if it doesn’t abort and log an error via Beholder. - if hash != wfID { - return fmt.Errorf("workflowID mismatch: %s != %s", hash, wfID) + if !bytes.Equal(hash[:], payload.WorkflowID[:]) { + return fmt.Errorf("workflowID mismatch: %x != %x", hash, payload.WorkflowID) } // Save the workflow secrets @@ -409,6 +416,7 @@ func (h *eventHandler) workflowRegisteredEvent( status = job.WorkflowSpecStatusPaused } + wfID := hex.EncodeToString(payload.WorkflowID[:]) entry := &job.WorkflowSpec{ Workflow: hex.EncodeToString(binary), Config: string(config), @@ -425,6 +433,7 @@ func (h *eventHandler) workflowRegisteredEvent( } if status != job.WorkflowSpecStatusActive { + h.lggr.Debugw("workflow is marked as paused, so not starting it", "workflow", wfID) return nil } @@ -611,15 +620,6 @@ func (h *eventHandler) tryEngineCleanup(wfID string) error { return nil } -// workflowID returns a hex encoded sha256 hash of the wasm, config and secretsURL. -func workflowID(wasm, config, secretsURL []byte) string { - sum := sha256.New() - sum.Write(wasm) - sum.Write(config) - sum.Write(secretsURL) - return hex.EncodeToString(sum.Sum(nil)) -} - // logCustMsg emits a custom message to the external sink and logs an error if that fails. func logCustMsg(ctx context.Context, cma custmsg.MessageEmitter, msg string, log logger.Logger) { err := cma.Emit(ctx, msg) diff --git a/core/services/workflows/syncer/handler_test.go b/core/services/workflows/syncer/handler_test.go index f5a915e48ab..bb0a61aea4d 100644 --- a/core/services/workflows/syncer/handler_test.go +++ b/core/services/workflows/syncer/handler_test.go @@ -10,6 +10,7 @@ import ( "time" "github.com/smartcontractkit/chainlink-common/pkg/custmsg" + pkgworkflows "github.com/smartcontractkit/chainlink-common/pkg/workflows" "github.com/smartcontractkit/chainlink-common/pkg/workflows/secrets" "github.com/smartcontractkit/chainlink/v2/core/capabilities" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -194,16 +195,12 @@ func Test_workflowRegisteredHandler(t *testing.T) { }) ) - giveWFID := workflowID(binary, config, []byte(secretsURL)) - - b, err := hex.DecodeString(giveWFID) + giveWFID, err := pkgworkflows.GenerateWorkflowID(wfOwner, binary, config, secretsURL) require.NoError(t, err) - wfID := make([]byte, 32) - copy(wfID, b) paused := WorkflowRegistryWorkflowRegisteredV1{ Status: uint8(1), - WorkflowID: [32]byte(wfID), + WorkflowID: giveWFID, Owner: wfOwner, WorkflowName: "workflow-name", BinaryURL: binaryURL, @@ -250,16 +247,14 @@ func Test_workflowRegisteredHandler(t *testing.T) { }) ) - giveWFID := workflowID(binary, config, []byte(secretsURL)) + giveWFID, err := pkgworkflows.GenerateWorkflowID(wfOwner, binary, config, secretsURL) + require.NoError(t, err) - b, err := hex.DecodeString(giveWFID) require.NoError(t, err) - wfID := make([]byte, 32) - copy(wfID, b) active := WorkflowRegistryWorkflowRegisteredV1{ Status: uint8(0), - WorkflowID: [32]byte(wfID), + WorkflowID: giveWFID, Owner: wfOwner, WorkflowName: "workflow-name", BinaryURL: binaryURL, @@ -291,7 +286,7 @@ func Test_workflowRegisteredHandler(t *testing.T) { require.Equal(t, job.WorkflowSpecStatusActive, dbSpec.Status) // Verify the engine is started - engine, err := h.engineRegistry.Get(giveWFID) + engine, err := h.engineRegistry.Get(hex.EncodeToString(giveWFID[:])) require.NoError(t, err) err = engine.Ready() require.NoError(t, err) @@ -321,16 +316,14 @@ func Test_workflowDeletedHandler(t *testing.T) { }) ) - giveWFID := workflowID(binary, config, []byte(secretsURL)) + giveWFID, err := pkgworkflows.GenerateWorkflowID(wfOwner, binary, config, secretsURL) - b, err := hex.DecodeString(giveWFID) require.NoError(t, err) - wfID := make([]byte, 32) - copy(wfID, b) + wfIDs := hex.EncodeToString(giveWFID[:]) active := WorkflowRegistryWorkflowRegisteredV1{ Status: uint8(0), - WorkflowID: [32]byte(wfID), + WorkflowID: giveWFID, Owner: wfOwner, WorkflowName: "workflow-name", BinaryURL: binaryURL, @@ -362,13 +355,13 @@ func Test_workflowDeletedHandler(t *testing.T) { require.Equal(t, job.WorkflowSpecStatusActive, dbSpec.Status) // Verify the engine is started - engine, err := h.engineRegistry.Get(giveWFID) + engine, err := h.engineRegistry.Get(wfIDs) require.NoError(t, err) err = engine.Ready() require.NoError(t, err) deleteEvent := WorkflowRegistryWorkflowDeletedV1{ - WorkflowID: [32]byte(wfID), + WorkflowID: giveWFID, WorkflowOwner: wfOwner, WorkflowName: "workflow-name", DonID: 1, @@ -381,7 +374,7 @@ func Test_workflowDeletedHandler(t *testing.T) { require.Error(t, err) // Verify the engine is deleted - _, err = h.engineRegistry.Get(giveWFID) + _, err = h.engineRegistry.Get(wfIDs) require.Error(t, err) }) } @@ -412,22 +405,20 @@ func Test_workflowPausedActivatedUpdatedHandler(t *testing.T) { }) ) - giveWFID := workflowID(binary, config, []byte(secretsURL)) - updatedWFID := workflowID(binary, updateConfig, []byte(secretsURL)) + giveWFID, err := pkgworkflows.GenerateWorkflowID(wfOwner, binary, config, secretsURL) + require.NoError(t, err) + updatedWFID, err := pkgworkflows.GenerateWorkflowID(wfOwner, binary, updateConfig, secretsURL) + require.NoError(t, err) - b, err := hex.DecodeString(giveWFID) require.NoError(t, err) - wfID := make([]byte, 32) - copy(wfID, b) + wfIDs := hex.EncodeToString(giveWFID[:]) - b, err = hex.DecodeString(updatedWFID) require.NoError(t, err) - newWFID := make([]byte, 32) - copy(newWFID, b) + newWFIDs := hex.EncodeToString(updatedWFID[:]) active := WorkflowRegistryWorkflowRegisteredV1{ Status: uint8(0), - WorkflowID: [32]byte(wfID), + WorkflowID: giveWFID, Owner: wfOwner, WorkflowName: "workflow-name", BinaryURL: binaryURL, @@ -459,14 +450,14 @@ func Test_workflowPausedActivatedUpdatedHandler(t *testing.T) { require.Equal(t, job.WorkflowSpecStatusActive, dbSpec.Status) // Verify the engine is started - engine, err := h.engineRegistry.Get(giveWFID) + engine, err := h.engineRegistry.Get(wfIDs) require.NoError(t, err) err = engine.Ready() require.NoError(t, err) // create a paused event pauseEvent := WorkflowRegistryWorkflowPausedV1{ - WorkflowID: [32]byte(wfID), + WorkflowID: giveWFID, WorkflowOwner: wfOwner, WorkflowName: "workflow-name", DonID: 1, @@ -482,12 +473,12 @@ func Test_workflowPausedActivatedUpdatedHandler(t *testing.T) { require.Equal(t, job.WorkflowSpecStatusPaused, dbSpec.Status) // Verify the engine is removed - _, err = h.engineRegistry.Get(giveWFID) + _, err = h.engineRegistry.Get(wfIDs) require.Error(t, err) // create an activated workflow event activatedEvent := WorkflowRegistryWorkflowActivatedV1{ - WorkflowID: [32]byte(wfID), + WorkflowID: giveWFID, WorkflowOwner: wfOwner, WorkflowName: "workflow-name", DonID: 1, @@ -504,15 +495,15 @@ func Test_workflowPausedActivatedUpdatedHandler(t *testing.T) { require.Equal(t, job.WorkflowSpecStatusActive, dbSpec.Status) // Verify the engine is started - engine, err = h.engineRegistry.Get(giveWFID) + engine, err = h.engineRegistry.Get(wfIDs) require.NoError(t, err) err = engine.Ready() require.NoError(t, err) // create an updated event updatedEvent := WorkflowRegistryWorkflowUpdatedV1{ - OldWorkflowID: [32]byte(wfID), - NewWorkflowID: [32]byte(newWFID), + OldWorkflowID: giveWFID, + NewWorkflowID: updatedWFID, WorkflowOwner: wfOwner, WorkflowName: "workflow-name", BinaryURL: binaryURL, @@ -529,16 +520,16 @@ func Test_workflowPausedActivatedUpdatedHandler(t *testing.T) { require.Equal(t, hex.EncodeToString(wfOwner), dbSpec.WorkflowOwner) require.Equal(t, "workflow-name", dbSpec.WorkflowName) require.Equal(t, job.WorkflowSpecStatusActive, dbSpec.Status) - require.Equal(t, hex.EncodeToString(newWFID), dbSpec.WorkflowID) + require.Equal(t, newWFIDs, dbSpec.WorkflowID) require.Equal(t, newConfigURL, dbSpec.ConfigURL) require.Equal(t, string(updateConfig), dbSpec.Config) // old engine is no longer running - _, err = h.engineRegistry.Get(giveWFID) + _, err = h.engineRegistry.Get(wfIDs) require.Error(t, err) // new engine is started - engine, err = h.engineRegistry.Get(updatedWFID) + engine, err = h.engineRegistry.Get(newWFIDs) require.NoError(t, err) err = engine.Ready() require.NoError(t, err) diff --git a/core/services/workflows/syncer/workflow_registry.go b/core/services/workflows/syncer/workflow_registry.go index 6fc319da76b..024975539af 100644 --- a/core/services/workflows/syncer/workflow_registry.go +++ b/core/services/workflows/syncer/workflow_registry.go @@ -173,7 +173,7 @@ func NewWorkflowRegistry( ) *workflowRegistry { ets := []WorkflowRegistryEventType{ForceUpdateSecretsEvent} wr := &workflowRegistry{ - lggr: lggr.Named(name), + lggr: lggr, newContractReaderFn: newContractReaderFn, workflowRegistryAddress: addr, eventPollerCfg: eventPollerConfig, @@ -633,7 +633,7 @@ func (l *workflowRegistryContractLoader) LoadWorkflows(ctx context.Context, don Data: workflow, EventType: WorkflowRegisteredEvent, }); err != nil { - return nil, fmt.Errorf("failed to handle workflow registration: %w", err) + l.lggr.Errorf("failed to handle workflow registration: %s", err) } }