From 4b21c32e257f534f97829f50aaac4bbe8e9c1ae7 Mon Sep 17 00:00:00 2001 From: Dylan Tinianov Date: Tue, 15 Oct 2024 11:56:30 -0400 Subject: [PATCH 1/2] Chain Agnostic MultiNode + EVM Implementation (#13386) * Update rpc client * Update RPCClient * Update RPCClient * use Eth Subscription for heads * Make RpcClient public * Update node * Update Multinode * Fix build + generate * Udate multinode * update multinode * fix tests * Fix mocks * Update node_lifecycle_test.go * Fix all client tests * Fix tests * Update client_test.go * go mod tidy * fix tests * Fix tests * Clean up * Fix features test mocking * Fix logging * Remove logging * Fix tests * Fix context * lint * Update node_lifecycle_test.go * Remove unused generics * Add state locking * Set block difficulty * Update node_lifecycle.go * Fix tests * Make NodeStates public * Update multi_node_test.go * Update Unsubscribe * Remove HEAD generic from Node interface * Remove unneeded generics * Remove unneeded generics from Multinode * Remove Multinode as interface * Add PoolChainInfoProvider * Setup SendOnly nodes * Test empty context * Add err to log * Add rpc newHeads method * Fix context * Changeset * Remove unused mocks * Create Transaction Sender * Implement TransactionSender * Fix transaction sender types * Implement transaction sender tests * Ensure MultiNode is running * Address comments * Remove ChainClientRPC interface * Remove unneeded test * Generate mocks * Fix tests * fix tests * Use UnsubscribeAllExcept * Fix rpc client tests * Address comments * Remove unused code * Generate private mock * lint * Fix locks and unsubscribing * Update node.go * fixed flaky headtracker tests * Update node_lifecycle_test.go * Update node_lifecycle_test.go * Update transaction sender * Update chain_client_test.go * Remove unused variables * lint * enable secondary url test * Use http if not nil * Update transaction_sender.go * Add close method * Update rpc_client.go * changeset * Add Close to transaction sender * Return struct not interface * Remove flaky test * Fix context * Test if mn closes * Fix context cancel * Fix DoAll * lint generate * Revert * Fix mocks * Update node_lifecycle.go * lint * Delete rpc_client.go * Address comments * Fix tests * Update common/client/send_only_node.go Co-authored-by: Dmytro Haidashenko <34754799+dhaidashenko@users.noreply.github.com> * Address comments * Address comments * Rename SubscribeToHeads * Return code * Update transaction_sender.go * Fix merge conflicts * Handle batch requests * Delete mock_rpc_client_test.go * Update common/client/types.go Co-authored-by: Dmytro Haidashenko <34754799+dhaidashenko@users.noreply.github.com> * Move RPC methods * Use map for subs * Don't cancel context * Update transaction_sender.go * Fix lint * Delete late-mails-battle.md * lint * lint * Check Transaction Sender state * Return Retryable * Update core/chains/evm/client/chain_client_test.go Co-authored-by: Jordan Krage * Update sub_forwarder.go * Update config.go * lint * Don't export nodeState * Update changeset * Use subsSliceMu * lint * Update rpc_client.go * nil ws url for sendonly nodes * Use head polling --------- Co-authored-by: Dmytro Haidashenko Co-authored-by: Dmytro Haidashenko <34754799+dhaidashenko@users.noreply.github.com> Co-authored-by: Jordan Krage --- .changeset/orange-feet-share.md | 25 + .mockery.yaml | 4 +- common/client/mock_node_client_test.go | 718 ----- common/client/mock_node_selector_test.go | 48 +- common/client/mock_node_test.go | 227 +- common/client/mock_rpc_client_test.go | 508 ++++ common/client/mock_rpc_test.go | 1918 ------------- common/client/mock_send_only_client_test.go | 31 +- common/client/mock_send_only_node_test.go | 20 +- common/client/multi_node.go | 680 +---- common/client/multi_node_test.go | 526 +--- common/client/node.go | 32 +- common/client/node_fsm.go | 8 +- common/client/node_fsm_test.go | 26 +- common/client/node_lifecycle.go | 77 +- common/client/node_lifecycle_test.go | 307 +- common/client/node_selector.go | 18 +- common/client/node_selector_highest_head.go | 18 +- .../client/node_selector_highest_head_test.go | 58 +- common/client/node_selector_priority_level.go | 46 +- .../node_selector_priority_level_test.go | 8 +- common/client/node_selector_round_robin.go | 18 +- .../client/node_selector_round_robin_test.go | 14 +- common/client/node_selector_test.go | 2 +- .../client/node_selector_total_difficulty.go | 20 +- .../node_selector_total_difficulty_test.go | 56 +- common/client/node_test.go | 8 +- common/client/send_only_node.go | 6 +- common/client/send_only_node_test.go | 10 +- common/client/transaction_sender.go | 280 ++ common/client/transaction_sender_test.go | 384 +++ common/client/types.go | 152 +- common/headtracker/head_listener.go | 9 +- common/headtracker/types/client.go | 4 +- common/types/subscription.go | 3 +- core/chains/evm/client/chain_client.go | 273 +- core/chains/evm/client/chain_client_test.go | 108 +- core/chains/evm/client/config_builder_test.go | 2 +- core/chains/evm/client/evm_client.go | 26 +- core/chains/evm/client/evm_client_test.go | 3 +- core/chains/evm/client/helpers_test.go | 33 +- core/chains/evm/client/mocks/client.go | 117 +- core/chains/evm/client/mocks/rpc_client.go | 2532 ----------------- core/chains/evm/client/null_client.go | 6 +- core/chains/evm/client/null_client_test.go | 6 +- core/chains/evm/client/rpc_client.go | 386 +-- core/chains/evm/client/rpc_client_test.go | 210 +- .../evm/client/simulated_backend_client.go | 12 +- core/chains/evm/config/toml/config.go | 15 + .../evm/headtracker/head_broadcaster_test.go | 7 +- .../evm/headtracker/head_listener_test.go | 22 +- .../evm/headtracker/head_tracker_test.go | 87 +- core/chains/evm/testutils/client.go | 70 +- core/chains/legacyevm/chain.go | 16 +- core/internal/cltest/cltest.go | 8 +- core/internal/features/features_test.go | 8 +- 56 files changed, 2578 insertions(+), 7638 deletions(-) create mode 100644 .changeset/orange-feet-share.md delete mode 100644 common/client/mock_node_client_test.go create mode 100644 common/client/mock_rpc_client_test.go delete mode 100644 common/client/mock_rpc_test.go create mode 100644 common/client/transaction_sender.go create mode 100644 common/client/transaction_sender_test.go delete mode 100644 core/chains/evm/client/mocks/rpc_client.go diff --git a/.changeset/orange-feet-share.md b/.changeset/orange-feet-share.md new file mode 100644 index 00000000000..9583bce3d64 --- /dev/null +++ b/.changeset/orange-feet-share.md @@ -0,0 +1,25 @@ +--- +"chainlink": minor +--- + +Implemented new chain agnostic MultiNode design along with the corresponding EVM implementation. The chain agnostic components enable Multinode to be integrated with Solana and other non-EVM chains. Previously the Multinode was coupled with EVM specific actions, and was called to execute these actions direclty. With this change, the MultiNode's responsibility has been simplified to focus on RPC selection along with performing health checks. Chain specific actions will instead be executed on the RPC directly after being selected by MultiNode. The Chain Agnostic MultiNode provides improved reliability and metrics for all chain integrations using it. + +These are following main components: +Node: Common component which wraps an RPC with state information, health checks, and an alive loop to handle state changes along with maintaining chain information. +RPCClient: Chain-specific RPC wrapper which implements required interface for MultiNode along with any chain-specific functionality needed. +MultiNode: Perform RPCClient selection and performs health checks on all RPCs. +TransactionSender: Chain agnostic component which broadcasts transactions to all healthy RPCs and aggregates results. A chain-specific error classifier must be implemented. + +MultiNode picks the "best" RPC based on one of the configurable criteria: +- Priority defined in the config. +- Highest latest block. +- Round-robin within the same priority level (or using other configurable selection algorithms) + +Benefits of Chain Agnostic MultiNode: + Reliability: Improved RPC reliability scaleable to all chains + Maintainability: Can apply changes across all chain integrations through the use of common code + Extendability: Can add new health checks, RPC selection and ranking algorithms + Integration Speed: Much faster to integrate MultiNode with new chains + Reduced Generics: Significantly less bulky code! + +#updated #changed #internal diff --git a/.mockery.yaml b/.mockery.yaml index 72b3788eb33..b74fedf2158 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -14,9 +14,8 @@ packages: NodeSelector: sendOnlyClient: SendOnlyNode: - RPC: + RPCClient: Head: - NodeClient: PoolChainInfoProvider: github.com/smartcontractkit/chainlink/v2/common/headtracker: interfaces: @@ -54,7 +53,6 @@ packages: github.com/smartcontractkit/chainlink/v2/core/chains/evm/client: interfaces: Client: - RPCClient: github.com/smartcontractkit/chainlink/v2/core/chains/evm/config: interfaces: GasEstimator: diff --git a/common/client/mock_node_client_test.go b/common/client/mock_node_client_test.go deleted file mode 100644 index 5643dcde90e..00000000000 --- a/common/client/mock_node_client_test.go +++ /dev/null @@ -1,718 +0,0 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. - -package client - -import ( - context "context" - - types "github.com/smartcontractkit/chainlink/v2/common/types" - mock "github.com/stretchr/testify/mock" -) - -// mockNodeClient is an autogenerated mock type for the NodeClient type -type mockNodeClient[CHAIN_ID types.ID, HEAD Head] struct { - mock.Mock -} - -type mockNodeClient_Expecter[CHAIN_ID types.ID, HEAD Head] struct { - mock *mock.Mock -} - -func (_m *mockNodeClient[CHAIN_ID, HEAD]) EXPECT() *mockNodeClient_Expecter[CHAIN_ID, HEAD] { - return &mockNodeClient_Expecter[CHAIN_ID, HEAD]{mock: &_m.Mock} -} - -// ChainID provides a mock function with given fields: ctx -func (_m *mockNodeClient[CHAIN_ID, HEAD]) ChainID(ctx context.Context) (CHAIN_ID, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for ChainID") - } - - var r0 CHAIN_ID - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (CHAIN_ID, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) CHAIN_ID); ok { - r0 = rf(ctx) - } else { - r0 = ret.Get(0).(CHAIN_ID) - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockNodeClient_ChainID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChainID' -type mockNodeClient_ChainID_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// ChainID is a helper method to define mock.On call -// - ctx context.Context -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) ChainID(ctx interface{}) *mockNodeClient_ChainID_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_ChainID_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("ChainID", ctx)} -} - -func (_c *mockNodeClient_ChainID_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockNodeClient_ChainID_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockNodeClient_ChainID_Call[CHAIN_ID, HEAD]) Return(_a0 CHAIN_ID, _a1 error) *mockNodeClient_ChainID_Call[CHAIN_ID, HEAD] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockNodeClient_ChainID_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (CHAIN_ID, error)) *mockNodeClient_ChainID_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// ClientVersion provides a mock function with given fields: _a0 -func (_m *mockNodeClient[CHAIN_ID, HEAD]) ClientVersion(_a0 context.Context) (string, error) { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for ClientVersion") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (string, error)); ok { - return rf(_a0) - } - if rf, ok := ret.Get(0).(func(context.Context) string); ok { - r0 = rf(_a0) - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(_a0) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockNodeClient_ClientVersion_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ClientVersion' -type mockNodeClient_ClientVersion_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// ClientVersion is a helper method to define mock.On call -// - _a0 context.Context -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) ClientVersion(_a0 interface{}) *mockNodeClient_ClientVersion_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_ClientVersion_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("ClientVersion", _a0)} -} - -func (_c *mockNodeClient_ClientVersion_Call[CHAIN_ID, HEAD]) Run(run func(_a0 context.Context)) *mockNodeClient_ClientVersion_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockNodeClient_ClientVersion_Call[CHAIN_ID, HEAD]) Return(_a0 string, _a1 error) *mockNodeClient_ClientVersion_Call[CHAIN_ID, HEAD] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockNodeClient_ClientVersion_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (string, error)) *mockNodeClient_ClientVersion_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// Close provides a mock function with given fields: -func (_m *mockNodeClient[CHAIN_ID, HEAD]) Close() { - _m.Called() -} - -// mockNodeClient_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' -type mockNodeClient_Close_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// Close is a helper method to define mock.On call -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) Close() *mockNodeClient_Close_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_Close_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("Close")} -} - -func (_c *mockNodeClient_Close_Call[CHAIN_ID, HEAD]) Run(run func()) *mockNodeClient_Close_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNodeClient_Close_Call[CHAIN_ID, HEAD]) Return() *mockNodeClient_Close_Call[CHAIN_ID, HEAD] { - _c.Call.Return() - return _c -} - -func (_c *mockNodeClient_Close_Call[CHAIN_ID, HEAD]) RunAndReturn(run func()) *mockNodeClient_Close_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// Dial provides a mock function with given fields: ctx -func (_m *mockNodeClient[CHAIN_ID, HEAD]) Dial(ctx context.Context) error { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for Dial") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context) error); ok { - r0 = rf(ctx) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// mockNodeClient_Dial_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Dial' -type mockNodeClient_Dial_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// Dial is a helper method to define mock.On call -// - ctx context.Context -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) Dial(ctx interface{}) *mockNodeClient_Dial_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_Dial_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("Dial", ctx)} -} - -func (_c *mockNodeClient_Dial_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockNodeClient_Dial_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockNodeClient_Dial_Call[CHAIN_ID, HEAD]) Return(_a0 error) *mockNodeClient_Dial_Call[CHAIN_ID, HEAD] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockNodeClient_Dial_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) error) *mockNodeClient_Dial_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// DialHTTP provides a mock function with given fields: -func (_m *mockNodeClient[CHAIN_ID, HEAD]) DialHTTP() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for DialHTTP") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// mockNodeClient_DialHTTP_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DialHTTP' -type mockNodeClient_DialHTTP_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// DialHTTP is a helper method to define mock.On call -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) DialHTTP() *mockNodeClient_DialHTTP_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_DialHTTP_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("DialHTTP")} -} - -func (_c *mockNodeClient_DialHTTP_Call[CHAIN_ID, HEAD]) Run(run func()) *mockNodeClient_DialHTTP_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNodeClient_DialHTTP_Call[CHAIN_ID, HEAD]) Return(_a0 error) *mockNodeClient_DialHTTP_Call[CHAIN_ID, HEAD] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockNodeClient_DialHTTP_Call[CHAIN_ID, HEAD]) RunAndReturn(run func() error) *mockNodeClient_DialHTTP_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// DisconnectAll provides a mock function with given fields: -func (_m *mockNodeClient[CHAIN_ID, HEAD]) DisconnectAll() { - _m.Called() -} - -// mockNodeClient_DisconnectAll_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisconnectAll' -type mockNodeClient_DisconnectAll_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// DisconnectAll is a helper method to define mock.On call -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) DisconnectAll() *mockNodeClient_DisconnectAll_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_DisconnectAll_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("DisconnectAll")} -} - -func (_c *mockNodeClient_DisconnectAll_Call[CHAIN_ID, HEAD]) Run(run func()) *mockNodeClient_DisconnectAll_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNodeClient_DisconnectAll_Call[CHAIN_ID, HEAD]) Return() *mockNodeClient_DisconnectAll_Call[CHAIN_ID, HEAD] { - _c.Call.Return() - return _c -} - -func (_c *mockNodeClient_DisconnectAll_Call[CHAIN_ID, HEAD]) RunAndReturn(run func()) *mockNodeClient_DisconnectAll_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// GetInterceptedChainInfo provides a mock function with given fields: -func (_m *mockNodeClient[CHAIN_ID, HEAD]) GetInterceptedChainInfo() (ChainInfo, ChainInfo) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetInterceptedChainInfo") - } - - var r0 ChainInfo - var r1 ChainInfo - if rf, ok := ret.Get(0).(func() (ChainInfo, ChainInfo)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() ChainInfo); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(ChainInfo) - } - - if rf, ok := ret.Get(1).(func() ChainInfo); ok { - r1 = rf() - } else { - r1 = ret.Get(1).(ChainInfo) - } - - return r0, r1 -} - -// mockNodeClient_GetInterceptedChainInfo_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetInterceptedChainInfo' -type mockNodeClient_GetInterceptedChainInfo_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// GetInterceptedChainInfo is a helper method to define mock.On call -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) GetInterceptedChainInfo() *mockNodeClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("GetInterceptedChainInfo")} -} - -func (_c *mockNodeClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD]) Run(run func()) *mockNodeClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNodeClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD]) Return(latest ChainInfo, highestUserObservations ChainInfo) *mockNodeClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD] { - _c.Call.Return(latest, highestUserObservations) - return _c -} - -func (_c *mockNodeClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD]) RunAndReturn(run func() (ChainInfo, ChainInfo)) *mockNodeClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// IsSyncing provides a mock function with given fields: ctx -func (_m *mockNodeClient[CHAIN_ID, HEAD]) IsSyncing(ctx context.Context) (bool, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for IsSyncing") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (bool, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) bool); ok { - r0 = rf(ctx) - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockNodeClient_IsSyncing_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsSyncing' -type mockNodeClient_IsSyncing_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// IsSyncing is a helper method to define mock.On call -// - ctx context.Context -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) IsSyncing(ctx interface{}) *mockNodeClient_IsSyncing_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_IsSyncing_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("IsSyncing", ctx)} -} - -func (_c *mockNodeClient_IsSyncing_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockNodeClient_IsSyncing_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockNodeClient_IsSyncing_Call[CHAIN_ID, HEAD]) Return(_a0 bool, _a1 error) *mockNodeClient_IsSyncing_Call[CHAIN_ID, HEAD] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockNodeClient_IsSyncing_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (bool, error)) *mockNodeClient_IsSyncing_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// SetAliveLoopSub provides a mock function with given fields: _a0 -func (_m *mockNodeClient[CHAIN_ID, HEAD]) SetAliveLoopSub(_a0 types.Subscription) { - _m.Called(_a0) -} - -// mockNodeClient_SetAliveLoopSub_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetAliveLoopSub' -type mockNodeClient_SetAliveLoopSub_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// SetAliveLoopSub is a helper method to define mock.On call -// - _a0 types.Subscription -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) SetAliveLoopSub(_a0 interface{}) *mockNodeClient_SetAliveLoopSub_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_SetAliveLoopSub_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("SetAliveLoopSub", _a0)} -} - -func (_c *mockNodeClient_SetAliveLoopSub_Call[CHAIN_ID, HEAD]) Run(run func(_a0 types.Subscription)) *mockNodeClient_SetAliveLoopSub_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(types.Subscription)) - }) - return _c -} - -func (_c *mockNodeClient_SetAliveLoopSub_Call[CHAIN_ID, HEAD]) Return() *mockNodeClient_SetAliveLoopSub_Call[CHAIN_ID, HEAD] { - _c.Call.Return() - return _c -} - -func (_c *mockNodeClient_SetAliveLoopSub_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(types.Subscription)) *mockNodeClient_SetAliveLoopSub_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// SubscribeNewHead provides a mock function with given fields: ctx, channel -func (_m *mockNodeClient[CHAIN_ID, HEAD]) SubscribeNewHead(ctx context.Context, channel chan<- HEAD) (types.Subscription, error) { - ret := _m.Called(ctx, channel) - - if len(ret) == 0 { - panic("no return value specified for SubscribeNewHead") - } - - var r0 types.Subscription - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, chan<- HEAD) (types.Subscription, error)); ok { - return rf(ctx, channel) - } - if rf, ok := ret.Get(0).(func(context.Context, chan<- HEAD) types.Subscription); ok { - r0 = rf(ctx, channel) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(types.Subscription) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, chan<- HEAD) error); ok { - r1 = rf(ctx, channel) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockNodeClient_SubscribeNewHead_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeNewHead' -type mockNodeClient_SubscribeNewHead_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// SubscribeNewHead is a helper method to define mock.On call -// - ctx context.Context -// - channel chan<- HEAD -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) SubscribeNewHead(ctx interface{}, channel interface{}) *mockNodeClient_SubscribeNewHead_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_SubscribeNewHead_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("SubscribeNewHead", ctx, channel)} -} - -func (_c *mockNodeClient_SubscribeNewHead_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context, channel chan<- HEAD)) *mockNodeClient_SubscribeNewHead_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(chan<- HEAD)) - }) - return _c -} - -func (_c *mockNodeClient_SubscribeNewHead_Call[CHAIN_ID, HEAD]) Return(s types.Subscription, err error) *mockNodeClient_SubscribeNewHead_Call[CHAIN_ID, HEAD] { - _c.Call.Return(s, err) - return _c -} - -func (_c *mockNodeClient_SubscribeNewHead_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context, chan<- HEAD) (types.Subscription, error)) *mockNodeClient_SubscribeNewHead_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// SubscribeToFinalizedHeads provides a mock function with given fields: _a0 -func (_m *mockNodeClient[CHAIN_ID, HEAD]) SubscribeToFinalizedHeads(_a0 context.Context) (<-chan HEAD, types.Subscription, error) { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for SubscribeToFinalizedHeads") - } - - var r0 <-chan HEAD - var r1 types.Subscription - var r2 error - if rf, ok := ret.Get(0).(func(context.Context) (<-chan HEAD, types.Subscription, error)); ok { - return rf(_a0) - } - if rf, ok := ret.Get(0).(func(context.Context) <-chan HEAD); ok { - r0 = rf(_a0) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan HEAD) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) types.Subscription); ok { - r1 = rf(_a0) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(types.Subscription) - } - } - - if rf, ok := ret.Get(2).(func(context.Context) error); ok { - r2 = rf(_a0) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// mockNodeClient_SubscribeToFinalizedHeads_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeToFinalizedHeads' -type mockNodeClient_SubscribeToFinalizedHeads_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// SubscribeToFinalizedHeads is a helper method to define mock.On call -// - _a0 context.Context -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) SubscribeToFinalizedHeads(_a0 interface{}) *mockNodeClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("SubscribeToFinalizedHeads", _a0)} -} - -func (_c *mockNodeClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD]) Run(run func(_a0 context.Context)) *mockNodeClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockNodeClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD]) Return(_a0 <-chan HEAD, _a1 types.Subscription, _a2 error) *mockNodeClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD] { - _c.Call.Return(_a0, _a1, _a2) - return _c -} - -func (_c *mockNodeClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (<-chan HEAD, types.Subscription, error)) *mockNodeClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// SubscribeToHeads provides a mock function with given fields: ctx -func (_m *mockNodeClient[CHAIN_ID, HEAD]) SubscribeToHeads(ctx context.Context) (<-chan HEAD, types.Subscription, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for SubscribeToHeads") - } - - var r0 <-chan HEAD - var r1 types.Subscription - var r2 error - if rf, ok := ret.Get(0).(func(context.Context) (<-chan HEAD, types.Subscription, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) <-chan HEAD); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan HEAD) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) types.Subscription); ok { - r1 = rf(ctx) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(types.Subscription) - } - } - - if rf, ok := ret.Get(2).(func(context.Context) error); ok { - r2 = rf(ctx) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// mockNodeClient_SubscribeToHeads_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeToHeads' -type mockNodeClient_SubscribeToHeads_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// SubscribeToHeads is a helper method to define mock.On call -// - ctx context.Context -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) SubscribeToHeads(ctx interface{}) *mockNodeClient_SubscribeToHeads_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_SubscribeToHeads_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("SubscribeToHeads", ctx)} -} - -func (_c *mockNodeClient_SubscribeToHeads_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockNodeClient_SubscribeToHeads_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockNodeClient_SubscribeToHeads_Call[CHAIN_ID, HEAD]) Return(ch <-chan HEAD, sub types.Subscription, err error) *mockNodeClient_SubscribeToHeads_Call[CHAIN_ID, HEAD] { - _c.Call.Return(ch, sub, err) - return _c -} - -func (_c *mockNodeClient_SubscribeToHeads_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (<-chan HEAD, types.Subscription, error)) *mockNodeClient_SubscribeToHeads_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// SubscribersCount provides a mock function with given fields: -func (_m *mockNodeClient[CHAIN_ID, HEAD]) SubscribersCount() int32 { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for SubscribersCount") - } - - var r0 int32 - if rf, ok := ret.Get(0).(func() int32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int32) - } - - return r0 -} - -// mockNodeClient_SubscribersCount_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribersCount' -type mockNodeClient_SubscribersCount_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// SubscribersCount is a helper method to define mock.On call -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) SubscribersCount() *mockNodeClient_SubscribersCount_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_SubscribersCount_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("SubscribersCount")} -} - -func (_c *mockNodeClient_SubscribersCount_Call[CHAIN_ID, HEAD]) Run(run func()) *mockNodeClient_SubscribersCount_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNodeClient_SubscribersCount_Call[CHAIN_ID, HEAD]) Return(_a0 int32) *mockNodeClient_SubscribersCount_Call[CHAIN_ID, HEAD] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockNodeClient_SubscribersCount_Call[CHAIN_ID, HEAD]) RunAndReturn(run func() int32) *mockNodeClient_SubscribersCount_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// UnsubscribeAllExceptAliveLoop provides a mock function with given fields: -func (_m *mockNodeClient[CHAIN_ID, HEAD]) UnsubscribeAllExceptAliveLoop() { - _m.Called() -} - -// mockNodeClient_UnsubscribeAllExceptAliveLoop_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UnsubscribeAllExceptAliveLoop' -type mockNodeClient_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// UnsubscribeAllExceptAliveLoop is a helper method to define mock.On call -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) UnsubscribeAllExceptAliveLoop() *mockNodeClient_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("UnsubscribeAllExceptAliveLoop")} -} - -func (_c *mockNodeClient_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD]) Run(run func()) *mockNodeClient_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNodeClient_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD]) Return() *mockNodeClient_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD] { - _c.Call.Return() - return _c -} - -func (_c *mockNodeClient_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD]) RunAndReturn(run func()) *mockNodeClient_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// newMockNodeClient creates a new instance of mockNodeClient. 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 newMockNodeClient[CHAIN_ID types.ID, HEAD Head](t interface { - mock.TestingT - Cleanup(func()) -}) *mockNodeClient[CHAIN_ID, HEAD] { - mock := &mockNodeClient[CHAIN_ID, HEAD]{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/common/client/mock_node_selector_test.go b/common/client/mock_node_selector_test.go index d05c2979fc1..2860f8076fd 100644 --- a/common/client/mock_node_selector_test.go +++ b/common/client/mock_node_selector_test.go @@ -8,20 +8,20 @@ import ( ) // mockNodeSelector is an autogenerated mock type for the NodeSelector type -type mockNodeSelector[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNodeSelector[CHAIN_ID types.ID, RPC interface{}] struct { mock.Mock } -type mockNodeSelector_Expecter[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNodeSelector_Expecter[CHAIN_ID types.ID, RPC interface{}] struct { mock *mock.Mock } -func (_m *mockNodeSelector[CHAIN_ID, HEAD, RPC]) EXPECT() *mockNodeSelector_Expecter[CHAIN_ID, HEAD, RPC] { - return &mockNodeSelector_Expecter[CHAIN_ID, HEAD, RPC]{mock: &_m.Mock} +func (_m *mockNodeSelector[CHAIN_ID, RPC]) EXPECT() *mockNodeSelector_Expecter[CHAIN_ID, RPC] { + return &mockNodeSelector_Expecter[CHAIN_ID, RPC]{mock: &_m.Mock} } // Name provides a mock function with given fields: -func (_m *mockNodeSelector[CHAIN_ID, HEAD, RPC]) Name() string { +func (_m *mockNodeSelector[CHAIN_ID, RPC]) Name() string { ret := _m.Called() if len(ret) == 0 { @@ -39,46 +39,46 @@ func (_m *mockNodeSelector[CHAIN_ID, HEAD, RPC]) Name() 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, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNodeSelector_Name_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // Name is a helper method to define mock.On call -func (_e *mockNodeSelector_Expecter[CHAIN_ID, HEAD, RPC]) Name() *mockNodeSelector_Name_Call[CHAIN_ID, HEAD, RPC] { - return &mockNodeSelector_Name_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("Name")} +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("Name")} } -func (_c *mockNodeSelector_Name_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNodeSelector_Name_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNodeSelector_Name_Call[CHAIN_ID, RPC]) Run(run func()) *mockNodeSelector_Name_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *mockNodeSelector_Name_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 string) *mockNodeSelector_Name_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNodeSelector_Name_Call[CHAIN_ID, RPC]) Return(_a0 string) *mockNodeSelector_Name_Call[CHAIN_ID, RPC] { _c.Call.Return(_a0) return _c } -func (_c *mockNodeSelector_Name_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func() string) *mockNodeSelector_Name_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNodeSelector_Name_Call[CHAIN_ID, RPC]) RunAndReturn(run func() string) *mockNodeSelector_Name_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // Select provides a mock function with given fields: -func (_m *mockNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HEAD, RPC] { +func (_m *mockNodeSelector[CHAIN_ID, RPC]) Select() Node[CHAIN_ID, RPC] { ret := _m.Called() if len(ret) == 0 { panic("no return value specified for Select") } - var r0 Node[CHAIN_ID, HEAD, RPC] - if rf, ok := ret.Get(0).(func() Node[CHAIN_ID, HEAD, RPC]); ok { + var r0 Node[CHAIN_ID, RPC] + if rf, ok := ret.Get(0).(func() Node[CHAIN_ID, RPC]); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(Node[CHAIN_ID, HEAD, RPC]) + r0 = ret.Get(0).(Node[CHAIN_ID, RPC]) } } @@ -86,39 +86,39 @@ func (_m *mockNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HEAD, R } // mockNodeSelector_Select_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Select' -type mockNodeSelector_Select_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNodeSelector_Select_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // Select is a helper method to define mock.On call -func (_e *mockNodeSelector_Expecter[CHAIN_ID, HEAD, RPC]) Select() *mockNodeSelector_Select_Call[CHAIN_ID, HEAD, RPC] { - return &mockNodeSelector_Select_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("Select")} +func (_e *mockNodeSelector_Expecter[CHAIN_ID, RPC]) Select() *mockNodeSelector_Select_Call[CHAIN_ID, RPC] { + return &mockNodeSelector_Select_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Select")} } -func (_c *mockNodeSelector_Select_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNodeSelector_Select_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNodeSelector_Select_Call[CHAIN_ID, RPC]) Run(run func()) *mockNodeSelector_Select_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *mockNodeSelector_Select_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 Node[CHAIN_ID, HEAD, RPC]) *mockNodeSelector_Select_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNodeSelector_Select_Call[CHAIN_ID, RPC]) Return(_a0 Node[CHAIN_ID, RPC]) *mockNodeSelector_Select_Call[CHAIN_ID, RPC] { _c.Call.Return(_a0) return _c } -func (_c *mockNodeSelector_Select_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func() Node[CHAIN_ID, HEAD, RPC]) *mockNodeSelector_Select_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNodeSelector_Select_Call[CHAIN_ID, RPC]) RunAndReturn(run func() Node[CHAIN_ID, RPC]) *mockNodeSelector_Select_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // newMockNodeSelector creates a new instance of mockNodeSelector. 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 newMockNodeSelector[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]](t interface { +func newMockNodeSelector[CHAIN_ID types.ID, RPC interface{}](t interface { mock.TestingT Cleanup(func()) -}) *mockNodeSelector[CHAIN_ID, HEAD, RPC] { - mock := &mockNodeSelector[CHAIN_ID, HEAD, RPC]{} +}) *mockNodeSelector[CHAIN_ID, RPC] { + mock := &mockNodeSelector[CHAIN_ID, RPC]{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) diff --git a/common/client/mock_node_test.go b/common/client/mock_node_test.go index a342a533a7d..968f980b988 100644 --- a/common/client/mock_node_test.go +++ b/common/client/mock_node_test.go @@ -10,20 +10,20 @@ import ( ) // mockNode is an autogenerated mock type for the Node type -type mockNode[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode[CHAIN_ID types.ID, RPC interface{}] struct { mock.Mock } -type mockNode_Expecter[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_Expecter[CHAIN_ID types.ID, RPC interface{}] struct { mock *mock.Mock } -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) EXPECT() *mockNode_Expecter[CHAIN_ID, HEAD, RPC] { - return &mockNode_Expecter[CHAIN_ID, HEAD, RPC]{mock: &_m.Mock} +func (_m *mockNode[CHAIN_ID, RPC]) EXPECT() *mockNode_Expecter[CHAIN_ID, RPC] { + return &mockNode_Expecter[CHAIN_ID, RPC]{mock: &_m.Mock} } // Close provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Close() error { +func (_m *mockNode[CHAIN_ID, RPC]) Close() error { ret := _m.Called() if len(ret) == 0 { @@ -41,34 +41,34 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Close() error { } // mockNode_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' -type mockNode_Close_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_Close_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // Close is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) Close() *mockNode_Close_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_Close_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("Close")} +func (_e *mockNode_Expecter[CHAIN_ID, RPC]) Close() *mockNode_Close_Call[CHAIN_ID, RPC] { + return &mockNode_Close_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Close")} } -func (_c *mockNode_Close_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNode_Close_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_Close_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_Close_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *mockNode_Close_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 error) *mockNode_Close_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_Close_Call[CHAIN_ID, RPC]) Return(_a0 error) *mockNode_Close_Call[CHAIN_ID, RPC] { _c.Call.Return(_a0) return _c } -func (_c *mockNode_Close_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func() error) *mockNode_Close_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_Close_Call[CHAIN_ID, RPC]) RunAndReturn(run func() error) *mockNode_Close_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // ConfiguredChainID provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) ConfiguredChainID() CHAIN_ID { +func (_m *mockNode[CHAIN_ID, RPC]) ConfiguredChainID() CHAIN_ID { ret := _m.Called() if len(ret) == 0 { @@ -86,34 +86,34 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) ConfiguredChainID() CHAIN_ID { } // mockNode_ConfiguredChainID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ConfiguredChainID' -type mockNode_ConfiguredChainID_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_ConfiguredChainID_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // ConfiguredChainID is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) ConfiguredChainID() *mockNode_ConfiguredChainID_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_ConfiguredChainID_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("ConfiguredChainID")} +func (_e *mockNode_Expecter[CHAIN_ID, RPC]) ConfiguredChainID() *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC] { + return &mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC]{Call: _e.mock.On("ConfiguredChainID")} } -func (_c *mockNode_ConfiguredChainID_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNode_ConfiguredChainID_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *mockNode_ConfiguredChainID_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 CHAIN_ID) *mockNode_ConfiguredChainID_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC]) Return(_a0 CHAIN_ID) *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC] { _c.Call.Return(_a0) return _c } -func (_c *mockNode_ConfiguredChainID_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func() CHAIN_ID) *mockNode_ConfiguredChainID_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC]) RunAndReturn(run func() CHAIN_ID) *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // HighestUserObservations provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) HighestUserObservations() ChainInfo { +func (_m *mockNode[CHAIN_ID, RPC]) HighestUserObservations() ChainInfo { ret := _m.Called() if len(ret) == 0 { @@ -131,34 +131,34 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) HighestUserObservations() ChainInfo { } // mockNode_HighestUserObservations_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HighestUserObservations' -type mockNode_HighestUserObservations_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_HighestUserObservations_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // HighestUserObservations is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) HighestUserObservations() *mockNode_HighestUserObservations_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_HighestUserObservations_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("HighestUserObservations")} +func (_e *mockNode_Expecter[CHAIN_ID, RPC]) HighestUserObservations() *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC] { + return &mockNode_HighestUserObservations_Call[CHAIN_ID, RPC]{Call: _e.mock.On("HighestUserObservations")} } -func (_c *mockNode_HighestUserObservations_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNode_HighestUserObservations_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *mockNode_HighestUserObservations_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 ChainInfo) *mockNode_HighestUserObservations_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC]) Return(_a0 ChainInfo) *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC] { _c.Call.Return(_a0) return _c } -func (_c *mockNode_HighestUserObservations_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func() ChainInfo) *mockNode_HighestUserObservations_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC]) RunAndReturn(run func() ChainInfo) *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // Name provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Name() string { +func (_m *mockNode[CHAIN_ID, RPC]) Name() string { ret := _m.Called() if len(ret) == 0 { @@ -176,34 +176,34 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Name() 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, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_Name_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // Name is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) Name() *mockNode_Name_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_Name_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("Name")} +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("Name")} } -func (_c *mockNode_Name_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNode_Name_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_Name_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_Name_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *mockNode_Name_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 string) *mockNode_Name_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_Name_Call[CHAIN_ID, RPC]) Return(_a0 string) *mockNode_Name_Call[CHAIN_ID, RPC] { _c.Call.Return(_a0) return _c } -func (_c *mockNode_Name_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func() string) *mockNode_Name_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_Name_Call[CHAIN_ID, RPC]) RunAndReturn(run func() string) *mockNode_Name_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // Order provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Order() int32 { +func (_m *mockNode[CHAIN_ID, RPC]) Order() int32 { ret := _m.Called() if len(ret) == 0 { @@ -221,34 +221,34 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Order() int32 { } // mockNode_Order_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Order' -type mockNode_Order_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_Order_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // Order is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) Order() *mockNode_Order_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_Order_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("Order")} +func (_e *mockNode_Expecter[CHAIN_ID, RPC]) Order() *mockNode_Order_Call[CHAIN_ID, RPC] { + return &mockNode_Order_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Order")} } -func (_c *mockNode_Order_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNode_Order_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_Order_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_Order_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *mockNode_Order_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 int32) *mockNode_Order_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_Order_Call[CHAIN_ID, RPC]) Return(_a0 int32) *mockNode_Order_Call[CHAIN_ID, RPC] { _c.Call.Return(_a0) return _c } -func (_c *mockNode_Order_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func() int32) *mockNode_Order_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_Order_Call[CHAIN_ID, RPC]) RunAndReturn(run func() int32) *mockNode_Order_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // RPC provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) RPC() RPC { +func (_m *mockNode[CHAIN_ID, RPC]) RPC() RPC { ret := _m.Called() if len(ret) == 0 { @@ -266,67 +266,67 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) RPC() RPC { } // mockNode_RPC_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RPC' -type mockNode_RPC_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_RPC_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // RPC is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) RPC() *mockNode_RPC_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_RPC_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("RPC")} +func (_e *mockNode_Expecter[CHAIN_ID, RPC]) RPC() *mockNode_RPC_Call[CHAIN_ID, RPC] { + return &mockNode_RPC_Call[CHAIN_ID, RPC]{Call: _e.mock.On("RPC")} } -func (_c *mockNode_RPC_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNode_RPC_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_RPC_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_RPC_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *mockNode_RPC_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 RPC) *mockNode_RPC_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_RPC_Call[CHAIN_ID, RPC]) Return(_a0 RPC) *mockNode_RPC_Call[CHAIN_ID, RPC] { _c.Call.Return(_a0) return _c } -func (_c *mockNode_RPC_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func() RPC) *mockNode_RPC_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_RPC_Call[CHAIN_ID, RPC]) RunAndReturn(run func() RPC) *mockNode_RPC_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // SetPoolChainInfoProvider provides a mock function with given fields: _a0 -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) SetPoolChainInfoProvider(_a0 PoolChainInfoProvider) { +func (_m *mockNode[CHAIN_ID, RPC]) SetPoolChainInfoProvider(_a0 PoolChainInfoProvider) { _m.Called(_a0) } // mockNode_SetPoolChainInfoProvider_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPoolChainInfoProvider' -type mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // SetPoolChainInfoProvider is a helper method to define mock.On call // - _a0 PoolChainInfoProvider -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) SetPoolChainInfoProvider(_a0 interface{}) *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("SetPoolChainInfoProvider", _a0)} +func (_e *mockNode_Expecter[CHAIN_ID, RPC]) SetPoolChainInfoProvider(_a0 interface{}) *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC] { + return &mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC]{Call: _e.mock.On("SetPoolChainInfoProvider", _a0)} } -func (_c *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, HEAD, RPC]) Run(run func(_a0 PoolChainInfoProvider)) *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC]) Run(run func(_a0 PoolChainInfoProvider)) *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run(args[0].(PoolChainInfoProvider)) }) return _c } -func (_c *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, HEAD, RPC]) Return() *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC]) Return() *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC] { _c.Call.Return() return _c } -func (_c *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func(PoolChainInfoProvider)) *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC]) RunAndReturn(run func(PoolChainInfoProvider)) *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // Start provides a mock function with given fields: _a0 -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Start(_a0 context.Context) error { +func (_m *mockNode[CHAIN_ID, RPC]) Start(_a0 context.Context) error { ret := _m.Called(_a0) if len(ret) == 0 { @@ -344,35 +344,35 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Start(_a0 context.Context) error { } // mockNode_Start_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Start' -type mockNode_Start_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_Start_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // Start is a helper method to define mock.On call // - _a0 context.Context -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) Start(_a0 interface{}) *mockNode_Start_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_Start_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("Start", _a0)} +func (_e *mockNode_Expecter[CHAIN_ID, RPC]) Start(_a0 interface{}) *mockNode_Start_Call[CHAIN_ID, RPC] { + return &mockNode_Start_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Start", _a0)} } -func (_c *mockNode_Start_Call[CHAIN_ID, HEAD, RPC]) Run(run func(_a0 context.Context)) *mockNode_Start_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_Start_Call[CHAIN_ID, RPC]) Run(run func(_a0 context.Context)) *mockNode_Start_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context)) }) return _c } -func (_c *mockNode_Start_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 error) *mockNode_Start_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_Start_Call[CHAIN_ID, RPC]) Return(_a0 error) *mockNode_Start_Call[CHAIN_ID, RPC] { _c.Call.Return(_a0) return _c } -func (_c *mockNode_Start_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func(context.Context) error) *mockNode_Start_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_Start_Call[CHAIN_ID, RPC]) RunAndReturn(run func(context.Context) error) *mockNode_Start_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // State provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) State() nodeState { +func (_m *mockNode[CHAIN_ID, RPC]) State() nodeState { ret := _m.Called() if len(ret) == 0 { @@ -390,34 +390,34 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) State() nodeState { } // mockNode_State_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'State' -type mockNode_State_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_State_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // State is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) State() *mockNode_State_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_State_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("State")} +func (_e *mockNode_Expecter[CHAIN_ID, RPC]) State() *mockNode_State_Call[CHAIN_ID, RPC] { + return &mockNode_State_Call[CHAIN_ID, RPC]{Call: _e.mock.On("State")} } -func (_c *mockNode_State_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNode_State_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_State_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_State_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *mockNode_State_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 nodeState) *mockNode_State_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_State_Call[CHAIN_ID, RPC]) Return(_a0 nodeState) *mockNode_State_Call[CHAIN_ID, RPC] { _c.Call.Return(_a0) return _c } -func (_c *mockNode_State_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func() nodeState) *mockNode_State_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_State_Call[CHAIN_ID, RPC]) RunAndReturn(run func() nodeState) *mockNode_State_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // StateAndLatest provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, ChainInfo) { +func (_m *mockNode[CHAIN_ID, RPC]) StateAndLatest() (nodeState, ChainInfo) { ret := _m.Called() if len(ret) == 0 { @@ -445,34 +445,34 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, ChainInfo) } // mockNode_StateAndLatest_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StateAndLatest' -type mockNode_StateAndLatest_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_StateAndLatest_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // StateAndLatest is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) StateAndLatest() *mockNode_StateAndLatest_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_StateAndLatest_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("StateAndLatest")} +func (_e *mockNode_Expecter[CHAIN_ID, RPC]) StateAndLatest() *mockNode_StateAndLatest_Call[CHAIN_ID, RPC] { + return &mockNode_StateAndLatest_Call[CHAIN_ID, RPC]{Call: _e.mock.On("StateAndLatest")} } -func (_c *mockNode_StateAndLatest_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNode_StateAndLatest_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_StateAndLatest_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_StateAndLatest_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *mockNode_StateAndLatest_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 nodeState, _a1 ChainInfo) *mockNode_StateAndLatest_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_StateAndLatest_Call[CHAIN_ID, RPC]) Return(_a0 nodeState, _a1 ChainInfo) *mockNode_StateAndLatest_Call[CHAIN_ID, RPC] { _c.Call.Return(_a0, _a1) return _c } -func (_c *mockNode_StateAndLatest_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func() (nodeState, ChainInfo)) *mockNode_StateAndLatest_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_StateAndLatest_Call[CHAIN_ID, RPC]) RunAndReturn(run func() (nodeState, ChainInfo)) *mockNode_StateAndLatest_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // String provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) String() string { +func (_m *mockNode[CHAIN_ID, RPC]) String() string { ret := _m.Called() if len(ret) == 0 { @@ -490,116 +490,71 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) String() string { } // mockNode_String_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' -type mockNode_String_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_String_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // String is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) String() *mockNode_String_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_String_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("String")} +func (_e *mockNode_Expecter[CHAIN_ID, RPC]) String() *mockNode_String_Call[CHAIN_ID, RPC] { + return &mockNode_String_Call[CHAIN_ID, RPC]{Call: _e.mock.On("String")} } -func (_c *mockNode_String_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNode_String_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_String_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_String_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *mockNode_String_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 string) *mockNode_String_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_String_Call[CHAIN_ID, RPC]) Return(_a0 string) *mockNode_String_Call[CHAIN_ID, RPC] { _c.Call.Return(_a0) return _c } -func (_c *mockNode_String_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func() string) *mockNode_String_Call[CHAIN_ID, HEAD, RPC] { - _c.Call.Return(run) - return _c -} - -// SubscribersCount provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) SubscribersCount() int32 { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for SubscribersCount") - } - - var r0 int32 - if rf, ok := ret.Get(0).(func() int32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int32) - } - - return r0 -} - -// mockNode_SubscribersCount_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribersCount' -type mockNode_SubscribersCount_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { - *mock.Call -} - -// SubscribersCount is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) SubscribersCount() *mockNode_SubscribersCount_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_SubscribersCount_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("SubscribersCount")} -} - -func (_c *mockNode_SubscribersCount_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNode_SubscribersCount_Call[CHAIN_ID, HEAD, RPC] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNode_SubscribersCount_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 int32) *mockNode_SubscribersCount_Call[CHAIN_ID, HEAD, RPC] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockNode_SubscribersCount_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func() int32) *mockNode_SubscribersCount_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_String_Call[CHAIN_ID, RPC]) RunAndReturn(run func() string) *mockNode_String_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // UnsubscribeAllExceptAliveLoop provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) UnsubscribeAllExceptAliveLoop() { +func (_m *mockNode[CHAIN_ID, RPC]) UnsubscribeAllExceptAliveLoop() { _m.Called() } // mockNode_UnsubscribeAllExceptAliveLoop_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UnsubscribeAllExceptAliveLoop' -type mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // UnsubscribeAllExceptAliveLoop is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) UnsubscribeAllExceptAliveLoop() *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("UnsubscribeAllExceptAliveLoop")} +func (_e *mockNode_Expecter[CHAIN_ID, RPC]) UnsubscribeAllExceptAliveLoop() *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC] { + return &mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC]{Call: _e.mock.On("UnsubscribeAllExceptAliveLoop")} } -func (_c *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD, RPC]) Return() *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC]) Return() *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC] { _c.Call.Return() return _c } -func (_c *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func()) *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC]) RunAndReturn(run func()) *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // newMockNode creates a new instance of mockNode. 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 newMockNode[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]](t interface { +func newMockNode[CHAIN_ID types.ID, RPC interface{}](t interface { mock.TestingT Cleanup(func()) -}) *mockNode[CHAIN_ID, HEAD, RPC] { - mock := &mockNode[CHAIN_ID, HEAD, RPC]{} +}) *mockNode[CHAIN_ID, RPC] { + mock := &mockNode[CHAIN_ID, RPC]{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) diff --git a/common/client/mock_rpc_client_test.go b/common/client/mock_rpc_client_test.go new file mode 100644 index 00000000000..30452ded77b --- /dev/null +++ b/common/client/mock_rpc_client_test.go @@ -0,0 +1,508 @@ +// Code generated by mockery v2.43.2. DO NOT EDIT. + +package client + +import ( + context "context" + + types "github.com/smartcontractkit/chainlink/v2/common/types" + mock "github.com/stretchr/testify/mock" +) + +// mockRPCClient is an autogenerated mock type for the RPCClient type +type mockRPCClient[CHAIN_ID types.ID, HEAD Head] struct { + mock.Mock +} + +type mockRPCClient_Expecter[CHAIN_ID types.ID, HEAD Head] struct { + mock *mock.Mock +} + +func (_m *mockRPCClient[CHAIN_ID, HEAD]) EXPECT() *mockRPCClient_Expecter[CHAIN_ID, HEAD] { + return &mockRPCClient_Expecter[CHAIN_ID, HEAD]{mock: &_m.Mock} +} + +// ChainID provides a mock function with given fields: ctx +func (_m *mockRPCClient[CHAIN_ID, HEAD]) ChainID(ctx context.Context) (CHAIN_ID, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for ChainID") + } + + var r0 CHAIN_ID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (CHAIN_ID, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) CHAIN_ID); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(CHAIN_ID) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// mockRPCClient_ChainID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChainID' +type mockRPCClient_ChainID_Call[CHAIN_ID types.ID, HEAD Head] struct { + *mock.Call +} + +// ChainID is a helper method to define mock.On call +// - ctx context.Context +func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) ChainID(ctx interface{}) *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD] { + return &mockRPCClient_ChainID_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("ChainID", ctx)} +} + +func (_c *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD] { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD]) Return(_a0 CHAIN_ID, _a1 error) *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD] { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (CHAIN_ID, error)) *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD] { + _c.Call.Return(run) + return _c +} + +// Close provides a mock function with given fields: +func (_m *mockRPCClient[CHAIN_ID, HEAD]) Close() { + _m.Called() +} + +// mockRPCClient_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' +type mockRPCClient_Close_Call[CHAIN_ID types.ID, HEAD Head] struct { + *mock.Call +} + +// Close is a helper method to define mock.On call +func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) Close() *mockRPCClient_Close_Call[CHAIN_ID, HEAD] { + return &mockRPCClient_Close_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("Close")} +} + +func (_c *mockRPCClient_Close_Call[CHAIN_ID, HEAD]) Run(run func()) *mockRPCClient_Close_Call[CHAIN_ID, HEAD] { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockRPCClient_Close_Call[CHAIN_ID, HEAD]) Return() *mockRPCClient_Close_Call[CHAIN_ID, HEAD] { + _c.Call.Return() + return _c +} + +func (_c *mockRPCClient_Close_Call[CHAIN_ID, HEAD]) RunAndReturn(run func()) *mockRPCClient_Close_Call[CHAIN_ID, HEAD] { + _c.Call.Return(run) + return _c +} + +// Dial provides a mock function with given fields: ctx +func (_m *mockRPCClient[CHAIN_ID, HEAD]) Dial(ctx context.Context) error { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for Dial") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// mockRPCClient_Dial_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Dial' +type mockRPCClient_Dial_Call[CHAIN_ID types.ID, HEAD Head] struct { + *mock.Call +} + +// Dial is a helper method to define mock.On call +// - ctx context.Context +func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) Dial(ctx interface{}) *mockRPCClient_Dial_Call[CHAIN_ID, HEAD] { + return &mockRPCClient_Dial_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("Dial", ctx)} +} + +func (_c *mockRPCClient_Dial_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockRPCClient_Dial_Call[CHAIN_ID, HEAD] { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *mockRPCClient_Dial_Call[CHAIN_ID, HEAD]) Return(_a0 error) *mockRPCClient_Dial_Call[CHAIN_ID, HEAD] { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockRPCClient_Dial_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) error) *mockRPCClient_Dial_Call[CHAIN_ID, HEAD] { + _c.Call.Return(run) + return _c +} + +// GetInterceptedChainInfo provides a mock function with given fields: +func (_m *mockRPCClient[CHAIN_ID, HEAD]) GetInterceptedChainInfo() (ChainInfo, ChainInfo) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetInterceptedChainInfo") + } + + var r0 ChainInfo + var r1 ChainInfo + if rf, ok := ret.Get(0).(func() (ChainInfo, ChainInfo)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() ChainInfo); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(ChainInfo) + } + + if rf, ok := ret.Get(1).(func() ChainInfo); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(ChainInfo) + } + + return r0, r1 +} + +// mockRPCClient_GetInterceptedChainInfo_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetInterceptedChainInfo' +type mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID types.ID, HEAD Head] struct { + *mock.Call +} + +// GetInterceptedChainInfo is a helper method to define mock.On call +func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) GetInterceptedChainInfo() *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD] { + return &mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("GetInterceptedChainInfo")} +} + +func (_c *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD]) Run(run func()) *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD] { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD]) Return(latest ChainInfo, highestUserObservations ChainInfo) *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD] { + _c.Call.Return(latest, highestUserObservations) + return _c +} + +func (_c *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD]) RunAndReturn(run func() (ChainInfo, ChainInfo)) *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD] { + _c.Call.Return(run) + return _c +} + +// IsSyncing provides a mock function with given fields: ctx +func (_m *mockRPCClient[CHAIN_ID, HEAD]) IsSyncing(ctx context.Context) (bool, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for IsSyncing") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (bool, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) bool); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// mockRPCClient_IsSyncing_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsSyncing' +type mockRPCClient_IsSyncing_Call[CHAIN_ID types.ID, HEAD Head] struct { + *mock.Call +} + +// IsSyncing is a helper method to define mock.On call +// - ctx context.Context +func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) IsSyncing(ctx interface{}) *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD] { + return &mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("IsSyncing", ctx)} +} + +func (_c *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD] { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD]) Return(_a0 bool, _a1 error) *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD] { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (bool, error)) *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD] { + _c.Call.Return(run) + return _c +} + +// Ping provides a mock function with given fields: _a0 +func (_m *mockRPCClient[CHAIN_ID, HEAD]) Ping(_a0 context.Context) error { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for Ping") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// mockRPCClient_Ping_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Ping' +type mockRPCClient_Ping_Call[CHAIN_ID types.ID, HEAD Head] struct { + *mock.Call +} + +// Ping is a helper method to define mock.On call +// - _a0 context.Context +func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) Ping(_a0 interface{}) *mockRPCClient_Ping_Call[CHAIN_ID, HEAD] { + return &mockRPCClient_Ping_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("Ping", _a0)} +} + +func (_c *mockRPCClient_Ping_Call[CHAIN_ID, HEAD]) Run(run func(_a0 context.Context)) *mockRPCClient_Ping_Call[CHAIN_ID, HEAD] { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *mockRPCClient_Ping_Call[CHAIN_ID, HEAD]) Return(_a0 error) *mockRPCClient_Ping_Call[CHAIN_ID, HEAD] { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockRPCClient_Ping_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) error) *mockRPCClient_Ping_Call[CHAIN_ID, HEAD] { + _c.Call.Return(run) + return _c +} + +// SubscribeToFinalizedHeads provides a mock function with given fields: ctx +func (_m *mockRPCClient[CHAIN_ID, HEAD]) SubscribeToFinalizedHeads(ctx context.Context) (<-chan HEAD, types.Subscription, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for SubscribeToFinalizedHeads") + } + + var r0 <-chan HEAD + var r1 types.Subscription + var r2 error + if rf, ok := ret.Get(0).(func(context.Context) (<-chan HEAD, types.Subscription, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) <-chan HEAD); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(<-chan HEAD) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) types.Subscription); ok { + r1 = rf(ctx) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(types.Subscription) + } + } + + if rf, ok := ret.Get(2).(func(context.Context) error); ok { + r2 = rf(ctx) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// mockRPCClient_SubscribeToFinalizedHeads_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeToFinalizedHeads' +type mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID types.ID, HEAD Head] struct { + *mock.Call +} + +// SubscribeToFinalizedHeads is a helper method to define mock.On call +// - ctx context.Context +func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) SubscribeToFinalizedHeads(ctx interface{}) *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD] { + return &mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("SubscribeToFinalizedHeads", ctx)} +} + +func (_c *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD] { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD]) Return(_a0 <-chan HEAD, _a1 types.Subscription, _a2 error) *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD] { + _c.Call.Return(_a0, _a1, _a2) + return _c +} + +func (_c *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (<-chan HEAD, types.Subscription, error)) *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD] { + _c.Call.Return(run) + return _c +} + +// SubscribeToHeads provides a mock function with given fields: ctx +func (_m *mockRPCClient[CHAIN_ID, HEAD]) SubscribeToHeads(ctx context.Context) (<-chan HEAD, types.Subscription, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for SubscribeToHeads") + } + + var r0 <-chan HEAD + var r1 types.Subscription + var r2 error + if rf, ok := ret.Get(0).(func(context.Context) (<-chan HEAD, types.Subscription, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) <-chan HEAD); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(<-chan HEAD) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) types.Subscription); ok { + r1 = rf(ctx) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(types.Subscription) + } + } + + if rf, ok := ret.Get(2).(func(context.Context) error); ok { + r2 = rf(ctx) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// mockRPCClient_SubscribeToHeads_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeToHeads' +type mockRPCClient_SubscribeToHeads_Call[CHAIN_ID types.ID, HEAD Head] struct { + *mock.Call +} + +// SubscribeToHeads is a helper method to define mock.On call +// - ctx context.Context +func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) SubscribeToHeads(ctx interface{}) *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD] { + return &mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("SubscribeToHeads", ctx)} +} + +func (_c *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD] { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD]) Return(_a0 <-chan HEAD, _a1 types.Subscription, _a2 error) *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD] { + _c.Call.Return(_a0, _a1, _a2) + return _c +} + +func (_c *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (<-chan HEAD, types.Subscription, error)) *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD] { + _c.Call.Return(run) + return _c +} + +// UnsubscribeAllExcept provides a mock function with given fields: subs +func (_m *mockRPCClient[CHAIN_ID, HEAD]) UnsubscribeAllExcept(subs ...types.Subscription) { + _va := make([]interface{}, len(subs)) + for _i := range subs { + _va[_i] = subs[_i] + } + var _ca []interface{} + _ca = append(_ca, _va...) + _m.Called(_ca...) +} + +// mockRPCClient_UnsubscribeAllExcept_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UnsubscribeAllExcept' +type mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID types.ID, HEAD Head] struct { + *mock.Call +} + +// UnsubscribeAllExcept is a helper method to define mock.On call +// - subs ...types.Subscription +func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) UnsubscribeAllExcept(subs ...interface{}) *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD] { + return &mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("UnsubscribeAllExcept", + append([]interface{}{}, subs...)...)} +} + +func (_c *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD]) Run(run func(subs ...types.Subscription)) *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD] { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]types.Subscription, len(args)-0) + for i, a := range args[0:] { + if a != nil { + variadicArgs[i] = a.(types.Subscription) + } + } + run(variadicArgs...) + }) + return _c +} + +func (_c *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD]) Return() *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD] { + _c.Call.Return() + return _c +} + +func (_c *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(...types.Subscription)) *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD] { + _c.Call.Return(run) + return _c +} + +// newMockRPCClient creates a new instance of mockRPCClient. 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 newMockRPCClient[CHAIN_ID types.ID, HEAD Head](t interface { + mock.TestingT + Cleanup(func()) +}) *mockRPCClient[CHAIN_ID, HEAD] { + mock := &mockRPCClient[CHAIN_ID, HEAD]{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/common/client/mock_rpc_test.go b/common/client/mock_rpc_test.go deleted file mode 100644 index 00473c66369..00000000000 --- a/common/client/mock_rpc_test.go +++ /dev/null @@ -1,1918 +0,0 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. - -package client - -import ( - big "math/big" - - assets "github.com/smartcontractkit/chainlink-common/pkg/assets" - - context "context" - - feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" - - mock "github.com/stretchr/testify/mock" - - types "github.com/smartcontractkit/chainlink/v2/common/types" -) - -// mockRPC is an autogenerated mock type for the RPC type -type mockRPC[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - mock.Mock -} - -type mockRPC_Expecter[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - mock *mock.Mock -} - -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) EXPECT() *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{mock: &_m.Mock} -} - -// BalanceAt provides a mock function with given fields: ctx, accountAddress, blockNumber -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) BalanceAt(ctx context.Context, accountAddress ADDR, blockNumber *big.Int) (*big.Int, error) { - ret := _m.Called(ctx, accountAddress, blockNumber) - - if len(ret) == 0 { - panic("no return value specified for BalanceAt") - } - - var r0 *big.Int - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, ADDR, *big.Int) (*big.Int, error)); ok { - return rf(ctx, accountAddress, blockNumber) - } - if rf, ok := ret.Get(0).(func(context.Context, ADDR, *big.Int) *big.Int); ok { - r0 = rf(ctx, accountAddress, blockNumber) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*big.Int) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, ADDR, *big.Int) error); ok { - r1 = rf(ctx, accountAddress, blockNumber) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_BalanceAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BalanceAt' -type mockRPC_BalanceAt_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// BalanceAt is a helper method to define mock.On call -// - ctx context.Context -// - accountAddress ADDR -// - blockNumber *big.Int -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) BalanceAt(ctx interface{}, accountAddress interface{}, blockNumber interface{}) *mockRPC_BalanceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_BalanceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("BalanceAt", ctx, accountAddress, blockNumber)} -} - -func (_c *mockRPC_BalanceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, accountAddress ADDR, blockNumber *big.Int)) *mockRPC_BalanceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(ADDR), args[2].(*big.Int)) - }) - return _c -} - -func (_c *mockRPC_BalanceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 *big.Int, _a1 error) *mockRPC_BalanceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_BalanceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, ADDR, *big.Int) (*big.Int, error)) *mockRPC_BalanceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// BatchCallContext provides a mock function with given fields: ctx, b -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) BatchCallContext(ctx context.Context, b []BATCH_ELEM) error { - ret := _m.Called(ctx, b) - - if len(ret) == 0 { - panic("no return value specified for BatchCallContext") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, []BATCH_ELEM) error); ok { - r0 = rf(ctx, b) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// mockRPC_BatchCallContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BatchCallContext' -type mockRPC_BatchCallContext_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// BatchCallContext is a helper method to define mock.On call -// - ctx context.Context -// - b []BATCH_ELEM -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) BatchCallContext(ctx interface{}, b interface{}) *mockRPC_BatchCallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_BatchCallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("BatchCallContext", ctx, b)} -} - -func (_c *mockRPC_BatchCallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, b []BATCH_ELEM)) *mockRPC_BatchCallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].([]BATCH_ELEM)) - }) - return _c -} - -func (_c *mockRPC_BatchCallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 error) *mockRPC_BatchCallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockRPC_BatchCallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, []BATCH_ELEM) error) *mockRPC_BatchCallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// BlockByHash provides a mock function with given fields: ctx, hash -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) BlockByHash(ctx context.Context, hash BLOCK_HASH) (HEAD, error) { - ret := _m.Called(ctx, hash) - - if len(ret) == 0 { - panic("no return value specified for BlockByHash") - } - - var r0 HEAD - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, BLOCK_HASH) (HEAD, error)); ok { - return rf(ctx, hash) - } - if rf, ok := ret.Get(0).(func(context.Context, BLOCK_HASH) HEAD); ok { - r0 = rf(ctx, hash) - } else { - r0 = ret.Get(0).(HEAD) - } - - if rf, ok := ret.Get(1).(func(context.Context, BLOCK_HASH) error); ok { - r1 = rf(ctx, hash) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_BlockByHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockByHash' -type mockRPC_BlockByHash_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// BlockByHash is a helper method to define mock.On call -// - ctx context.Context -// - hash BLOCK_HASH -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) BlockByHash(ctx interface{}, hash interface{}) *mockRPC_BlockByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_BlockByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("BlockByHash", ctx, hash)} -} - -func (_c *mockRPC_BlockByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, hash BLOCK_HASH)) *mockRPC_BlockByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(BLOCK_HASH)) - }) - return _c -} - -func (_c *mockRPC_BlockByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 HEAD, _a1 error) *mockRPC_BlockByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_BlockByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, BLOCK_HASH) (HEAD, error)) *mockRPC_BlockByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// BlockByNumber provides a mock function with given fields: ctx, number -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) BlockByNumber(ctx context.Context, number *big.Int) (HEAD, error) { - ret := _m.Called(ctx, number) - - if len(ret) == 0 { - panic("no return value specified for BlockByNumber") - } - - var r0 HEAD - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (HEAD, error)); ok { - return rf(ctx, number) - } - if rf, ok := ret.Get(0).(func(context.Context, *big.Int) HEAD); ok { - r0 = rf(ctx, number) - } else { - r0 = ret.Get(0).(HEAD) - } - - if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { - r1 = rf(ctx, number) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_BlockByNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockByNumber' -type mockRPC_BlockByNumber_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// BlockByNumber is a helper method to define mock.On call -// - ctx context.Context -// - number *big.Int -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) BlockByNumber(ctx interface{}, number interface{}) *mockRPC_BlockByNumber_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_BlockByNumber_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("BlockByNumber", ctx, number)} -} - -func (_c *mockRPC_BlockByNumber_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, number *big.Int)) *mockRPC_BlockByNumber_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*big.Int)) - }) - return _c -} - -func (_c *mockRPC_BlockByNumber_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 HEAD, _a1 error) *mockRPC_BlockByNumber_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_BlockByNumber_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, *big.Int) (HEAD, error)) *mockRPC_BlockByNumber_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// CallContext provides a mock function with given fields: ctx, result, method, args -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { - var _ca []interface{} - _ca = append(_ca, ctx, result, method) - _ca = append(_ca, args...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for CallContext") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, interface{}, string, ...interface{}) error); ok { - r0 = rf(ctx, result, method, args...) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// mockRPC_CallContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CallContext' -type mockRPC_CallContext_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// CallContext is a helper method to define mock.On call -// - ctx context.Context -// - result interface{} -// - method string -// - args ...interface{} -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) CallContext(ctx interface{}, result interface{}, method interface{}, args ...interface{}) *mockRPC_CallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_CallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("CallContext", - append([]interface{}{ctx, result, method}, args...)...)} -} - -func (_c *mockRPC_CallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, result interface{}, method string, args ...interface{})) *mockRPC_CallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]interface{}, len(args)-3) - for i, a := range args[3:] { - if a != nil { - variadicArgs[i] = a.(interface{}) - } - } - run(args[0].(context.Context), args[1].(interface{}), args[2].(string), variadicArgs...) - }) - return _c -} - -func (_c *mockRPC_CallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 error) *mockRPC_CallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockRPC_CallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, interface{}, string, ...interface{}) error) *mockRPC_CallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// CallContract provides a mock function with given fields: ctx, msg, blockNumber -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) CallContract(ctx context.Context, msg interface{}, blockNumber *big.Int) ([]byte, error) { - ret := _m.Called(ctx, msg, blockNumber) - - if len(ret) == 0 { - panic("no return value specified for CallContract") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, interface{}, *big.Int) ([]byte, error)); ok { - return rf(ctx, msg, blockNumber) - } - if rf, ok := ret.Get(0).(func(context.Context, interface{}, *big.Int) []byte); ok { - r0 = rf(ctx, msg, blockNumber) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, interface{}, *big.Int) error); ok { - r1 = rf(ctx, msg, blockNumber) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_CallContract_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CallContract' -type mockRPC_CallContract_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// CallContract is a helper method to define mock.On call -// - ctx context.Context -// - msg interface{} -// - blockNumber *big.Int -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) CallContract(ctx interface{}, msg interface{}, blockNumber interface{}) *mockRPC_CallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_CallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("CallContract", ctx, msg, blockNumber)} -} - -func (_c *mockRPC_CallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, msg interface{}, blockNumber *big.Int)) *mockRPC_CallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(interface{}), args[2].(*big.Int)) - }) - return _c -} - -func (_c *mockRPC_CallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(rpcErr []byte, extractErr error) *mockRPC_CallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(rpcErr, extractErr) - return _c -} - -func (_c *mockRPC_CallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, interface{}, *big.Int) ([]byte, error)) *mockRPC_CallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// ChainID provides a mock function with given fields: ctx -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) ChainID(ctx context.Context) (CHAIN_ID, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for ChainID") - } - - var r0 CHAIN_ID - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (CHAIN_ID, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) CHAIN_ID); ok { - r0 = rf(ctx) - } else { - r0 = ret.Get(0).(CHAIN_ID) - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_ChainID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChainID' -type mockRPC_ChainID_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// ChainID is a helper method to define mock.On call -// - ctx context.Context -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) ChainID(ctx interface{}) *mockRPC_ChainID_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_ChainID_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("ChainID", ctx)} -} - -func (_c *mockRPC_ChainID_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context)) *mockRPC_ChainID_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockRPC_ChainID_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 CHAIN_ID, _a1 error) *mockRPC_ChainID_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_ChainID_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context) (CHAIN_ID, error)) *mockRPC_ChainID_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// ClientVersion provides a mock function with given fields: _a0 -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) ClientVersion(_a0 context.Context) (string, error) { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for ClientVersion") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (string, error)); ok { - return rf(_a0) - } - if rf, ok := ret.Get(0).(func(context.Context) string); ok { - r0 = rf(_a0) - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(_a0) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_ClientVersion_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ClientVersion' -type mockRPC_ClientVersion_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// ClientVersion is a helper method to define mock.On call -// - _a0 context.Context -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) ClientVersion(_a0 interface{}) *mockRPC_ClientVersion_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_ClientVersion_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("ClientVersion", _a0)} -} - -func (_c *mockRPC_ClientVersion_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(_a0 context.Context)) *mockRPC_ClientVersion_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockRPC_ClientVersion_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 string, _a1 error) *mockRPC_ClientVersion_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_ClientVersion_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context) (string, error)) *mockRPC_ClientVersion_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// Close provides a mock function with given fields: -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Close() { - _m.Called() -} - -// mockRPC_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' -type mockRPC_Close_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// Close is a helper method to define mock.On call -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Close() *mockRPC_Close_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_Close_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("Close")} -} - -func (_c *mockRPC_Close_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func()) *mockRPC_Close_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockRPC_Close_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return() *mockRPC_Close_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return() - return _c -} - -func (_c *mockRPC_Close_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func()) *mockRPC_Close_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// CodeAt provides a mock function with given fields: ctx, account, blockNumber -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) CodeAt(ctx context.Context, account ADDR, blockNumber *big.Int) ([]byte, error) { - ret := _m.Called(ctx, account, blockNumber) - - if len(ret) == 0 { - panic("no return value specified for CodeAt") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, ADDR, *big.Int) ([]byte, error)); ok { - return rf(ctx, account, blockNumber) - } - if rf, ok := ret.Get(0).(func(context.Context, ADDR, *big.Int) []byte); ok { - r0 = rf(ctx, account, blockNumber) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, ADDR, *big.Int) error); ok { - r1 = rf(ctx, account, blockNumber) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_CodeAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CodeAt' -type mockRPC_CodeAt_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// CodeAt is a helper method to define mock.On call -// - ctx context.Context -// - account ADDR -// - blockNumber *big.Int -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) CodeAt(ctx interface{}, account interface{}, blockNumber interface{}) *mockRPC_CodeAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_CodeAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("CodeAt", ctx, account, blockNumber)} -} - -func (_c *mockRPC_CodeAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, account ADDR, blockNumber *big.Int)) *mockRPC_CodeAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(ADDR), args[2].(*big.Int)) - }) - return _c -} - -func (_c *mockRPC_CodeAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 []byte, _a1 error) *mockRPC_CodeAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_CodeAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, ADDR, *big.Int) ([]byte, error)) *mockRPC_CodeAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// Dial provides a mock function with given fields: ctx -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Dial(ctx context.Context) error { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for Dial") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context) error); ok { - r0 = rf(ctx) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// mockRPC_Dial_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Dial' -type mockRPC_Dial_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// Dial is a helper method to define mock.On call -// - ctx context.Context -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Dial(ctx interface{}) *mockRPC_Dial_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_Dial_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("Dial", ctx)} -} - -func (_c *mockRPC_Dial_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context)) *mockRPC_Dial_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockRPC_Dial_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 error) *mockRPC_Dial_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockRPC_Dial_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context) error) *mockRPC_Dial_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// DialHTTP provides a mock function with given fields: -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) DialHTTP() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for DialHTTP") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// mockRPC_DialHTTP_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DialHTTP' -type mockRPC_DialHTTP_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// DialHTTP is a helper method to define mock.On call -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) DialHTTP() *mockRPC_DialHTTP_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_DialHTTP_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("DialHTTP")} -} - -func (_c *mockRPC_DialHTTP_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func()) *mockRPC_DialHTTP_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockRPC_DialHTTP_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 error) *mockRPC_DialHTTP_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockRPC_DialHTTP_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func() error) *mockRPC_DialHTTP_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// DisconnectAll provides a mock function with given fields: -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) DisconnectAll() { - _m.Called() -} - -// mockRPC_DisconnectAll_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisconnectAll' -type mockRPC_DisconnectAll_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// DisconnectAll is a helper method to define mock.On call -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) DisconnectAll() *mockRPC_DisconnectAll_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_DisconnectAll_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("DisconnectAll")} -} - -func (_c *mockRPC_DisconnectAll_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func()) *mockRPC_DisconnectAll_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockRPC_DisconnectAll_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return() *mockRPC_DisconnectAll_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return() - return _c -} - -func (_c *mockRPC_DisconnectAll_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func()) *mockRPC_DisconnectAll_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// EstimateGas provides a mock function with given fields: ctx, call -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) EstimateGas(ctx context.Context, call interface{}) (uint64, error) { - ret := _m.Called(ctx, call) - - if len(ret) == 0 { - panic("no return value specified for EstimateGas") - } - - var r0 uint64 - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, interface{}) (uint64, error)); ok { - return rf(ctx, call) - } - if rf, ok := ret.Get(0).(func(context.Context, interface{}) uint64); ok { - r0 = rf(ctx, call) - } else { - r0 = ret.Get(0).(uint64) - } - - if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { - r1 = rf(ctx, call) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_EstimateGas_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EstimateGas' -type mockRPC_EstimateGas_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// EstimateGas is a helper method to define mock.On call -// - ctx context.Context -// - call interface{} -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) EstimateGas(ctx interface{}, call interface{}) *mockRPC_EstimateGas_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_EstimateGas_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("EstimateGas", ctx, call)} -} - -func (_c *mockRPC_EstimateGas_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, call interface{})) *mockRPC_EstimateGas_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(interface{})) - }) - return _c -} - -func (_c *mockRPC_EstimateGas_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(gas uint64, err error) *mockRPC_EstimateGas_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(gas, err) - return _c -} - -func (_c *mockRPC_EstimateGas_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, interface{}) (uint64, error)) *mockRPC_EstimateGas_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// FilterEvents provides a mock function with given fields: ctx, query -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) FilterEvents(ctx context.Context, query EVENT_OPS) ([]EVENT, error) { - ret := _m.Called(ctx, query) - - if len(ret) == 0 { - panic("no return value specified for FilterEvents") - } - - var r0 []EVENT - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, EVENT_OPS) ([]EVENT, error)); ok { - return rf(ctx, query) - } - if rf, ok := ret.Get(0).(func(context.Context, EVENT_OPS) []EVENT); ok { - r0 = rf(ctx, query) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]EVENT) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, EVENT_OPS) error); ok { - r1 = rf(ctx, query) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_FilterEvents_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FilterEvents' -type mockRPC_FilterEvents_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// FilterEvents is a helper method to define mock.On call -// - ctx context.Context -// - query EVENT_OPS -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) FilterEvents(ctx interface{}, query interface{}) *mockRPC_FilterEvents_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_FilterEvents_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("FilterEvents", ctx, query)} -} - -func (_c *mockRPC_FilterEvents_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, query EVENT_OPS)) *mockRPC_FilterEvents_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(EVENT_OPS)) - }) - return _c -} - -func (_c *mockRPC_FilterEvents_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 []EVENT, _a1 error) *mockRPC_FilterEvents_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_FilterEvents_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, EVENT_OPS) ([]EVENT, error)) *mockRPC_FilterEvents_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// GetInterceptedChainInfo provides a mock function with given fields: -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) GetInterceptedChainInfo() (ChainInfo, ChainInfo) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetInterceptedChainInfo") - } - - var r0 ChainInfo - var r1 ChainInfo - if rf, ok := ret.Get(0).(func() (ChainInfo, ChainInfo)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() ChainInfo); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(ChainInfo) - } - - if rf, ok := ret.Get(1).(func() ChainInfo); ok { - r1 = rf() - } else { - r1 = ret.Get(1).(ChainInfo) - } - - return r0, r1 -} - -// mockRPC_GetInterceptedChainInfo_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetInterceptedChainInfo' -type mockRPC_GetInterceptedChainInfo_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// GetInterceptedChainInfo is a helper method to define mock.On call -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) GetInterceptedChainInfo() *mockRPC_GetInterceptedChainInfo_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_GetInterceptedChainInfo_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("GetInterceptedChainInfo")} -} - -func (_c *mockRPC_GetInterceptedChainInfo_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func()) *mockRPC_GetInterceptedChainInfo_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockRPC_GetInterceptedChainInfo_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(latest ChainInfo, highestUserObservations ChainInfo) *mockRPC_GetInterceptedChainInfo_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(latest, highestUserObservations) - return _c -} - -func (_c *mockRPC_GetInterceptedChainInfo_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func() (ChainInfo, ChainInfo)) *mockRPC_GetInterceptedChainInfo_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// IsSyncing provides a mock function with given fields: ctx -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) IsSyncing(ctx context.Context) (bool, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for IsSyncing") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (bool, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) bool); ok { - r0 = rf(ctx) - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_IsSyncing_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsSyncing' -type mockRPC_IsSyncing_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// IsSyncing is a helper method to define mock.On call -// - ctx context.Context -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) IsSyncing(ctx interface{}) *mockRPC_IsSyncing_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_IsSyncing_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("IsSyncing", ctx)} -} - -func (_c *mockRPC_IsSyncing_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context)) *mockRPC_IsSyncing_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockRPC_IsSyncing_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 bool, _a1 error) *mockRPC_IsSyncing_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_IsSyncing_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context) (bool, error)) *mockRPC_IsSyncing_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// LINKBalance provides a mock function with given fields: ctx, accountAddress, linkAddress -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) LINKBalance(ctx context.Context, accountAddress ADDR, linkAddress ADDR) (*assets.Link, error) { - ret := _m.Called(ctx, accountAddress, linkAddress) - - if len(ret) == 0 { - panic("no return value specified for LINKBalance") - } - - var r0 *assets.Link - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, ADDR, ADDR) (*assets.Link, error)); ok { - return rf(ctx, accountAddress, linkAddress) - } - if rf, ok := ret.Get(0).(func(context.Context, ADDR, ADDR) *assets.Link); ok { - r0 = rf(ctx, accountAddress, linkAddress) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*assets.Link) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, ADDR, ADDR) error); ok { - r1 = rf(ctx, accountAddress, linkAddress) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_LINKBalance_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LINKBalance' -type mockRPC_LINKBalance_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// LINKBalance is a helper method to define mock.On call -// - ctx context.Context -// - accountAddress ADDR -// - linkAddress ADDR -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) LINKBalance(ctx interface{}, accountAddress interface{}, linkAddress interface{}) *mockRPC_LINKBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_LINKBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("LINKBalance", ctx, accountAddress, linkAddress)} -} - -func (_c *mockRPC_LINKBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, accountAddress ADDR, linkAddress ADDR)) *mockRPC_LINKBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(ADDR), args[2].(ADDR)) - }) - return _c -} - -func (_c *mockRPC_LINKBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 *assets.Link, _a1 error) *mockRPC_LINKBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_LINKBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, ADDR, ADDR) (*assets.Link, error)) *mockRPC_LINKBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// LatestBlockHeight provides a mock function with given fields: _a0 -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) LatestBlockHeight(_a0 context.Context) (*big.Int, error) { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for LatestBlockHeight") - } - - var r0 *big.Int - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (*big.Int, error)); ok { - return rf(_a0) - } - if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { - r0 = rf(_a0) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*big.Int) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(_a0) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_LatestBlockHeight_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LatestBlockHeight' -type mockRPC_LatestBlockHeight_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// LatestBlockHeight is a helper method to define mock.On call -// - _a0 context.Context -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) LatestBlockHeight(_a0 interface{}) *mockRPC_LatestBlockHeight_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_LatestBlockHeight_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("LatestBlockHeight", _a0)} -} - -func (_c *mockRPC_LatestBlockHeight_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(_a0 context.Context)) *mockRPC_LatestBlockHeight_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockRPC_LatestBlockHeight_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 *big.Int, _a1 error) *mockRPC_LatestBlockHeight_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_LatestBlockHeight_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context) (*big.Int, error)) *mockRPC_LatestBlockHeight_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// LatestFinalizedBlock provides a mock function with given fields: ctx -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) LatestFinalizedBlock(ctx context.Context) (HEAD, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for LatestFinalizedBlock") - } - - var r0 HEAD - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (HEAD, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) HEAD); ok { - r0 = rf(ctx) - } else { - r0 = ret.Get(0).(HEAD) - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_LatestFinalizedBlock_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LatestFinalizedBlock' -type mockRPC_LatestFinalizedBlock_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// LatestFinalizedBlock is a helper method to define mock.On call -// - ctx context.Context -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) LatestFinalizedBlock(ctx interface{}) *mockRPC_LatestFinalizedBlock_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_LatestFinalizedBlock_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("LatestFinalizedBlock", ctx)} -} - -func (_c *mockRPC_LatestFinalizedBlock_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context)) *mockRPC_LatestFinalizedBlock_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockRPC_LatestFinalizedBlock_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 HEAD, _a1 error) *mockRPC_LatestFinalizedBlock_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_LatestFinalizedBlock_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context) (HEAD, error)) *mockRPC_LatestFinalizedBlock_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// PendingCallContract provides a mock function with given fields: ctx, msg -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) PendingCallContract(ctx context.Context, msg interface{}) ([]byte, error) { - ret := _m.Called(ctx, msg) - - if len(ret) == 0 { - panic("no return value specified for PendingCallContract") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, interface{}) ([]byte, error)); ok { - return rf(ctx, msg) - } - if rf, ok := ret.Get(0).(func(context.Context, interface{}) []byte); ok { - r0 = rf(ctx, msg) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { - r1 = rf(ctx, msg) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_PendingCallContract_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PendingCallContract' -type mockRPC_PendingCallContract_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// PendingCallContract is a helper method to define mock.On call -// - ctx context.Context -// - msg interface{} -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) PendingCallContract(ctx interface{}, msg interface{}) *mockRPC_PendingCallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_PendingCallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("PendingCallContract", ctx, msg)} -} - -func (_c *mockRPC_PendingCallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, msg interface{})) *mockRPC_PendingCallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(interface{})) - }) - return _c -} - -func (_c *mockRPC_PendingCallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(rpcErr []byte, extractErr error) *mockRPC_PendingCallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(rpcErr, extractErr) - return _c -} - -func (_c *mockRPC_PendingCallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, interface{}) ([]byte, error)) *mockRPC_PendingCallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// PendingSequenceAt provides a mock function with given fields: ctx, addr -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) PendingSequenceAt(ctx context.Context, addr ADDR) (SEQ, error) { - ret := _m.Called(ctx, addr) - - if len(ret) == 0 { - panic("no return value specified for PendingSequenceAt") - } - - var r0 SEQ - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, ADDR) (SEQ, error)); ok { - return rf(ctx, addr) - } - if rf, ok := ret.Get(0).(func(context.Context, ADDR) SEQ); ok { - r0 = rf(ctx, addr) - } else { - r0 = ret.Get(0).(SEQ) - } - - if rf, ok := ret.Get(1).(func(context.Context, ADDR) error); ok { - r1 = rf(ctx, addr) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_PendingSequenceAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PendingSequenceAt' -type mockRPC_PendingSequenceAt_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// PendingSequenceAt is a helper method to define mock.On call -// - ctx context.Context -// - addr ADDR -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) PendingSequenceAt(ctx interface{}, addr interface{}) *mockRPC_PendingSequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_PendingSequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("PendingSequenceAt", ctx, addr)} -} - -func (_c *mockRPC_PendingSequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, addr ADDR)) *mockRPC_PendingSequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(ADDR)) - }) - return _c -} - -func (_c *mockRPC_PendingSequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 SEQ, _a1 error) *mockRPC_PendingSequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_PendingSequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, ADDR) (SEQ, error)) *mockRPC_PendingSequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// SendEmptyTransaction provides a mock function with given fields: ctx, newTxAttempt, seq, gasLimit, fee, fromAddress -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SendEmptyTransaction(ctx context.Context, newTxAttempt func(SEQ, uint32, FEE, ADDR) (interface{}, error), seq SEQ, gasLimit uint32, fee FEE, fromAddress ADDR) (string, error) { - ret := _m.Called(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) - - if len(ret) == 0 { - panic("no return value specified for SendEmptyTransaction") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, func(SEQ, uint32, FEE, ADDR) (interface{}, error), SEQ, uint32, FEE, ADDR) (string, error)); ok { - return rf(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) - } - if rf, ok := ret.Get(0).(func(context.Context, func(SEQ, uint32, FEE, ADDR) (interface{}, error), SEQ, uint32, FEE, ADDR) string); ok { - r0 = rf(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func(context.Context, func(SEQ, uint32, FEE, ADDR) (interface{}, error), SEQ, uint32, FEE, ADDR) error); ok { - r1 = rf(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_SendEmptyTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendEmptyTransaction' -type mockRPC_SendEmptyTransaction_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// SendEmptyTransaction is a helper method to define mock.On call -// - ctx context.Context -// - newTxAttempt func(SEQ , uint32 , FEE , ADDR)(interface{} , error) -// - seq SEQ -// - gasLimit uint32 -// - fee FEE -// - fromAddress ADDR -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SendEmptyTransaction(ctx interface{}, newTxAttempt interface{}, seq interface{}, gasLimit interface{}, fee interface{}, fromAddress interface{}) *mockRPC_SendEmptyTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_SendEmptyTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("SendEmptyTransaction", ctx, newTxAttempt, seq, gasLimit, fee, fromAddress)} -} - -func (_c *mockRPC_SendEmptyTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, newTxAttempt func(SEQ, uint32, FEE, ADDR) (interface{}, error), seq SEQ, gasLimit uint32, fee FEE, fromAddress ADDR)) *mockRPC_SendEmptyTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(func(SEQ, uint32, FEE, ADDR) (interface{}, error)), args[2].(SEQ), args[3].(uint32), args[4].(FEE), args[5].(ADDR)) - }) - return _c -} - -func (_c *mockRPC_SendEmptyTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(txhash string, err error) *mockRPC_SendEmptyTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(txhash, err) - return _c -} - -func (_c *mockRPC_SendEmptyTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, func(SEQ, uint32, FEE, ADDR) (interface{}, error), SEQ, uint32, FEE, ADDR) (string, error)) *mockRPC_SendEmptyTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// SendTransaction provides a mock function with given fields: ctx, tx -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SendTransaction(ctx context.Context, tx TX) error { - ret := _m.Called(ctx, tx) - - if len(ret) == 0 { - panic("no return value specified for SendTransaction") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, TX) error); ok { - r0 = rf(ctx, tx) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// mockRPC_SendTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendTransaction' -type mockRPC_SendTransaction_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// SendTransaction is a helper method to define mock.On call -// - ctx context.Context -// - tx TX -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SendTransaction(ctx interface{}, tx interface{}) *mockRPC_SendTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_SendTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("SendTransaction", ctx, tx)} -} - -func (_c *mockRPC_SendTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, tx TX)) *mockRPC_SendTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(TX)) - }) - return _c -} - -func (_c *mockRPC_SendTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 error) *mockRPC_SendTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockRPC_SendTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, TX) error) *mockRPC_SendTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// SequenceAt provides a mock function with given fields: ctx, accountAddress, blockNumber -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SequenceAt(ctx context.Context, accountAddress ADDR, blockNumber *big.Int) (SEQ, error) { - ret := _m.Called(ctx, accountAddress, blockNumber) - - if len(ret) == 0 { - panic("no return value specified for SequenceAt") - } - - var r0 SEQ - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, ADDR, *big.Int) (SEQ, error)); ok { - return rf(ctx, accountAddress, blockNumber) - } - if rf, ok := ret.Get(0).(func(context.Context, ADDR, *big.Int) SEQ); ok { - r0 = rf(ctx, accountAddress, blockNumber) - } else { - r0 = ret.Get(0).(SEQ) - } - - if rf, ok := ret.Get(1).(func(context.Context, ADDR, *big.Int) error); ok { - r1 = rf(ctx, accountAddress, blockNumber) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_SequenceAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SequenceAt' -type mockRPC_SequenceAt_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// SequenceAt is a helper method to define mock.On call -// - ctx context.Context -// - accountAddress ADDR -// - blockNumber *big.Int -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SequenceAt(ctx interface{}, accountAddress interface{}, blockNumber interface{}) *mockRPC_SequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_SequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("SequenceAt", ctx, accountAddress, blockNumber)} -} - -func (_c *mockRPC_SequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, accountAddress ADDR, blockNumber *big.Int)) *mockRPC_SequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(ADDR), args[2].(*big.Int)) - }) - return _c -} - -func (_c *mockRPC_SequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 SEQ, _a1 error) *mockRPC_SequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_SequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, ADDR, *big.Int) (SEQ, error)) *mockRPC_SequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// SetAliveLoopSub provides a mock function with given fields: _a0 -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SetAliveLoopSub(_a0 types.Subscription) { - _m.Called(_a0) -} - -// mockRPC_SetAliveLoopSub_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetAliveLoopSub' -type mockRPC_SetAliveLoopSub_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// SetAliveLoopSub is a helper method to define mock.On call -// - _a0 types.Subscription -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SetAliveLoopSub(_a0 interface{}) *mockRPC_SetAliveLoopSub_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_SetAliveLoopSub_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("SetAliveLoopSub", _a0)} -} - -func (_c *mockRPC_SetAliveLoopSub_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(_a0 types.Subscription)) *mockRPC_SetAliveLoopSub_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(types.Subscription)) - }) - return _c -} - -func (_c *mockRPC_SetAliveLoopSub_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return() *mockRPC_SetAliveLoopSub_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return() - return _c -} - -func (_c *mockRPC_SetAliveLoopSub_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(types.Subscription)) *mockRPC_SetAliveLoopSub_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// SimulateTransaction provides a mock function with given fields: ctx, tx -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SimulateTransaction(ctx context.Context, tx TX) error { - ret := _m.Called(ctx, tx) - - if len(ret) == 0 { - panic("no return value specified for SimulateTransaction") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, TX) error); ok { - r0 = rf(ctx, tx) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// mockRPC_SimulateTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SimulateTransaction' -type mockRPC_SimulateTransaction_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// SimulateTransaction is a helper method to define mock.On call -// - ctx context.Context -// - tx TX -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SimulateTransaction(ctx interface{}, tx interface{}) *mockRPC_SimulateTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_SimulateTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("SimulateTransaction", ctx, tx)} -} - -func (_c *mockRPC_SimulateTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, tx TX)) *mockRPC_SimulateTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(TX)) - }) - return _c -} - -func (_c *mockRPC_SimulateTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 error) *mockRPC_SimulateTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockRPC_SimulateTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, TX) error) *mockRPC_SimulateTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// SubscribeNewHead provides a mock function with given fields: ctx, channel -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SubscribeNewHead(ctx context.Context, channel chan<- HEAD) (types.Subscription, error) { - ret := _m.Called(ctx, channel) - - if len(ret) == 0 { - panic("no return value specified for SubscribeNewHead") - } - - var r0 types.Subscription - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, chan<- HEAD) (types.Subscription, error)); ok { - return rf(ctx, channel) - } - if rf, ok := ret.Get(0).(func(context.Context, chan<- HEAD) types.Subscription); ok { - r0 = rf(ctx, channel) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(types.Subscription) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, chan<- HEAD) error); ok { - r1 = rf(ctx, channel) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_SubscribeNewHead_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeNewHead' -type mockRPC_SubscribeNewHead_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// SubscribeNewHead is a helper method to define mock.On call -// - ctx context.Context -// - channel chan<- HEAD -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SubscribeNewHead(ctx interface{}, channel interface{}) *mockRPC_SubscribeNewHead_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_SubscribeNewHead_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("SubscribeNewHead", ctx, channel)} -} - -func (_c *mockRPC_SubscribeNewHead_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, channel chan<- HEAD)) *mockRPC_SubscribeNewHead_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(chan<- HEAD)) - }) - return _c -} - -func (_c *mockRPC_SubscribeNewHead_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(s types.Subscription, err error) *mockRPC_SubscribeNewHead_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(s, err) - return _c -} - -func (_c *mockRPC_SubscribeNewHead_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, chan<- HEAD) (types.Subscription, error)) *mockRPC_SubscribeNewHead_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// SubscribeToFinalizedHeads provides a mock function with given fields: _a0 -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SubscribeToFinalizedHeads(_a0 context.Context) (<-chan HEAD, types.Subscription, error) { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for SubscribeToFinalizedHeads") - } - - var r0 <-chan HEAD - var r1 types.Subscription - var r2 error - if rf, ok := ret.Get(0).(func(context.Context) (<-chan HEAD, types.Subscription, error)); ok { - return rf(_a0) - } - if rf, ok := ret.Get(0).(func(context.Context) <-chan HEAD); ok { - r0 = rf(_a0) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan HEAD) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) types.Subscription); ok { - r1 = rf(_a0) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(types.Subscription) - } - } - - if rf, ok := ret.Get(2).(func(context.Context) error); ok { - r2 = rf(_a0) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// mockRPC_SubscribeToFinalizedHeads_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeToFinalizedHeads' -type mockRPC_SubscribeToFinalizedHeads_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// SubscribeToFinalizedHeads is a helper method to define mock.On call -// - _a0 context.Context -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SubscribeToFinalizedHeads(_a0 interface{}) *mockRPC_SubscribeToFinalizedHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_SubscribeToFinalizedHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("SubscribeToFinalizedHeads", _a0)} -} - -func (_c *mockRPC_SubscribeToFinalizedHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(_a0 context.Context)) *mockRPC_SubscribeToFinalizedHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockRPC_SubscribeToFinalizedHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 <-chan HEAD, _a1 types.Subscription, _a2 error) *mockRPC_SubscribeToFinalizedHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1, _a2) - return _c -} - -func (_c *mockRPC_SubscribeToFinalizedHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context) (<-chan HEAD, types.Subscription, error)) *mockRPC_SubscribeToFinalizedHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// SubscribeToHeads provides a mock function with given fields: ctx -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SubscribeToHeads(ctx context.Context) (<-chan HEAD, types.Subscription, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for SubscribeToHeads") - } - - var r0 <-chan HEAD - var r1 types.Subscription - var r2 error - if rf, ok := ret.Get(0).(func(context.Context) (<-chan HEAD, types.Subscription, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) <-chan HEAD); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan HEAD) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) types.Subscription); ok { - r1 = rf(ctx) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(types.Subscription) - } - } - - if rf, ok := ret.Get(2).(func(context.Context) error); ok { - r2 = rf(ctx) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// mockRPC_SubscribeToHeads_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeToHeads' -type mockRPC_SubscribeToHeads_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// SubscribeToHeads is a helper method to define mock.On call -// - ctx context.Context -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SubscribeToHeads(ctx interface{}) *mockRPC_SubscribeToHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_SubscribeToHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("SubscribeToHeads", ctx)} -} - -func (_c *mockRPC_SubscribeToHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context)) *mockRPC_SubscribeToHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockRPC_SubscribeToHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(ch <-chan HEAD, sub types.Subscription, err error) *mockRPC_SubscribeToHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(ch, sub, err) - return _c -} - -func (_c *mockRPC_SubscribeToHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context) (<-chan HEAD, types.Subscription, error)) *mockRPC_SubscribeToHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// SubscribersCount provides a mock function with given fields: -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SubscribersCount() int32 { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for SubscribersCount") - } - - var r0 int32 - if rf, ok := ret.Get(0).(func() int32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int32) - } - - return r0 -} - -// mockRPC_SubscribersCount_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribersCount' -type mockRPC_SubscribersCount_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// SubscribersCount is a helper method to define mock.On call -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SubscribersCount() *mockRPC_SubscribersCount_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_SubscribersCount_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("SubscribersCount")} -} - -func (_c *mockRPC_SubscribersCount_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func()) *mockRPC_SubscribersCount_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockRPC_SubscribersCount_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 int32) *mockRPC_SubscribersCount_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockRPC_SubscribersCount_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func() int32) *mockRPC_SubscribersCount_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// TokenBalance provides a mock function with given fields: ctx, accountAddress, tokenAddress -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) TokenBalance(ctx context.Context, accountAddress ADDR, tokenAddress ADDR) (*big.Int, error) { - ret := _m.Called(ctx, accountAddress, tokenAddress) - - if len(ret) == 0 { - panic("no return value specified for TokenBalance") - } - - var r0 *big.Int - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, ADDR, ADDR) (*big.Int, error)); ok { - return rf(ctx, accountAddress, tokenAddress) - } - if rf, ok := ret.Get(0).(func(context.Context, ADDR, ADDR) *big.Int); ok { - r0 = rf(ctx, accountAddress, tokenAddress) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*big.Int) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, ADDR, ADDR) error); ok { - r1 = rf(ctx, accountAddress, tokenAddress) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_TokenBalance_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TokenBalance' -type mockRPC_TokenBalance_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// TokenBalance is a helper method to define mock.On call -// - ctx context.Context -// - accountAddress ADDR -// - tokenAddress ADDR -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) TokenBalance(ctx interface{}, accountAddress interface{}, tokenAddress interface{}) *mockRPC_TokenBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_TokenBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("TokenBalance", ctx, accountAddress, tokenAddress)} -} - -func (_c *mockRPC_TokenBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, accountAddress ADDR, tokenAddress ADDR)) *mockRPC_TokenBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(ADDR), args[2].(ADDR)) - }) - return _c -} - -func (_c *mockRPC_TokenBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 *big.Int, _a1 error) *mockRPC_TokenBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_TokenBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, ADDR, ADDR) (*big.Int, error)) *mockRPC_TokenBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// TransactionByHash provides a mock function with given fields: ctx, txHash -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) TransactionByHash(ctx context.Context, txHash TX_HASH) (TX, error) { - ret := _m.Called(ctx, txHash) - - if len(ret) == 0 { - panic("no return value specified for TransactionByHash") - } - - var r0 TX - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, TX_HASH) (TX, error)); ok { - return rf(ctx, txHash) - } - if rf, ok := ret.Get(0).(func(context.Context, TX_HASH) TX); ok { - r0 = rf(ctx, txHash) - } else { - r0 = ret.Get(0).(TX) - } - - if rf, ok := ret.Get(1).(func(context.Context, TX_HASH) error); ok { - r1 = rf(ctx, txHash) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_TransactionByHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TransactionByHash' -type mockRPC_TransactionByHash_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// TransactionByHash is a helper method to define mock.On call -// - ctx context.Context -// - txHash TX_HASH -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) TransactionByHash(ctx interface{}, txHash interface{}) *mockRPC_TransactionByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_TransactionByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("TransactionByHash", ctx, txHash)} -} - -func (_c *mockRPC_TransactionByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, txHash TX_HASH)) *mockRPC_TransactionByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(TX_HASH)) - }) - return _c -} - -func (_c *mockRPC_TransactionByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 TX, _a1 error) *mockRPC_TransactionByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_TransactionByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, TX_HASH) (TX, error)) *mockRPC_TransactionByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// TransactionReceipt provides a mock function with given fields: ctx, txHash -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) TransactionReceipt(ctx context.Context, txHash TX_HASH) (TX_RECEIPT, error) { - ret := _m.Called(ctx, txHash) - - if len(ret) == 0 { - panic("no return value specified for TransactionReceipt") - } - - var r0 TX_RECEIPT - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, TX_HASH) (TX_RECEIPT, error)); ok { - return rf(ctx, txHash) - } - if rf, ok := ret.Get(0).(func(context.Context, TX_HASH) TX_RECEIPT); ok { - r0 = rf(ctx, txHash) - } else { - r0 = ret.Get(0).(TX_RECEIPT) - } - - if rf, ok := ret.Get(1).(func(context.Context, TX_HASH) error); ok { - r1 = rf(ctx, txHash) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_TransactionReceipt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TransactionReceipt' -type mockRPC_TransactionReceipt_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// TransactionReceipt is a helper method to define mock.On call -// - ctx context.Context -// - txHash TX_HASH -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) TransactionReceipt(ctx interface{}, txHash interface{}) *mockRPC_TransactionReceipt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_TransactionReceipt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("TransactionReceipt", ctx, txHash)} -} - -func (_c *mockRPC_TransactionReceipt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, txHash TX_HASH)) *mockRPC_TransactionReceipt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(TX_HASH)) - }) - return _c -} - -func (_c *mockRPC_TransactionReceipt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 TX_RECEIPT, _a1 error) *mockRPC_TransactionReceipt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_TransactionReceipt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, TX_HASH) (TX_RECEIPT, error)) *mockRPC_TransactionReceipt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// UnsubscribeAllExceptAliveLoop provides a mock function with given fields: -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) UnsubscribeAllExceptAliveLoop() { - _m.Called() -} - -// mockRPC_UnsubscribeAllExceptAliveLoop_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UnsubscribeAllExceptAliveLoop' -type mockRPC_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// UnsubscribeAllExceptAliveLoop is a helper method to define mock.On call -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) UnsubscribeAllExceptAliveLoop() *mockRPC_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("UnsubscribeAllExceptAliveLoop")} -} - -func (_c *mockRPC_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func()) *mockRPC_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockRPC_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return() *mockRPC_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return() - return _c -} - -func (_c *mockRPC_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func()) *mockRPC_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// newMockRPC creates a new instance of mockRPC. 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 newMockRPC[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}](t interface { - mock.TestingT - Cleanup(func()) -}) *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - mock := &mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/common/client/mock_send_only_client_test.go b/common/client/mock_send_only_client_test.go index a9c9f23b141..fa65a84551b 100644 --- a/common/client/mock_send_only_client_test.go +++ b/common/client/mock_send_only_client_test.go @@ -110,17 +110,17 @@ func (_c *mockSendOnlyClient_Close_Call[CHAIN_ID]) RunAndReturn(run func()) *moc return _c } -// DialHTTP provides a mock function with given fields: -func (_m *mockSendOnlyClient[CHAIN_ID]) DialHTTP() error { - ret := _m.Called() +// Dial provides a mock function with given fields: ctx +func (_m *mockSendOnlyClient[CHAIN_ID]) Dial(ctx context.Context) error { + ret := _m.Called(ctx) if len(ret) == 0 { - panic("no return value specified for DialHTTP") + panic("no return value specified for Dial") } var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) } else { r0 = ret.Error(0) } @@ -128,29 +128,30 @@ func (_m *mockSendOnlyClient[CHAIN_ID]) DialHTTP() error { return r0 } -// mockSendOnlyClient_DialHTTP_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DialHTTP' -type mockSendOnlyClient_DialHTTP_Call[CHAIN_ID types.ID] struct { +// mockSendOnlyClient_Dial_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Dial' +type mockSendOnlyClient_Dial_Call[CHAIN_ID types.ID] struct { *mock.Call } -// DialHTTP is a helper method to define mock.On call -func (_e *mockSendOnlyClient_Expecter[CHAIN_ID]) DialHTTP() *mockSendOnlyClient_DialHTTP_Call[CHAIN_ID] { - return &mockSendOnlyClient_DialHTTP_Call[CHAIN_ID]{Call: _e.mock.On("DialHTTP")} +// Dial is a helper method to define mock.On call +// - ctx context.Context +func (_e *mockSendOnlyClient_Expecter[CHAIN_ID]) Dial(ctx interface{}) *mockSendOnlyClient_Dial_Call[CHAIN_ID] { + return &mockSendOnlyClient_Dial_Call[CHAIN_ID]{Call: _e.mock.On("Dial", ctx)} } -func (_c *mockSendOnlyClient_DialHTTP_Call[CHAIN_ID]) Run(run func()) *mockSendOnlyClient_DialHTTP_Call[CHAIN_ID] { +func (_c *mockSendOnlyClient_Dial_Call[CHAIN_ID]) Run(run func(ctx context.Context)) *mockSendOnlyClient_Dial_Call[CHAIN_ID] { _c.Call.Run(func(args mock.Arguments) { - run() + run(args[0].(context.Context)) }) return _c } -func (_c *mockSendOnlyClient_DialHTTP_Call[CHAIN_ID]) Return(_a0 error) *mockSendOnlyClient_DialHTTP_Call[CHAIN_ID] { +func (_c *mockSendOnlyClient_Dial_Call[CHAIN_ID]) Return(_a0 error) *mockSendOnlyClient_Dial_Call[CHAIN_ID] { _c.Call.Return(_a0) return _c } -func (_c *mockSendOnlyClient_DialHTTP_Call[CHAIN_ID]) RunAndReturn(run func() error) *mockSendOnlyClient_DialHTTP_Call[CHAIN_ID] { +func (_c *mockSendOnlyClient_Dial_Call[CHAIN_ID]) RunAndReturn(run func(context.Context) error) *mockSendOnlyClient_Dial_Call[CHAIN_ID] { _c.Call.Return(run) return _c } diff --git a/common/client/mock_send_only_node_test.go b/common/client/mock_send_only_node_test.go index 53077cd49db..256faea62bc 100644 --- a/common/client/mock_send_only_node_test.go +++ b/common/client/mock_send_only_node_test.go @@ -10,11 +10,11 @@ import ( ) // mockSendOnlyNode is an autogenerated mock type for the SendOnlyNode type -type mockSendOnlyNode[CHAIN_ID types.ID, RPC sendOnlyClient[CHAIN_ID]] struct { +type mockSendOnlyNode[CHAIN_ID types.ID, RPC interface{}] struct { mock.Mock } -type mockSendOnlyNode_Expecter[CHAIN_ID types.ID, RPC sendOnlyClient[CHAIN_ID]] struct { +type mockSendOnlyNode_Expecter[CHAIN_ID types.ID, RPC interface{}] struct { mock *mock.Mock } @@ -41,7 +41,7 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Close() error { } // mockSendOnlyNode_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' -type mockSendOnlyNode_Close_Call[CHAIN_ID types.ID, RPC sendOnlyClient[CHAIN_ID]] struct { +type mockSendOnlyNode_Close_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } @@ -86,7 +86,7 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) ConfiguredChainID() CHAIN_ID { } // mockSendOnlyNode_ConfiguredChainID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ConfiguredChainID' -type mockSendOnlyNode_ConfiguredChainID_Call[CHAIN_ID types.ID, RPC sendOnlyClient[CHAIN_ID]] struct { +type mockSendOnlyNode_ConfiguredChainID_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } @@ -131,7 +131,7 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Name() 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 sendOnlyClient[CHAIN_ID]] struct { +type mockSendOnlyNode_Name_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } @@ -176,7 +176,7 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) RPC() RPC { } // mockSendOnlyNode_RPC_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RPC' -type mockSendOnlyNode_RPC_Call[CHAIN_ID types.ID, RPC sendOnlyClient[CHAIN_ID]] struct { +type mockSendOnlyNode_RPC_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } @@ -221,7 +221,7 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Start(_a0 context.Context) error { } // mockSendOnlyNode_Start_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Start' -type mockSendOnlyNode_Start_Call[CHAIN_ID types.ID, RPC sendOnlyClient[CHAIN_ID]] struct { +type mockSendOnlyNode_Start_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } @@ -267,7 +267,7 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) State() nodeState { } // mockSendOnlyNode_State_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'State' -type mockSendOnlyNode_State_Call[CHAIN_ID types.ID, RPC sendOnlyClient[CHAIN_ID]] struct { +type mockSendOnlyNode_State_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } @@ -312,7 +312,7 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) String() string { } // mockSendOnlyNode_String_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' -type mockSendOnlyNode_String_Call[CHAIN_ID types.ID, RPC sendOnlyClient[CHAIN_ID]] struct { +type mockSendOnlyNode_String_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } @@ -340,7 +340,7 @@ func (_c *mockSendOnlyNode_String_Call[CHAIN_ID, RPC]) RunAndReturn(run func() s // newMockSendOnlyNode creates a new instance of mockSendOnlyNode. 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 newMockSendOnlyNode[CHAIN_ID types.ID, RPC sendOnlyClient[CHAIN_ID]](t interface { +func newMockSendOnlyNode[CHAIN_ID types.ID, RPC interface{}](t interface { mock.TestingT Cleanup(func()) }) *mockSendOnlyNode[CHAIN_ID, RPC] { diff --git a/common/client/multi_node.go b/common/client/multi_node.go index 00ec436d9ab..9594743f6bd 100644 --- a/common/client/multi_node.go +++ b/common/client/multi_node.go @@ -4,20 +4,15 @@ import ( "context" "errors" "fmt" - "math" "math/big" - "slices" "sync" "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" - - feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/common/types" ) @@ -27,11 +22,6 @@ var ( Name: "multi_node_states", Help: "The number of RPC nodes currently in the given state for the given chain", }, []string{"network", "chainId", "state"}) - // PromMultiNodeInvariantViolations reports violation of our assumptions - PromMultiNodeInvariantViolations = promauto.NewCounterVec(prometheus.CounterOpts{ - Name: "multi_node_invariant_violations", - Help: "The number of invariant violations", - }, []string{"network", "chainId", "invariant"}) ErroringNodeError = fmt.Errorf("no live nodes available") ) @@ -39,129 +29,57 @@ var ( // It also handles multiple node RPC connections simultaneously. type MultiNode[ CHAIN_ID types.ID, - SEQ types.Sequence, - ADDR types.Hashable, - BLOCK_HASH types.Hashable, - TX any, - TX_HASH types.Hashable, - EVENT any, - EVENT_OPS any, - TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], - FEE feetypes.Fee, - HEAD types.Head[BLOCK_HASH], - RPC_CLIENT RPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM], - BATCH_ELEM any, -] interface { - clientAPI[ - CHAIN_ID, - SEQ, - ADDR, - BLOCK_HASH, - TX, - TX_HASH, - EVENT, - EVENT_OPS, - TX_RECEIPT, - FEE, - HEAD, - BATCH_ELEM, - ] - Close() error - NodeStates() map[string]string - SelectNodeRPC() (RPC_CLIENT, error) - - BatchCallContextAll(ctx context.Context, b []BATCH_ELEM) error - ConfiguredChainID() CHAIN_ID -} - -type multiNode[ - CHAIN_ID types.ID, - SEQ types.Sequence, - ADDR types.Hashable, - BLOCK_HASH types.Hashable, - TX any, - TX_HASH types.Hashable, - EVENT any, - EVENT_OPS any, - TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], - FEE feetypes.Fee, - HEAD types.Head[BLOCK_HASH], - RPC_CLIENT RPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM], - BATCH_ELEM any, + RPC any, ] struct { services.StateMachine - nodes []Node[CHAIN_ID, HEAD, RPC_CLIENT] - sendonlys []SendOnlyNode[CHAIN_ID, RPC_CLIENT] + primaryNodes []Node[CHAIN_ID, RPC] + sendOnlyNodes []SendOnlyNode[CHAIN_ID, RPC] chainID CHAIN_ID lggr logger.SugaredLogger selectionMode string - noNewHeadsThreshold time.Duration - nodeSelector NodeSelector[CHAIN_ID, HEAD, RPC_CLIENT] + nodeSelector NodeSelector[CHAIN_ID, RPC] leaseDuration time.Duration leaseTicker *time.Ticker chainFamily string reportInterval time.Duration deathDeclarationDelay time.Duration - sendTxSoftTimeout time.Duration // defines max waiting time from first response til responses evaluation activeMu sync.RWMutex - activeNode Node[CHAIN_ID, HEAD, RPC_CLIENT] + activeNode Node[CHAIN_ID, RPC] chStop services.StopChan wg sync.WaitGroup - - classifySendTxError func(tx TX, err error) SendTxReturnCode } func NewMultiNode[ CHAIN_ID types.ID, - SEQ types.Sequence, - ADDR types.Hashable, - BLOCK_HASH types.Hashable, - TX any, - TX_HASH types.Hashable, - EVENT any, - EVENT_OPS any, - TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], - FEE feetypes.Fee, - HEAD types.Head[BLOCK_HASH], - RPC_CLIENT RPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM], - BATCH_ELEM any, + RPC any, ]( lggr logger.Logger, - selectionMode string, - leaseDuration time.Duration, - noNewHeadsThreshold time.Duration, - nodes []Node[CHAIN_ID, HEAD, RPC_CLIENT], - sendonlys []SendOnlyNode[CHAIN_ID, RPC_CLIENT], - chainID CHAIN_ID, - chainFamily string, - classifySendTxError func(tx TX, err error) SendTxReturnCode, - sendTxSoftTimeout time.Duration, + selectionMode string, // type of the "best" RPC selector (e.g HighestHead, RoundRobin, etc.) + leaseDuration time.Duration, // defines interval on which new "best" RPC should be selected + primaryNodes []Node[CHAIN_ID, RPC], + sendOnlyNodes []SendOnlyNode[CHAIN_ID, RPC], + chainID CHAIN_ID, // configured chain ID (used to verify that passed primaryNodes belong to the same chain) + chainFamily string, // name of the chain family - used in the metrics deathDeclarationDelay time.Duration, -) MultiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM] { - nodeSelector := newNodeSelector(selectionMode, nodes) +) *MultiNode[CHAIN_ID, RPC] { + nodeSelector := newNodeSelector(selectionMode, primaryNodes) // Prometheus' default interval is 15s, set this to under 7.5s to avoid // aliasing (see: https://en.wikipedia.org/wiki/Nyquist_frequency) const reportInterval = 6500 * time.Millisecond - if sendTxSoftTimeout == 0 { - sendTxSoftTimeout = QueryTimeout / 2 - } - c := &multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]{ - nodes: nodes, - sendonlys: sendonlys, + c := &MultiNode[CHAIN_ID, RPC]{ + primaryNodes: primaryNodes, + sendOnlyNodes: sendOnlyNodes, chainID: chainID, lggr: logger.Sugared(lggr).Named("MultiNode").With("chainID", chainID.String()), selectionMode: selectionMode, - noNewHeadsThreshold: noNewHeadsThreshold, nodeSelector: nodeSelector, chStop: make(services.StopChan), leaseDuration: leaseDuration, chainFamily: chainFamily, - classifySendTxError: classifySendTxError, reportInterval: reportInterval, deathDeclarationDelay: deathDeclarationDelay, - sendTxSoftTimeout: sendTxSoftTimeout, } c.lggr.Debugf("The MultiNode is configured to use NodeSelectionMode: %s", selectionMode) @@ -169,17 +87,74 @@ func NewMultiNode[ return c } -// Dial starts every node in the pool +func (c *MultiNode[CHAIN_ID, RPC]) ChainID() CHAIN_ID { + return c.chainID +} + +func (c *MultiNode[CHAIN_ID, RPC]) DoAll(ctx context.Context, do func(ctx context.Context, rpc RPC, isSendOnly bool)) error { + var err error + ok := c.IfNotStopped(func() { + ctx, _ = c.chStop.Ctx(ctx) + + callsCompleted := 0 + for _, n := range c.primaryNodes { + select { + case <-ctx.Done(): + err = ctx.Err() + return + default: + if n.State() != nodeStateAlive { + continue + } + do(ctx, n.RPC(), false) + callsCompleted++ + } + } + if callsCompleted == 0 { + err = ErroringNodeError + } + + for _, n := range c.sendOnlyNodes { + select { + case <-ctx.Done(): + err = ctx.Err() + return + default: + if n.State() != nodeStateAlive { + continue + } + do(ctx, n.RPC(), true) + } + } + }) + if !ok { + return errors.New("MultiNode is stopped") + } + return err +} + +func (c *MultiNode[CHAIN_ID, RPC]) NodeStates() map[string]string { + states := map[string]string{} + for _, n := range c.primaryNodes { + states[n.String()] = n.State().String() + } + for _, n := range c.sendOnlyNodes { + states[n.String()] = n.State().String() + } + return states +} + +// Start starts every node in the pool // // Nodes handle their own redialing and runloops, so this function does not // return any error if the nodes aren't available -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) Dial(ctx context.Context) error { +func (c *MultiNode[CHAIN_ID, RPC]) Start(ctx context.Context) error { return c.StartOnce("MultiNode", func() (merr error) { - if len(c.nodes) == 0 { + if len(c.primaryNodes) == 0 { return fmt.Errorf("no available nodes for chain %s", c.chainID.String()) } var ms services.MultiStart - for _, n := range c.nodes { + for _, n := range c.primaryNodes { if n.ConfiguredChainID().String() != c.chainID.String() { return ms.CloseBecause(fmt.Errorf("node %s has configured chain ID %s which does not match multinode configured chain ID of %s", n.String(), n.ConfiguredChainID().String(), c.chainID.String())) } @@ -189,7 +164,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return err } } - for _, s := range c.sendonlys { + for _, s := range c.sendOnlyNodes { if s.ConfiguredChainID().String() != c.chainID.String() { return ms.CloseBecause(fmt.Errorf("sendonly node %s has configured chain ID %s which does not match multinode configured chain ID of %s", s.String(), s.ConfiguredChainID().String(), c.chainID.String())) } @@ -213,18 +188,18 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } // Close tears down the MultiNode and closes all nodes -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) Close() error { +func (c *MultiNode[CHAIN_ID, RPC]) Close() error { return c.StopOnce("MultiNode", func() error { close(c.chStop) c.wg.Wait() - return services.CloseAll(services.MultiCloser(c.nodes), services.MultiCloser(c.sendonlys)) + return services.CloseAll(services.MultiCloser(c.primaryNodes), services.MultiCloser(c.sendOnlyNodes)) }) } -// SelectNodeRPC returns an RPC of an active node. If there are no active nodes it returns an error. +// SelectRPC returns an RPC of an active node. If there are no active nodes it returns an error. // Call this method from your chain-specific client implementation to access any chain-specific rpc calls. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) SelectNodeRPC() (rpc RPC_CLIENT, err error) { +func (c *MultiNode[CHAIN_ID, RPC]) SelectRPC() (rpc RPC, err error) { n, err := c.selectNode() if err != nil { return rpc, err @@ -233,7 +208,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } // selectNode returns the active Node, if it is still nodeStateAlive, otherwise it selects a new one from the NodeSelector. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) selectNode() (node Node[CHAIN_ID, HEAD, RPC_CLIENT], err error) { +func (c *MultiNode[CHAIN_ID, RPC]) selectNode() (node Node[CHAIN_ID, RPC], err error) { c.activeMu.RLock() node = c.activeNode c.activeMu.RUnlock() @@ -267,12 +242,12 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP // LatestChainInfo - returns number of live nodes available in the pool, so we can prevent the last alive node in a pool from being marked as out-of-sync. // Return highest ChainInfo most recently received by the alive nodes. // E.g. If Node A's the most recent block is 10 and highest 15 and for Node B it's - 12 and 14. This method will return 12. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) LatestChainInfo() (int, ChainInfo) { +func (c *MultiNode[CHAIN_ID, RPC]) LatestChainInfo() (int, ChainInfo) { var nLiveNodes int ch := ChainInfo{ TotalDifficulty: big.NewInt(0), } - for _, n := range c.nodes { + for _, n := range c.primaryNodes { if s, nodeChainInfo := n.StateAndLatest(); s == nodeStateAlive { nLiveNodes++ ch.BlockNumber = max(ch.BlockNumber, nodeChainInfo.BlockNumber) @@ -284,11 +259,11 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } // HighestUserObservations - returns highest ChainInfo ever observed by any user of the MultiNode -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) HighestUserObservations() ChainInfo { +func (c *MultiNode[CHAIN_ID, RPC]) HighestUserObservations() ChainInfo { ch := ChainInfo{ TotalDifficulty: big.NewInt(0), } - for _, n := range c.nodes { + for _, n := range c.primaryNodes { nodeChainInfo := n.HighestUserObservations() ch.BlockNumber = max(ch.BlockNumber, nodeChainInfo.BlockNumber) ch.FinalizedBlockNumber = max(ch.FinalizedBlockNumber, nodeChainInfo.FinalizedBlockNumber) @@ -297,12 +272,12 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return ch } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) checkLease() { +func (c *MultiNode[CHAIN_ID, RPC]) checkLease() { bestNode := c.nodeSelector.Select() - for _, n := range c.nodes { + for _, n := range c.primaryNodes { // Terminate client subscriptions. Services are responsible for reconnecting, which will be routed to the new // best node. Only terminate connections with more than 1 subscription to account for the aliveLoop subscription - if n.State() == nodeStateAlive && n != bestNode && n.SubscribersCount() > 1 { + if n.State() == nodeStateAlive && n != bestNode { c.lggr.Infof("Switching to best node from %q to %q", n.String(), bestNode.String()) n.UnsubscribeAllExceptAliveLoop() } @@ -318,7 +293,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) checkLeaseLoop() { +func (c *MultiNode[CHAIN_ID, RPC]) checkLeaseLoop() { defer c.wg.Done() c.leaseTicker = time.NewTicker(c.leaseDuration) defer c.leaseTicker.Stop() @@ -333,11 +308,11 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) runLoop() { +func (c *MultiNode[CHAIN_ID, RPC]) runLoop() { defer c.wg.Done() - nodeStates := make([]nodeWithState, len(c.nodes)) - for i, n := range c.nodes { + nodeStates := make([]nodeWithState, len(c.primaryNodes)) + for i, n := range c.primaryNodes { nodeStates[i] = nodeWithState{ Node: n.String(), State: n.State().String(), @@ -366,11 +341,11 @@ type nodeWithState struct { DeadSince *time.Time } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) report(nodesStateInfo []nodeWithState) { +func (c *MultiNode[CHAIN_ID, RPC]) report(nodesStateInfo []nodeWithState) { start := time.Now() var dead int counts := make(map[nodeState]int) - for i, n := range c.nodes { + for i, n := range c.primaryNodes { state := n.State() counts[state]++ nodesStateInfo[i].State = state.String() @@ -392,7 +367,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP PromMultiNodeRPCNodeStates.WithLabelValues(c.chainFamily, c.chainID.String(), state.String()).Set(float64(count)) } - total := len(c.nodes) + total := len(c.primaryNodes) live := total - dead c.lggr.Tracew(fmt.Sprintf("MultiNode state: %d/%d nodes are alive", live, total), "nodeStates", nodesStateInfo) if total == dead { @@ -403,462 +378,3 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP c.lggr.Errorw(fmt.Sprintf("At least one primary node is dead: %d/%d nodes are alive", live, total), "nodeStates", nodesStateInfo) } } - -// ClientAPI methods -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) BalanceAt(ctx context.Context, account ADDR, blockNumber *big.Int) (*big.Int, error) { - n, err := c.selectNode() - if err != nil { - return nil, err - } - return n.RPC().BalanceAt(ctx, account, blockNumber) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) BatchCallContext(ctx context.Context, b []BATCH_ELEM) error { - n, err := c.selectNode() - if err != nil { - return err - } - return n.RPC().BatchCallContext(ctx, b) -} - -// BatchCallContextAll calls BatchCallContext for every single node including -// sendonlys. -// CAUTION: This should only be used for mass re-transmitting transactions, it -// might have unexpected effects to use it for anything else. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) BatchCallContextAll(ctx context.Context, b []BATCH_ELEM) error { - var wg sync.WaitGroup - defer wg.Wait() - - main, selectionErr := c.selectNode() - var all []SendOnlyNode[CHAIN_ID, RPC_CLIENT] - for _, n := range c.nodes { - all = append(all, n) - } - all = append(all, c.sendonlys...) - for _, n := range all { - if n == main { - // main node is used at the end for the return value - continue - } - - if n.State() != nodeStateAlive { - continue - } - // Parallel call made to all other nodes with ignored return value - wg.Add(1) - go func(n SendOnlyNode[CHAIN_ID, RPC_CLIENT]) { - defer wg.Done() - err := n.RPC().BatchCallContext(ctx, b) - if err != nil { - c.lggr.Debugw("Secondary node BatchCallContext failed", "err", err) - } else { - c.lggr.Trace("Secondary node BatchCallContext success") - } - }(n) - } - - if selectionErr != nil { - return selectionErr - } - return main.RPC().BatchCallContext(ctx, b) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) BlockByHash(ctx context.Context, hash BLOCK_HASH) (h HEAD, err error) { - n, err := c.selectNode() - if err != nil { - return h, err - } - return n.RPC().BlockByHash(ctx, hash) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) BlockByNumber(ctx context.Context, number *big.Int) (h HEAD, err error) { - n, err := c.selectNode() - if err != nil { - return h, err - } - return n.RPC().BlockByNumber(ctx, number) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { - n, err := c.selectNode() - if err != nil { - return err - } - return n.RPC().CallContext(ctx, result, method, args...) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) CallContract( - ctx context.Context, - attempt interface{}, - blockNumber *big.Int, -) (rpcErr []byte, extractErr error) { - n, err := c.selectNode() - if err != nil { - return rpcErr, err - } - return n.RPC().CallContract(ctx, attempt, blockNumber) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) PendingCallContract( - ctx context.Context, - attempt interface{}, -) (rpcErr []byte, extractErr error) { - n, err := c.selectNode() - if err != nil { - return rpcErr, err - } - return n.RPC().PendingCallContract(ctx, attempt) -} - -// ChainID makes a direct RPC call. In most cases it should be better to use the configured chain id instead by -// calling ConfiguredChainID. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) ChainID(ctx context.Context) (id CHAIN_ID, err error) { - n, err := c.selectNode() - if err != nil { - return id, err - } - return n.RPC().ChainID(ctx) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) CodeAt(ctx context.Context, account ADDR, blockNumber *big.Int) (code []byte, err error) { - n, err := c.selectNode() - if err != nil { - return code, err - } - return n.RPC().CodeAt(ctx, account, blockNumber) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) ConfiguredChainID() CHAIN_ID { - return c.chainID -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) EstimateGas(ctx context.Context, call any) (gas uint64, err error) { - n, err := c.selectNode() - if err != nil { - return gas, err - } - return n.RPC().EstimateGas(ctx, call) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) FilterEvents(ctx context.Context, query EVENT_OPS) (e []EVENT, err error) { - n, err := c.selectNode() - if err != nil { - return e, err - } - return n.RPC().FilterEvents(ctx, query) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) LatestBlockHeight(ctx context.Context) (h *big.Int, err error) { - n, err := c.selectNode() - if err != nil { - return h, err - } - return n.RPC().LatestBlockHeight(ctx) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) LINKBalance(ctx context.Context, accountAddress ADDR, linkAddress ADDR) (b *assets.Link, err error) { - n, err := c.selectNode() - if err != nil { - return b, err - } - return n.RPC().LINKBalance(ctx, accountAddress, linkAddress) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) NodeStates() (states map[string]string) { - states = make(map[string]string) - for _, n := range c.nodes { - states[n.Name()] = n.State().String() - } - for _, s := range c.sendonlys { - states[s.Name()] = s.State().String() - } - return -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) PendingSequenceAt(ctx context.Context, addr ADDR) (s SEQ, err error) { - n, err := c.selectNode() - if err != nil { - return s, err - } - return n.RPC().PendingSequenceAt(ctx, addr) -} - -type sendTxErrors map[SendTxReturnCode][]error - -// String - returns string representation of the errors map. Required by logger to properly represent the value -func (errs sendTxErrors) String() string { - return fmt.Sprint(map[SendTxReturnCode][]error(errs)) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) SendEmptyTransaction( - ctx context.Context, - newTxAttempt func(seq SEQ, feeLimit uint32, fee FEE, fromAddress ADDR) (attempt any, err error), - seq SEQ, - gasLimit uint32, - fee FEE, - fromAddress ADDR, -) (txhash string, err error) { - n, err := c.selectNode() - if err != nil { - return txhash, err - } - return n.RPC().SendEmptyTransaction(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) -} - -type sendTxResult struct { - Err error - ResultCode SendTxReturnCode -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) broadcastTxAsync(ctx context.Context, - n SendOnlyNode[CHAIN_ID, RPC_CLIENT], tx TX) sendTxResult { - txErr := n.RPC().SendTransaction(ctx, tx) - c.lggr.Debugw("Node sent transaction", "name", n.String(), "tx", tx, "err", txErr) - resultCode := c.classifySendTxError(tx, txErr) - if !slices.Contains(sendTxSuccessfulCodes, resultCode) { - c.lggr.Warnw("RPC returned error", "name", n.String(), "tx", tx, "err", txErr) - } - - return sendTxResult{Err: txErr, ResultCode: resultCode} -} - -// collectTxResults - refer to SendTransaction comment for implementation details, -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) collectTxResults(ctx context.Context, tx TX, healthyNodesNum int, txResults <-chan sendTxResult) error { - if healthyNodesNum == 0 { - return ErroringNodeError - } - // combine context and stop channel to ensure we stop, when signal received - ctx, cancel := c.chStop.Ctx(ctx) - defer cancel() - requiredResults := int(math.Ceil(float64(healthyNodesNum) * sendTxQuorum)) - errorsByCode := sendTxErrors{} - var softTimeoutChan <-chan time.Time - var resultsCount int -loop: - for { - select { - case <-ctx.Done(): - c.lggr.Debugw("Failed to collect of the results before context was done", "tx", tx, "errorsByCode", errorsByCode) - return ctx.Err() - case result := <-txResults: - errorsByCode[result.ResultCode] = append(errorsByCode[result.ResultCode], result.Err) - resultsCount++ - if slices.Contains(sendTxSuccessfulCodes, result.ResultCode) || resultsCount >= requiredResults { - break loop - } - case <-softTimeoutChan: - c.lggr.Debugw("Send Tx soft timeout expired - returning responses we've collected so far", "tx", tx, "resultsCount", resultsCount, "requiredResults", requiredResults) - break loop - } - - if softTimeoutChan == nil { - tm := time.NewTimer(c.sendTxSoftTimeout) - softTimeoutChan = tm.C - // we are fine with stopping timer at the end of function - //nolint - defer tm.Stop() - } - } - - // ignore critical error as it's reported in reportSendTxAnomalies - result, _ := aggregateTxResults(errorsByCode) - return result -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) reportSendTxAnomalies(tx TX, txResults <-chan sendTxResult) { - defer c.wg.Done() - resultsByCode := sendTxErrors{} - // txResults eventually will be closed - for txResult := range txResults { - resultsByCode[txResult.ResultCode] = append(resultsByCode[txResult.ResultCode], txResult.Err) - } - - _, criticalErr := aggregateTxResults(resultsByCode) - if criticalErr != nil { - c.lggr.Criticalw("observed invariant violation on SendTransaction", "tx", tx, "resultsByCode", resultsByCode, "err", criticalErr) - c.SvcErrBuffer.Append(criticalErr) - PromMultiNodeInvariantViolations.WithLabelValues(c.chainFamily, c.chainID.String(), criticalErr.Error()).Inc() - } -} - -func aggregateTxResults(resultsByCode sendTxErrors) (txResult error, err error) { - severeErrors, hasSevereErrors := findFirstIn(resultsByCode, sendTxSevereErrors) - successResults, hasSuccess := findFirstIn(resultsByCode, sendTxSuccessfulCodes) - if hasSuccess { - // We assume that primary node would never report false positive txResult for a transaction. - // Thus, if such case occurs it's probably due to misconfiguration or a bug and requires manual intervention. - if hasSevereErrors { - // return success, since at least 1 node has accepted our broadcasted Tx, and thus it can now be included onchain - return successResults[0], errors.New("found contradictions in nodes replies on SendTransaction: got success and severe error") - } - - // other errors are temporary - we are safe to return success - return successResults[0], nil - } - - if hasSevereErrors { - return severeErrors[0], nil - } - - // return temporary error - for _, result := range resultsByCode { - return result[0], nil - } - - err = fmt.Errorf("expected at least one response on SendTransaction") - return err, err -} - -const sendTxQuorum = 0.7 - -// SendTransaction - broadcasts transaction to all the send-only and primary nodes regardless of their health. -// A returned nil or error does not guarantee that the transaction will or won't be included. Additional checks must be -// performed to determine the final state. -// -// Send-only nodes' results are ignored as they tend to return false-positive responses. Broadcast to them is necessary -// to speed up the propagation of TX in the network. -// -// Handling of primary nodes' results consists of collection and aggregation. -// In the collection step, we gather as many results as possible while minimizing waiting time. This operation succeeds -// on one of the following conditions: -// * Received at least one success -// * Received at least one result and `sendTxSoftTimeout` expired -// * Received results from the sufficient number of nodes defined by sendTxQuorum. -// The aggregation is based on the following conditions: -// * If there is at least one success - returns success -// * If there is at least one terminal error - returns terminal error -// * If there is both success and terminal error - returns success and reports invariant violation -// * Otherwise, returns any (effectively random) of the errors. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) SendTransaction(ctx context.Context, tx TX) error { - if len(c.nodes) == 0 { - return ErroringNodeError - } - - healthyNodesNum := 0 - txResults := make(chan sendTxResult, len(c.nodes)) - // Must wrap inside IfNotStopped to avoid waitgroup racing with Close - ok := c.IfNotStopped(func() { - // fire-n-forget, as sendOnlyNodes can not be trusted with result reporting - for _, n := range c.sendonlys { - if n.State() != nodeStateAlive { - continue - } - c.wg.Add(1) - go func(n SendOnlyNode[CHAIN_ID, RPC_CLIENT]) { - defer c.wg.Done() - c.broadcastTxAsync(ctx, n, tx) - }(n) - } - - var primaryBroadcastWg sync.WaitGroup - txResultsToReport := make(chan sendTxResult, len(c.nodes)) - for _, n := range c.nodes { - if n.State() != nodeStateAlive { - continue - } - - healthyNodesNum++ - primaryBroadcastWg.Add(1) - go func(n SendOnlyNode[CHAIN_ID, RPC_CLIENT]) { - defer primaryBroadcastWg.Done() - result := c.broadcastTxAsync(ctx, n, tx) - // both channels are sufficiently buffered, so we won't be locked - txResultsToReport <- result - txResults <- result - }(n) - } - - c.wg.Add(1) - go func() { - // wait for primary nodes to finish the broadcast before closing the channel - primaryBroadcastWg.Wait() - close(txResultsToReport) - close(txResults) - c.wg.Done() - }() - - c.wg.Add(1) - go c.reportSendTxAnomalies(tx, txResultsToReport) - }) - if !ok { - return fmt.Errorf("aborted while broadcasting tx - multiNode is stopped: %w", context.Canceled) - } - - return c.collectTxResults(ctx, tx, healthyNodesNum, txResults) -} - -// findFirstIn - returns first existing value for the slice of keys -func findFirstIn[K comparable, V any](set map[K]V, keys []K) (V, bool) { - for _, k := range keys { - if v, ok := set[k]; ok { - return v, true - } - } - var v V - return v, false -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) SequenceAt(ctx context.Context, account ADDR, blockNumber *big.Int) (s SEQ, err error) { - n, err := c.selectNode() - if err != nil { - return s, err - } - return n.RPC().SequenceAt(ctx, account, blockNumber) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) SimulateTransaction(ctx context.Context, tx TX) error { - n, err := c.selectNode() - if err != nil { - return err - } - return n.RPC().SimulateTransaction(ctx, tx) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) SubscribeNewHead(ctx context.Context, channel chan<- HEAD) (s types.Subscription, err error) { - n, err := c.selectNode() - if err != nil { - return s, err - } - return n.RPC().SubscribeNewHead(ctx, channel) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) SubscribeToHeads(ctx context.Context) (ch <-chan HEAD, sub types.Subscription, err error) { - n, err := c.selectNode() - if err != nil { - return nil, nil, err - } - return n.RPC().SubscribeToHeads(ctx) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) TokenBalance(ctx context.Context, account ADDR, tokenAddr ADDR) (b *big.Int, err error) { - n, err := c.selectNode() - if err != nil { - return b, err - } - return n.RPC().TokenBalance(ctx, account, tokenAddr) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) TransactionByHash(ctx context.Context, txHash TX_HASH) (tx TX, err error) { - n, err := c.selectNode() - if err != nil { - return tx, err - } - return n.RPC().TransactionByHash(ctx, txHash) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) TransactionReceipt(ctx context.Context, txHash TX_HASH) (txr TX_RECEIPT, err error) { - n, err := c.selectNode() - if err != nil { - return txr, err - } - return n.RPC().TransactionReceipt(ctx, txHash) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) LatestFinalizedBlock(ctx context.Context) (head HEAD, err error) { - n, err := c.selectNode() - if err != nil { - return head, err - } - - return n.RPC().LatestFinalizedBlock(ctx) -} diff --git a/common/client/multi_node_test.go b/common/client/multi_node_test.go index ffef0c29d56..57b849a3c0a 100644 --- a/common/client/multi_node_test.go +++ b/common/client/multi_node_test.go @@ -1,44 +1,38 @@ package client import ( - "context" - "errors" "fmt" "math/big" "math/rand" "testing" "time" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "go.uber.org/zap" - "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/common/types" ) -type multiNodeRPCClient RPC[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, - types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], any] +type multiNodeRPCClient RPCClient[types.ID, types.Head[Hashable]] type testMultiNode struct { - *multiNode[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, - types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient, any] + *MultiNode[types.ID, multiNodeRPCClient] } type multiNodeOpts struct { logger logger.Logger selectionMode string leaseDuration time.Duration - noNewHeadsThreshold time.Duration - nodes []Node[types.ID, types.Head[Hashable], multiNodeRPCClient] + nodes []Node[types.ID, multiNodeRPCClient] sendonlys []SendOnlyNode[types.ID, multiNodeRPCClient] chainID types.ID chainFamily string - classifySendTxError func(tx any, err error) SendTxReturnCode - sendTxSoftTimeout time.Duration deathDeclarationDelay time.Duration } @@ -47,46 +41,32 @@ func newTestMultiNode(t *testing.T, opts multiNodeOpts) testMultiNode { opts.logger = logger.Test(t) } - result := NewMultiNode[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, - types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient, any](opts.logger, - opts.selectionMode, opts.leaseDuration, opts.noNewHeadsThreshold, opts.nodes, opts.sendonlys, - opts.chainID, opts.chainFamily, opts.classifySendTxError, opts.sendTxSoftTimeout, opts.deathDeclarationDelay) + result := NewMultiNode[types.ID, multiNodeRPCClient]( + opts.logger, opts.selectionMode, opts.leaseDuration, opts.nodes, opts.sendonlys, opts.chainID, opts.chainFamily, opts.deathDeclarationDelay) return testMultiNode{ - result.(*multiNode[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, - types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient, any]), + result, } } -func newMultiNodeRPCClient(t *testing.T) *mockRPC[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, - types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], any] { - return newMockRPC[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, - types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], any](t) -} - -func newHealthyNode(t *testing.T, chainID types.ID) *mockNode[types.ID, types.Head[Hashable], multiNodeRPCClient] { +func newHealthyNode(t *testing.T, chainID types.ID) *mockNode[types.ID, multiNodeRPCClient] { return newNodeWithState(t, chainID, nodeStateAlive) } -func newNodeWithState(t *testing.T, chainID types.ID, state nodeState) *mockNode[types.ID, types.Head[Hashable], multiNodeRPCClient] { - node := newDialableNode(t, chainID) - node.On("State").Return(state).Maybe() - return node -} - -func newDialableNode(t *testing.T, chainID types.ID) *mockNode[types.ID, types.Head[Hashable], multiNodeRPCClient] { - node := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) +func newNodeWithState(t *testing.T, chainID types.ID, state nodeState) *mockNode[types.ID, multiNodeRPCClient] { + node := newMockNode[types.ID, multiNodeRPCClient](t) node.On("ConfiguredChainID").Return(chainID).Once() node.On("Start", mock.Anything).Return(nil).Once() node.On("Close").Return(nil).Once() node.On("String").Return(fmt.Sprintf("healthy_node_%d", rand.Int())).Maybe() node.On("SetPoolChainInfoProvider", mock.Anything).Once() + node.On("State").Return(state).Maybe() return node } func TestMultiNode_Dial(t *testing.T) { t.Parallel() - newMockNode := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient] + newMockNode := newMockNode[types.ID, multiNodeRPCClient] newMockSendOnlyNode := newMockSendOnlyNode[types.ID, multiNodeRPCClient] t.Run("Fails without nodes", func(t *testing.T) { @@ -95,7 +75,7 @@ func TestMultiNode_Dial(t *testing.T) { selectionMode: NodeSelectionModeRoundRobin, chainID: types.RandomID(), }) - err := mn.Dial(tests.Context(t)) + err := mn.Start(tests.Context(t)) assert.EqualError(t, err, fmt.Sprintf("no available nodes for chain %s", mn.chainID.String())) }) t.Run("Fails with wrong node's chainID", func(t *testing.T) { @@ -109,9 +89,9 @@ func TestMultiNode_Dial(t *testing.T) { mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: multiNodeChainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node}, + nodes: []Node[types.ID, multiNodeRPCClient]{node}, }) - err := mn.Dial(tests.Context(t)) + err := mn.Start(tests.Context(t)) assert.EqualError(t, err, fmt.Sprintf("node %s has configured chain ID %s which does not match multinode configured chain ID of %s", nodeName, nodeChainID, mn.chainID)) }) t.Run("Fails if node fails", func(t *testing.T) { @@ -119,15 +99,15 @@ func TestMultiNode_Dial(t *testing.T) { node := newMockNode(t) chainID := types.RandomID() node.On("ConfiguredChainID").Return(chainID).Once() - node.On("SetPoolChainInfoProvider", mock.Anything).Once() expectedError := errors.New("failed to start node") node.On("Start", mock.Anything).Return(expectedError).Once() + node.On("SetPoolChainInfoProvider", mock.Anything).Once() mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node}, + nodes: []Node[types.ID, multiNodeRPCClient]{node}, }) - err := mn.Dial(tests.Context(t)) + err := mn.Start(tests.Context(t)) assert.EqualError(t, err, expectedError.Error()) }) @@ -137,16 +117,16 @@ func TestMultiNode_Dial(t *testing.T) { node1 := newHealthyNode(t, chainID) node2 := newMockNode(t) node2.On("ConfiguredChainID").Return(chainID).Once() - node2.On("SetPoolChainInfoProvider", mock.Anything).Once() expectedError := errors.New("failed to start node") node2.On("Start", mock.Anything).Return(expectedError).Once() + node2.On("SetPoolChainInfoProvider", mock.Anything).Once() mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node1, node2}, + nodes: []Node[types.ID, multiNodeRPCClient]{node1, node2}, }) - err := mn.Dial(tests.Context(t)) + err := mn.Start(tests.Context(t)) assert.EqualError(t, err, expectedError.Error()) }) t.Run("Fails with wrong send only node's chainID", func(t *testing.T) { @@ -162,10 +142,10 @@ func TestMultiNode_Dial(t *testing.T) { mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: multiNodeChainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node}, + nodes: []Node[types.ID, multiNodeRPCClient]{node}, sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{sendOnly}, }) - err := mn.Dial(tests.Context(t)) + err := mn.Start(tests.Context(t)) assert.EqualError(t, err, fmt.Sprintf("sendonly node %s has configured chain ID %s which does not match multinode configured chain ID of %s", sendOnlyName, sendOnlyChainID, mn.chainID)) }) @@ -189,10 +169,10 @@ func TestMultiNode_Dial(t *testing.T) { mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node}, + nodes: []Node[types.ID, multiNodeRPCClient]{node}, sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{sendOnly1, sendOnly2}, }) - err := mn.Dial(tests.Context(t)) + err := mn.Start(tests.Context(t)) assert.EqualError(t, err, expectedError.Error()) }) t.Run("Starts successfully with healthy nodes", func(t *testing.T) { @@ -202,11 +182,11 @@ func TestMultiNode_Dial(t *testing.T) { mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node}, + nodes: []Node[types.ID, multiNodeRPCClient]{node}, sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{newHealthySendOnly(t, chainID)}, }) defer func() { assert.NoError(t, mn.Close()) }() - err := mn.Dial(tests.Context(t)) + err := mn.Start(tests.Context(t)) require.NoError(t, err) selectedNode, err := mn.selectNode() require.NoError(t, err) @@ -225,13 +205,13 @@ func TestMultiNode_Report(t *testing.T) { mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node1, node2}, + nodes: []Node[types.ID, multiNodeRPCClient]{node1, node2}, logger: lggr, }) mn.reportInterval = tests.TestInterval mn.deathDeclarationDelay = tests.TestInterval defer func() { assert.NoError(t, mn.Close()) }() - err := mn.Dial(tests.Context(t)) + err := mn.Start(tests.Context(t)) require.NoError(t, err) tests.AssertLogCountEventually(t, observedLogs, "At least one primary node is dead: 1/2 nodes are alive", 2) }) @@ -243,13 +223,13 @@ func TestMultiNode_Report(t *testing.T) { mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node}, + nodes: []Node[types.ID, multiNodeRPCClient]{node}, logger: lggr, }) mn.reportInterval = tests.TestInterval mn.deathDeclarationDelay = tests.TestInterval defer func() { assert.NoError(t, mn.Close()) }() - err := mn.Dial(tests.Context(t)) + err := mn.Start(tests.Context(t)) require.NoError(t, err) tests.AssertLogCountEventually(t, observedLogs, "no primary nodes available: 0/1 nodes are alive", 2) err = mn.Healthy() @@ -269,10 +249,10 @@ func TestMultiNode_CheckLease(t *testing.T) { selectionMode: NodeSelectionModeRoundRobin, chainID: chainID, logger: lggr, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node}, + nodes: []Node[types.ID, multiNodeRPCClient]{node}, }) defer func() { assert.NoError(t, mn.Close()) }() - err := mn.Dial(tests.Context(t)) + err := mn.Start(tests.Context(t)) require.NoError(t, err) tests.RequireLogMessage(t, observedLogs, "Best node switching is disabled") }) @@ -285,11 +265,11 @@ func TestMultiNode_CheckLease(t *testing.T) { selectionMode: NodeSelectionModeHighestHead, chainID: chainID, logger: lggr, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node}, + nodes: []Node[types.ID, multiNodeRPCClient]{node}, leaseDuration: 0, }) defer func() { assert.NoError(t, mn.Close()) }() - err := mn.Dial(tests.Context(t)) + err := mn.Start(tests.Context(t)) require.NoError(t, err) tests.RequireLogMessage(t, observedLogs, "Best node switching is disabled") }) @@ -297,22 +277,21 @@ func TestMultiNode_CheckLease(t *testing.T) { t.Parallel() chainID := types.RandomID() node := newHealthyNode(t, chainID) - node.On("SubscribersCount").Return(int32(2)) node.On("UnsubscribeAllExceptAliveLoop") bestNode := newHealthyNode(t, chainID) - nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + nodeSelector := newMockNodeSelector[types.ID, multiNodeRPCClient](t) nodeSelector.On("Select").Return(bestNode) lggr, observedLogs := logger.TestObserved(t, zap.InfoLevel) mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeHighestHead, chainID: chainID, logger: lggr, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node, bestNode}, + nodes: []Node[types.ID, multiNodeRPCClient]{node, bestNode}, leaseDuration: tests.TestInterval, }) defer func() { assert.NoError(t, mn.Close()) }() mn.nodeSelector = nodeSelector - err := mn.Dial(tests.Context(t)) + err := mn.Start(tests.Context(t)) require.NoError(t, err) tests.AssertLogEventually(t, observedLogs, fmt.Sprintf("Switching to best node from %q to %q", node.String(), bestNode.String())) tests.AssertEventually(t, func() bool { @@ -338,15 +317,15 @@ func TestMultiNode_CheckLease(t *testing.T) { expectedResult := map[string]string{} for name, state := range nodes { - node := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - node.On("Name").Return(name).Once() + node := newMockNode[types.ID, multiNodeRPCClient](t) node.On("State").Return(state).Once() + node.On("String").Return(name).Once() opts.nodes = append(opts.nodes, node) sendOnly := newMockSendOnlyNode[types.ID, multiNodeRPCClient](t) sendOnlyName := "send_only_" + name - sendOnly.On("Name").Return(sendOnlyName).Once() sendOnly.On("State").Return(state).Once() + sendOnly.On("String").Return(sendOnlyName).Once() opts.sendonlys = append(opts.sendonlys, sendOnly) expectedResult[name] = state.String() @@ -364,17 +343,17 @@ func TestMultiNode_selectNode(t *testing.T) { t.Run("Returns same node, if it's still healthy", func(t *testing.T) { t.Parallel() chainID := types.RandomID() - node1 := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + node1 := newMockNode[types.ID, multiNodeRPCClient](t) node1.On("State").Return(nodeStateAlive).Once() node1.On("String").Return("node1").Maybe() - node2 := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + node2 := newMockNode[types.ID, multiNodeRPCClient](t) node2.On("String").Return("node2").Maybe() mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node1, node2}, + nodes: []Node[types.ID, multiNodeRPCClient]{node1, node2}, }) - nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + nodeSelector := newMockNodeSelector[types.ID, multiNodeRPCClient](t) nodeSelector.On("Select").Return(node1).Once() mn.nodeSelector = nodeSelector prevActiveNode, err := mn.selectNode() @@ -387,17 +366,17 @@ func TestMultiNode_selectNode(t *testing.T) { t.Run("Updates node if active is not healthy", func(t *testing.T) { t.Parallel() chainID := types.RandomID() - oldBest := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + oldBest := newMockNode[types.ID, multiNodeRPCClient](t) oldBest.On("String").Return("oldBest").Maybe() - oldBest.On("UnsubscribeAllExceptAliveLoop").Once() - newBest := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + oldBest.On("UnsubscribeAllExceptAliveLoop") + newBest := newMockNode[types.ID, multiNodeRPCClient](t) newBest.On("String").Return("newBest").Maybe() mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{oldBest, newBest}, + nodes: []Node[types.ID, multiNodeRPCClient]{oldBest, newBest}, }) - nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + nodeSelector := newMockNodeSelector[types.ID, multiNodeRPCClient](t) nodeSelector.On("Select").Return(oldBest).Once() mn.nodeSelector = nodeSelector activeNode, err := mn.selectNode() @@ -419,7 +398,7 @@ func TestMultiNode_selectNode(t *testing.T) { chainID: chainID, logger: lggr, }) - nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + nodeSelector := newMockNodeSelector[types.ID, multiNodeRPCClient](t) nodeSelector.On("Select").Return(nil).Once() nodeSelector.On("Name").Return("MockedNodeSelector").Once() mn.nodeSelector = nodeSelector @@ -532,10 +511,10 @@ func TestMultiNode_ChainInfo(t *testing.T) { tc := testCases[i] t.Run(tc.Name, func(t *testing.T) { for _, params := range tc.NodeParams { - node := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + node := newMockNode[types.ID, multiNodeRPCClient](t) + mn.primaryNodes = append(mn.primaryNodes, node) node.On("StateAndLatest").Return(params.State, params.LatestChainInfo) node.On("HighestUserObservations").Return(params.HighestUserObservations) - mn.nodes = append(mn.nodes, node) } nNodes, latestChainInfo := mn.LatestChainInfo() @@ -547,400 +526,3 @@ func TestMultiNode_ChainInfo(t *testing.T) { }) } } - -func TestMultiNode_BatchCallContextAll(t *testing.T) { - t.Parallel() - t.Run("Fails if failed to select active node", func(t *testing.T) { - chainID := types.RandomID() - mn := newTestMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - }) - nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - nodeSelector.On("Select").Return(nil).Once() - nodeSelector.On("Name").Return("MockedNodeSelector").Once() - mn.nodeSelector = nodeSelector - err := mn.BatchCallContextAll(tests.Context(t), nil) - require.EqualError(t, err, ErroringNodeError.Error()) - }) - t.Run("Returns error if RPC call fails for active node", func(t *testing.T) { - chainID := types.RandomID() - rpc := newMultiNodeRPCClient(t) - expectedError := errors.New("rpc failed to do the batch call") - rpc.On("BatchCallContext", mock.Anything, mock.Anything).Return(expectedError).Once() - node := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - node.On("RPC").Return(rpc) - nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - nodeSelector.On("Select").Return(node).Once() - mn := newTestMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - }) - mn.nodeSelector = nodeSelector - err := mn.BatchCallContextAll(tests.Context(t), nil) - require.EqualError(t, err, expectedError.Error()) - }) - t.Run("Waits for all nodes to complete the call and logs results", func(t *testing.T) { - // setup RPCs - failedRPC := newMultiNodeRPCClient(t) - failedRPC.On("BatchCallContext", mock.Anything, mock.Anything). - Return(errors.New("rpc failed to do the batch call")).Once() - okRPC := newMultiNodeRPCClient(t) - okRPC.On("BatchCallContext", mock.Anything, mock.Anything).Return(nil).Twice() - - // setup ok and failed auxiliary nodes - okNode := newMockSendOnlyNode[types.ID, multiNodeRPCClient](t) - okNode.On("RPC").Return(okRPC).Once() - okNode.On("State").Return(nodeStateAlive) - failedNode := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - failedNode.On("RPC").Return(failedRPC).Once() - failedNode.On("State").Return(nodeStateAlive) - - // setup main node - mainNode := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - mainNode.On("RPC").Return(okRPC) - nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - nodeSelector.On("Select").Return(mainNode).Once() - lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - mn := newTestMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: types.RandomID(), - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{failedNode, mainNode}, - sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{okNode}, - logger: lggr, - }) - mn.nodeSelector = nodeSelector - - err := mn.BatchCallContextAll(tests.Context(t), nil) - require.NoError(t, err) - tests.RequireLogMessage(t, observedLogs, "Secondary node BatchCallContext failed") - }) - t.Run("Does not call BatchCallContext for unhealthy nodes", func(t *testing.T) { - // setup RPCs - okRPC := newMultiNodeRPCClient(t) - okRPC.On("BatchCallContext", mock.Anything, mock.Anything).Return(nil).Twice() - - // setup ok and failed auxiliary nodes - healthyNode := newMockSendOnlyNode[types.ID, multiNodeRPCClient](t) - healthyNode.On("RPC").Return(okRPC).Once() - healthyNode.On("State").Return(nodeStateAlive) - deadNode := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - deadNode.On("State").Return(nodeStateUnreachable) - - // setup main node - mainNode := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - mainNode.On("RPC").Return(okRPC) - nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - nodeSelector.On("Select").Return(mainNode).Once() - mn := newTestMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: types.RandomID(), - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{deadNode, mainNode}, - sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{healthyNode, deadNode}, - }) - mn.nodeSelector = nodeSelector - - err := mn.BatchCallContextAll(tests.Context(t), nil) - require.NoError(t, err) - }) -} - -func TestMultiNode_SendTransaction(t *testing.T) { - t.Parallel() - classifySendTxError := func(tx any, err error) SendTxReturnCode { - if err != nil { - return Fatal - } - - return Successful - } - newNodeWithState := func(t *testing.T, state nodeState, txErr error, sendTxRun func(args mock.Arguments)) *mockNode[types.ID, types.Head[Hashable], multiNodeRPCClient] { - rpc := newMultiNodeRPCClient(t) - rpc.On("SendTransaction", mock.Anything, mock.Anything).Return(txErr).Run(sendTxRun).Maybe() - node := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - node.On("String").Return("node name").Maybe() - node.On("RPC").Return(rpc).Maybe() - node.On("State").Return(state).Maybe() - node.On("Close").Return(nil).Once() - return node - } - - newNode := func(t *testing.T, txErr error, sendTxRun func(args mock.Arguments)) *mockNode[types.ID, types.Head[Hashable], multiNodeRPCClient] { - return newNodeWithState(t, nodeStateAlive, txErr, sendTxRun) - } - newStartedMultiNode := func(t *testing.T, opts multiNodeOpts) testMultiNode { - mn := newTestMultiNode(t, opts) - err := mn.StartOnce("startedTestMultiNode", func() error { return nil }) - require.NoError(t, err) - t.Cleanup(func() { - require.NoError(t, mn.Close()) - }) - return mn - } - t.Run("Fails if there is no nodes available", func(t *testing.T) { - mn := newStartedMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: types.RandomID(), - }) - err := mn.SendTransaction(tests.Context(t), nil) - assert.EqualError(t, err, ErroringNodeError.Error()) - }) - t.Run("Transaction failure happy path", func(t *testing.T) { - chainID := types.RandomID() - expectedError := errors.New("transaction failed") - mainNode := newNode(t, expectedError, nil) - lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - mn := newStartedMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{mainNode}, - sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{newNode(t, errors.New("unexpected error"), nil)}, - classifySendTxError: classifySendTxError, - logger: lggr, - }) - err := mn.SendTransaction(tests.Context(t), nil) - require.EqualError(t, err, expectedError.Error()) - tests.AssertLogCountEventually(t, observedLogs, "Node sent transaction", 2) - tests.AssertLogCountEventually(t, observedLogs, "RPC returned error", 2) - }) - t.Run("Transaction success happy path", func(t *testing.T) { - chainID := types.RandomID() - mainNode := newNode(t, nil, nil) - lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - mn := newStartedMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{mainNode}, - sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{newNode(t, errors.New("unexpected error"), nil)}, - classifySendTxError: classifySendTxError, - logger: lggr, - }) - err := mn.SendTransaction(tests.Context(t), nil) - require.NoError(t, err) - tests.AssertLogCountEventually(t, observedLogs, "Node sent transaction", 2) - tests.AssertLogCountEventually(t, observedLogs, "RPC returned error", 1) - }) - t.Run("Context expired before collecting sufficient results", func(t *testing.T) { - chainID := types.RandomID() - testContext, testCancel := context.WithCancel(tests.Context(t)) - defer testCancel() - mainNode := newNode(t, errors.New("unexpected error"), func(_ mock.Arguments) { - // block caller til end of the test - <-testContext.Done() - }) - mn := newStartedMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{mainNode}, - classifySendTxError: classifySendTxError, - }) - requestContext, cancel := context.WithCancel(tests.Context(t)) - cancel() - err := mn.SendTransaction(requestContext, nil) - require.EqualError(t, err, "context canceled") - }) - t.Run("Soft timeout stops results collection", func(t *testing.T) { - chainID := types.RandomID() - expectedError := errors.New("tmp error") - fastNode := newNode(t, expectedError, nil) - // hold reply from the node till end of the test - testContext, testCancel := context.WithCancel(tests.Context(t)) - defer testCancel() - slowNode := newNode(t, errors.New("transaction failed"), func(_ mock.Arguments) { - // block caller til end of the test - <-testContext.Done() - }) - mn := newStartedMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{fastNode, slowNode}, - classifySendTxError: classifySendTxError, - sendTxSoftTimeout: tests.TestInterval, - }) - err := mn.SendTransaction(tests.Context(t), nil) - require.EqualError(t, err, expectedError.Error()) - }) - t.Run("Returns success without waiting for the rest of the nodes", func(t *testing.T) { - chainID := types.RandomID() - fastNode := newNode(t, nil, nil) - // hold reply from the node till end of the test - testContext, testCancel := context.WithCancel(tests.Context(t)) - defer testCancel() - slowNode := newNode(t, errors.New("transaction failed"), func(_ mock.Arguments) { - // block caller til end of the test - <-testContext.Done() - }) - slowSendOnly := newNode(t, errors.New("send only failed"), func(_ mock.Arguments) { - // block caller til end of the test - <-testContext.Done() - }) - lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) - mn := newTestMultiNode(t, multiNodeOpts{ - logger: lggr, - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{fastNode, slowNode}, - sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{slowSendOnly}, - classifySendTxError: classifySendTxError, - sendTxSoftTimeout: tests.TestInterval, - }) - assert.NoError(t, mn.StartOnce("startedTestMultiNode", func() error { return nil })) - err := mn.SendTransaction(tests.Context(t), nil) - require.NoError(t, err) - testCancel() - require.NoError(t, mn.Close()) - tests.AssertLogEventually(t, observedLogs, "observed invariant violation on SendTransaction") - }) - t.Run("Fails when closed", func(t *testing.T) { - mn := newTestMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: types.RandomID(), - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{newNode(t, nil, nil)}, - sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{newNode(t, nil, nil)}, - classifySendTxError: classifySendTxError, - }) - err := mn.StartOnce("startedTestMultiNode", func() error { return nil }) - require.NoError(t, err) - require.NoError(t, mn.Close()) - err = mn.SendTransaction(tests.Context(t), nil) - require.EqualError(t, err, "aborted while broadcasting tx - multiNode is stopped: context canceled") - }) - t.Run("Returns error if there is no healthy primary nodes", func(t *testing.T) { - mn := newStartedMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: types.RandomID(), - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{newNodeWithState(t, nodeStateUnreachable, nil, nil)}, - sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{newNodeWithState(t, nodeStateUnreachable, nil, nil)}, - classifySendTxError: classifySendTxError, - }) - err := mn.SendTransaction(tests.Context(t), nil) - assert.EqualError(t, err, ErroringNodeError.Error()) - }) - t.Run("Transaction success even if one of the nodes is unhealthy", func(t *testing.T) { - chainID := types.RandomID() - mainNode := newNode(t, nil, nil) - unexpectedCall := func(args mock.Arguments) { - panic("SendTx must not be called for unhealthy node") - } - unhealthyNode := newNodeWithState(t, nodeStateUnreachable, nil, unexpectedCall) - unhealthySendOnlyNode := newNodeWithState(t, nodeStateUnreachable, nil, unexpectedCall) - lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - mn := newStartedMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{mainNode, unhealthyNode}, - sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{unhealthySendOnlyNode, newNode(t, errors.New("unexpected error"), nil)}, - classifySendTxError: classifySendTxError, - logger: lggr, - }) - err := mn.SendTransaction(tests.Context(t), nil) - require.NoError(t, err) - tests.AssertLogCountEventually(t, observedLogs, "Node sent transaction", 2) - tests.AssertLogCountEventually(t, observedLogs, "RPC returned error", 1) - }) -} - -func TestMultiNode_SendTransaction_aggregateTxResults(t *testing.T) { - t.Parallel() - // ensure failure on new SendTxReturnCode - codesToCover := map[SendTxReturnCode]struct{}{} - for code := Successful; code < sendTxReturnCodeLen; code++ { - codesToCover[code] = struct{}{} - } - - testCases := []struct { - Name string - ExpectedTxResult string - ExpectedCriticalErr string - ResultsByCode sendTxErrors - }{ - { - Name: "Returns success and logs critical error on success and Fatal", - ExpectedTxResult: "success", - ExpectedCriticalErr: "found contradictions in nodes replies on SendTransaction: got success and severe error", - ResultsByCode: sendTxErrors{ - Successful: {errors.New("success")}, - Fatal: {errors.New("fatal")}, - }, - }, - { - Name: "Returns TransactionAlreadyKnown and logs critical error on TransactionAlreadyKnown and Fatal", - ExpectedTxResult: "tx_already_known", - ExpectedCriticalErr: "found contradictions in nodes replies on SendTransaction: got success and severe error", - ResultsByCode: sendTxErrors{ - TransactionAlreadyKnown: {errors.New("tx_already_known")}, - Unsupported: {errors.New("unsupported")}, - }, - }, - { - Name: "Prefers sever error to temporary", - ExpectedTxResult: "underpriced", - ExpectedCriticalErr: "", - ResultsByCode: sendTxErrors{ - Retryable: {errors.New("retryable")}, - Underpriced: {errors.New("underpriced")}, - }, - }, - { - Name: "Returns temporary error", - ExpectedTxResult: "retryable", - ExpectedCriticalErr: "", - ResultsByCode: sendTxErrors{ - Retryable: {errors.New("retryable")}, - }, - }, - { - Name: "Insufficient funds is treated as error", - ExpectedTxResult: "", - ExpectedCriticalErr: "", - ResultsByCode: sendTxErrors{ - Successful: {nil}, - InsufficientFunds: {errors.New("insufficientFunds")}, - }, - }, - { - Name: "Logs critical error on empty ResultsByCode", - ExpectedTxResult: "expected at least one response on SendTransaction", - ExpectedCriticalErr: "expected at least one response on SendTransaction", - ResultsByCode: sendTxErrors{}, - }, - { - Name: "Zk out of counter error", - ExpectedTxResult: "not enough keccak counters to continue the execution", - ExpectedCriticalErr: "", - ResultsByCode: sendTxErrors{ - TerminallyStuck: {errors.New("not enough keccak counters to continue the execution")}, - }, - }, - } - - for _, testCase := range testCases { - for code := range testCase.ResultsByCode { - delete(codesToCover, code) - } - t.Run(testCase.Name, func(t *testing.T) { - txResult, err := aggregateTxResults(testCase.ResultsByCode) - if testCase.ExpectedTxResult == "" { - assert.NoError(t, err) - } else { - assert.EqualError(t, txResult, testCase.ExpectedTxResult) - } - - logger.Sugared(logger.Test(t)).Info("Map: " + fmt.Sprint(testCase.ResultsByCode)) - logger.Sugared(logger.Test(t)).Criticalw("observed invariant violation on SendTransaction", "resultsByCode", testCase.ResultsByCode, "err", err) - - if testCase.ExpectedCriticalErr == "" { - assert.NoError(t, err) - } else { - assert.EqualError(t, err, testCase.ExpectedCriticalErr) - } - }) - } - - // explicitly signal that following codes are properly handled in aggregateTxResults, - //but dedicated test cases won't be beneficial - for _, codeToIgnore := range []SendTxReturnCode{Unknown, ExceedsMaxFee, FeeOutOfValidRange} { - delete(codesToCover, codeToIgnore) - } - assert.Empty(t, codesToCover, "all of the SendTxReturnCode must be covered by this test") -} diff --git a/common/client/node.go b/common/client/node.go index 7885fe76760..66161ac5d5f 100644 --- a/common/client/node.go +++ b/common/client/node.go @@ -58,8 +58,7 @@ type ChainConfig interface { type Node[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], + RPC any, ] interface { // State returns most accurate state of the Node on the moment of call. // While some of the checks may be performed in the background and State may return cached value, critical, like @@ -72,13 +71,15 @@ type Node[ SetPoolChainInfoProvider(PoolChainInfoProvider) // Name is a unique identifier for this node. Name() string + // String - returns string representation of the node, useful for debugging (name + URLS used to connect to the RPC) String() string RPC() RPC - SubscribersCount() int32 // UnsubscribeAllExceptAliveLoop - closes all subscriptions except the aliveLoop subscription UnsubscribeAllExceptAliveLoop() ConfiguredChainID() CHAIN_ID + // Order - returns priority order configured for the RPC Order() int32 + // Start - starts health checks Start(context.Context) error Close() error } @@ -86,7 +87,7 @@ type Node[ type node[ CHAIN_ID types.ID, HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], + RPC RPCClient[CHAIN_ID, HEAD], ] struct { services.StateMachine lfcLog logger.Logger @@ -111,12 +112,14 @@ type node[ stopCh services.StopChan // wg waits for subsidiary goroutines wg sync.WaitGroup + + healthCheckSubs []types.Subscription } func NewNode[ CHAIN_ID types.ID, HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], + RPC RPCClient[CHAIN_ID, HEAD], ]( nodeCfg NodeConfig, chainCfg ChainConfig, @@ -129,7 +132,7 @@ func NewNode[ nodeOrder int32, rpc RPC, chainFamily string, -) Node[CHAIN_ID, HEAD, RPC] { +) Node[CHAIN_ID, RPC] { n := new(node[CHAIN_ID, HEAD, RPC]) n.name = name n.id = id @@ -181,12 +184,16 @@ func (n *node[CHAIN_ID, HEAD, RPC]) RPC() RPC { return n.rpc } -func (n *node[CHAIN_ID, HEAD, RPC]) SubscribersCount() int32 { - return n.rpc.SubscribersCount() +// unsubscribeAllExceptAliveLoop is not thread-safe; it should only be called +// while holding the stateMu lock. +func (n *node[CHAIN_ID, HEAD, RPC]) unsubscribeAllExceptAliveLoop() { + n.rpc.UnsubscribeAllExcept(n.healthCheckSubs...) } func (n *node[CHAIN_ID, HEAD, RPC]) UnsubscribeAllExceptAliveLoop() { - n.rpc.UnsubscribeAllExceptAliveLoop() + n.stateMu.Lock() + defer n.stateMu.Unlock() + n.unsubscribeAllExceptAliveLoop() } func (n *node[CHAIN_ID, HEAD, RPC]) Close() error { @@ -318,13 +325,6 @@ func (n *node[CHAIN_ID, HEAD, RPC]) verifyConn(ctx context.Context, lggr logger. return nodeStateAlive } -// disconnectAll disconnects all clients connected to the node -// WARNING: NOT THREAD-SAFE -// This must be called from within the n.stateMu lock -func (n *node[CHAIN_ID, HEAD, RPC]) disconnectAll() { - n.rpc.DisconnectAll() -} - func (n *node[CHAIN_ID, HEAD, RPC]) Order() int32 { return n.order } diff --git a/common/client/node_fsm.go b/common/client/node_fsm.go index e58de071fbc..4a80e4fae9b 100644 --- a/common/client/node_fsm.go +++ b/common/client/node_fsm.go @@ -256,7 +256,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) transitionToOutOfSync(fn func()) { } switch n.state { case nodeStateAlive: - n.disconnectAll() + n.rpc.Close() n.state = nodeStateOutOfSync default: panic(transitionFail(n.state, nodeStateOutOfSync)) @@ -281,7 +281,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) transitionToUnreachable(fn func()) { } switch n.state { case nodeStateUndialed, nodeStateDialed, nodeStateAlive, nodeStateOutOfSync, nodeStateInvalidChainID, nodeStateSyncing: - n.disconnectAll() + n.rpc.Close() n.state = nodeStateUnreachable default: panic(transitionFail(n.state, nodeStateUnreachable)) @@ -324,7 +324,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) transitionToInvalidChainID(fn func()) { } switch n.state { case nodeStateDialed, nodeStateOutOfSync, nodeStateSyncing: - n.disconnectAll() + n.rpc.Close() n.state = nodeStateInvalidChainID default: panic(transitionFail(n.state, nodeStateInvalidChainID)) @@ -349,7 +349,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) transitionToSyncing(fn func()) { } switch n.state { case nodeStateDialed, nodeStateOutOfSync, nodeStateInvalidChainID: - n.disconnectAll() + n.rpc.Close() n.state = nodeStateSyncing default: panic(transitionFail(n.state, nodeStateSyncing)) diff --git a/common/client/node_fsm_test.go b/common/client/node_fsm_test.go index dc0ca0e7de8..93460d934a3 100644 --- a/common/client/node_fsm_test.go +++ b/common/client/node_fsm_test.go @@ -39,47 +39,47 @@ func TestUnit_Node_StateTransitions(t *testing.T) { t.Run("transitionToAlive", func(t *testing.T) { const destinationState = nodeStateAlive allowedStates := []nodeState{nodeStateDialed, nodeStateInvalidChainID, nodeStateSyncing} - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) testTransition(t, rpc, testNode.transitionToAlive, destinationState, allowedStates...) }) t.Run("transitionToInSync", func(t *testing.T) { const destinationState = nodeStateAlive allowedStates := []nodeState{nodeStateOutOfSync, nodeStateSyncing} - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) testTransition(t, rpc, testNode.transitionToInSync, destinationState, allowedStates...) }) t.Run("transitionToOutOfSync", func(t *testing.T) { const destinationState = nodeStateOutOfSync allowedStates := []nodeState{nodeStateAlive} - rpc := newMockNodeClient[types.ID, Head](t) - rpc.On("DisconnectAll").Once() + rpc := newMockRPCClient[types.ID, Head](t) + rpc.On("Close") testTransition(t, rpc, testNode.transitionToOutOfSync, destinationState, allowedStates...) }) t.Run("transitionToUnreachable", func(t *testing.T) { const destinationState = nodeStateUnreachable allowedStates := []nodeState{nodeStateUndialed, nodeStateDialed, nodeStateAlive, nodeStateOutOfSync, nodeStateInvalidChainID, nodeStateSyncing} - rpc := newMockNodeClient[types.ID, Head](t) - rpc.On("DisconnectAll").Times(len(allowedStates)) + rpc := newMockRPCClient[types.ID, Head](t) + rpc.On("Close") testTransition(t, rpc, testNode.transitionToUnreachable, destinationState, allowedStates...) }) t.Run("transitionToInvalidChain", func(t *testing.T) { const destinationState = nodeStateInvalidChainID allowedStates := []nodeState{nodeStateDialed, nodeStateOutOfSync, nodeStateSyncing} - rpc := newMockNodeClient[types.ID, Head](t) - rpc.On("DisconnectAll").Times(len(allowedStates)) + rpc := newMockRPCClient[types.ID, Head](t) + rpc.On("Close") testTransition(t, rpc, testNode.transitionToInvalidChainID, destinationState, allowedStates...) }) t.Run("transitionToSyncing", func(t *testing.T) { const destinationState = nodeStateSyncing allowedStates := []nodeState{nodeStateDialed, nodeStateOutOfSync, nodeStateInvalidChainID} - rpc := newMockNodeClient[types.ID, Head](t) - rpc.On("DisconnectAll").Times(len(allowedStates)) + rpc := newMockRPCClient[types.ID, Head](t) + rpc.On("Close") testTransition(t, rpc, testNode.transitionToSyncing, destinationState, allowedStates...) }) t.Run("transitionToSyncing panics if nodeIsSyncing is disabled", func(t *testing.T) { - rpc := newMockNodeClient[types.ID, Head](t) - rpc.On("DisconnectAll").Once() + rpc := newMockRPCClient[types.ID, Head](t) + rpc.On("Close") node := newTestNode(t, testNodeOpts{rpc: rpc}) node.setState(nodeStateDialed) fn := new(fnMock) @@ -90,7 +90,7 @@ func TestUnit_Node_StateTransitions(t *testing.T) { }) } -func testTransition(t *testing.T, rpc *mockNodeClient[types.ID, Head], transition func(node testNode, fn func()), destinationState nodeState, allowedStates ...nodeState) { +func testTransition(t *testing.T, rpc *mockRPCClient[types.ID, Head], transition func(node testNode, fn func()), destinationState nodeState, allowedStates ...nodeState) { node := newTestNode(t, testNodeOpts{rpc: rpc, config: testNodeConfig{nodeIsSyncingEnabled: true}}) for _, allowedState := range allowedStates { m := new(fnMock) diff --git a/common/client/node_lifecycle.go b/common/client/node_lifecycle.go index 40d9a9ef6ef..ce508a43dde 100644 --- a/common/client/node_lifecycle.go +++ b/common/client/node_lifecycle.go @@ -103,23 +103,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { return } - // TODO: will be removed as part of merging effort with BCI-2875 - n.rpc.SetAliveLoopSub(headsSub.sub) - - defer headsSub.Unsubscribe() - - var finalizedHeadsSub headSubscription[HEAD] - if n.chainCfg.FinalityTagEnabled() { - finalizedHeadsSub, err = n.registerNewSubscription(ctx, lggr.With("subscriptionType", "finalizedHeads"), - n.chainCfg.NoNewFinalizedHeadsThreshold(), n.rpc.SubscribeToFinalizedHeads) - if err != nil { - lggr.Errorw("Failed to subscribe to finalized heads", "err", err) - n.declareUnreachable() - return - } - - defer finalizedHeadsSub.Unsubscribe() - } + defer n.unsubscribeHealthChecks() var pollCh <-chan time.Time if pollInterval > 0 { @@ -137,6 +121,17 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { lggr.Debug("Polling disabled") } + var finalizedHeadsSub headSubscription[HEAD] + if n.chainCfg.FinalityTagEnabled() { + finalizedHeadsSub, err = n.registerNewSubscription(ctx, lggr.With("subscriptionType", "finalizedHeads"), + n.chainCfg.NoNewFinalizedHeadsThreshold(), n.rpc.SubscribeToFinalizedHeads) + if err != nil { + lggr.Errorw("Failed to subscribe to finalized heads", "err", err) + n.declareUnreachable() + return + } + } + localHighestChainInfo, _ := n.rpc.GetInterceptedChainInfo() var pollFailures uint32 @@ -146,13 +141,10 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { return case <-pollCh: promPoolRPCNodePolls.WithLabelValues(n.chainID.String(), n.name).Inc() - lggr.Tracew("Polling for version", "nodeState", n.getCachedState(), "pollFailures", pollFailures) - var version string - version, err = func(ctx context.Context) (string, error) { - ctx, cancel := context.WithTimeout(ctx, pollInterval) - defer cancel() - return n.RPC().ClientVersion(ctx) - }(ctx) + lggr.Tracew("Pinging RPC", "nodeState", n.State(), "pollFailures", pollFailures) + pollCtx, cancel := context.WithTimeout(ctx, pollInterval) + err = n.RPC().Ping(pollCtx) + cancel() if err != nil { // prevent overflow if pollFailures < math.MaxUint32 { @@ -161,7 +153,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { } lggr.Warnw(fmt.Sprintf("Poll failure, RPC endpoint %s failed to respond properly", n.String()), "err", err, "pollFailures", pollFailures, "nodeState", n.getCachedState()) } else { - lggr.Debugw("Version poll successful", "nodeState", n.getCachedState(), "clientVersion", version) + lggr.Debugw("Ping successful", "nodeState", n.State()) promPoolRPCNodePollsSuccess.WithLabelValues(n.chainID.String(), n.name).Inc() pollFailures = 0 } @@ -193,7 +185,6 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { n.declareUnreachable() return } - receivedNewHead := n.onNewHead(lggr, &localHighestChainInfo, bh) if receivedNewHead && noNewHeadsTimeoutThreshold > 0 { headsSub.ResetTimer(noNewHeadsTimeoutThreshold) @@ -219,7 +210,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { return case latestFinalized, open := <-finalizedHeadsSub.Heads: if !open { - lggr.Errorw("Finalized heads subscription channel unexpectedly closed", "nodeState", n.getCachedState()) + lggr.Errorw("Finalized heads subscription channel unexpectedly closed") n.declareUnreachable() return } @@ -251,6 +242,15 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { } } +func (n *node[CHAIN_ID, HEAD, RPC]) unsubscribeHealthChecks() { + n.stateMu.Lock() + for _, sub := range n.healthCheckSubs { + sub.Unsubscribe() + } + n.healthCheckSubs = []types.Subscription{} + n.stateMu.Unlock() +} + type headSubscription[HEAD any] struct { Heads <-chan HEAD Errors <-chan error @@ -284,11 +284,10 @@ func (n *node[CHAIN_ID, HEAD, PRC]) registerNewSubscription(ctx context.Context, result.Errors = sub.Err() lggr.Debug("Successfully subscribed") - // TODO: will be removed as part of merging effort with BCI-2875 result.sub = sub - //n.stateMu.Lock() - //n.healthCheckSubs = append(n.healthCheckSubs, sub) - //n.stateMu.Unlock() + n.stateMu.Lock() + n.healthCheckSubs = append(n.healthCheckSubs, sub) + n.stateMu.Unlock() result.cleanUpTasks = append(result.cleanUpTasks, sub.Unsubscribe) @@ -350,6 +349,12 @@ func (n *node[CHAIN_ID, HEAD, RPC]) onNewHead(lggr logger.SugaredLogger, chainIn return true } +const ( + msgReceivedBlock = "Received block for RPC node, waiting until back in-sync to mark as live again" + msgReceivedFinalizedBlock = "Received new finalized block for RPC node, waiting until back in-sync to mark as live again" + msgInSync = "RPC node back in sync" +) + // isOutOfSyncWithPool returns outOfSync true if num or td is more than SyncThresold behind the best node. // Always returns outOfSync false for SyncThreshold 0. // liveNodes is only included when outOfSync is true. @@ -376,12 +381,6 @@ func (n *node[CHAIN_ID, HEAD, RPC]) isOutOfSyncWithPool(localState ChainInfo) (o } } -const ( - msgReceivedBlock = "Received block for RPC node, waiting until back in-sync to mark as live again" - msgReceivedFinalizedBlock = "Received new finalized block for RPC node, waiting until back in-sync to mark as live again" - msgInSync = "RPC node back in sync" -) - // outOfSyncLoop takes an OutOfSync node and waits until isOutOfSync returns false to go back to live status func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(syncIssues syncStatus) { defer n.wg.Done() @@ -422,8 +421,9 @@ func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(syncIssues syncStatus) { return } + defer n.unsubscribeHealthChecks() + lggr.Tracew("Successfully subscribed to heads feed on out-of-sync RPC node") - defer headsSub.Unsubscribe() noNewFinalizedBlocksTimeoutThreshold := n.chainCfg.NoNewFinalizedHeadsThreshold() var finalizedHeadsSub headSubscription[HEAD] @@ -437,7 +437,6 @@ func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(syncIssues syncStatus) { } lggr.Tracew("Successfully subscribed to finalized heads feed on out-of-sync RPC node") - defer finalizedHeadsSub.Unsubscribe() } _, localHighestChainInfo := n.rpc.GetInterceptedChainInfo() diff --git a/common/client/node_lifecycle_test.go b/common/client/node_lifecycle_test.go index 833bccf7f29..6f9b4653393 100644 --- a/common/client/node_lifecycle_test.go +++ b/common/client/node_lifecycle_test.go @@ -23,12 +23,19 @@ import ( "github.com/smartcontractkit/chainlink/v2/common/types/mocks" ) +func newSub(t *testing.T) *mocks.Subscription { + sub := mocks.NewSubscription(t) + sub.On("Err").Return((<-chan error)(nil)).Maybe() + sub.On("Unsubscribe") + return sub +} + func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { t.Parallel() newDialedNode := func(t *testing.T, opts testNodeOpts) testNode { node := newTestNode(t, opts) - opts.rpc.On("Close").Return(nil).Once() + opts.rpc.On("Close").Return(nil) node.setState(nodeStateDialed) return node @@ -42,14 +49,13 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("if initial subscribe fails, transitions to unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) node := newDialedNode(t, testNodeOpts{ rpc: rpc, }) defer func() { assert.NoError(t, node.close()) }() expectedError := errors.New("failed to subscribe to rpc") - rpc.On("DisconnectAll").Once() rpc.On("SubscribeToHeads", mock.Anything).Return(nil, nil, expectedError).Once() // might be called in unreachable loop rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() @@ -60,7 +66,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("if remote RPC connection is closed transitions to unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) node := newDialedNode(t, testNodeOpts{ @@ -76,9 +82,6 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { sub.On("Err").Return((<-chan error)(errChan)).Once() sub.On("Unsubscribe").Once() rpc.On("SubscribeToHeads", mock.Anything).Return(nil, sub, nil).Once() - rpc.On("SetAliveLoopSub", sub).Once() - // disconnects all on transfer to unreachable - rpc.On("DisconnectAll").Once() // might be called in unreachable loop rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() @@ -87,16 +90,13 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) newSubscribedNode := func(t *testing.T, opts testNodeOpts) testNode { - sub := mocks.NewSubscription(t) - sub.On("Err").Return((<-chan error)(nil)) - sub.On("Unsubscribe").Once() - opts.rpc.On("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), sub, nil) - opts.rpc.On("SetAliveLoopSub", sub).Once() + sub := newSub(t) + opts.rpc.On("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), sub, nil).Once() return newDialedNode(t, opts) } t.Run("Stays alive and waits for signal", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newSubscribedNode(t, testNodeOpts{ @@ -112,7 +112,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("stays alive while below pollFailureThreshold and resets counter on success", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}) lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) const pollFailureThreshold = 3 @@ -128,17 +128,17 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { pollError := errors.New("failed to get ClientVersion") // 1. Return error several times, but below threshold - rpc.On("ClientVersion", mock.Anything).Return("", pollError).Run(func(_ mock.Arguments) { + rpc.On("Ping", mock.Anything).Return(pollError).Run(func(_ mock.Arguments) { // stays healthy while below threshold assert.Equal(t, nodeStateAlive, node.State()) }).Times(pollFailureThreshold - 1) // 2. Successful call that is expected to reset counter - rpc.On("ClientVersion", mock.Anything).Return("client_version", nil).Once() + rpc.On("Ping", mock.Anything).Return(nil).Once() // 3. Return error. If we have not reset the timer, we'll transition to nonAliveState - rpc.On("ClientVersion", mock.Anything).Return("", pollError).Once() + rpc.On("Ping", mock.Anything).Return(pollError).Once() // 4. Once during the call, check if node is alive var ensuredAlive atomic.Bool - rpc.On("ClientVersion", mock.Anything).Return("client_version", nil).Run(func(_ mock.Arguments) { + rpc.On("Ping", mock.Anything).Return(nil).Run(func(_ mock.Arguments) { if ensuredAlive.Load() { return } @@ -146,15 +146,15 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { assert.Equal(t, nodeStateAlive, node.State()) }).Once() // redundant call to stay in alive state - rpc.On("ClientVersion", mock.Anything).Return("client_version", nil) + rpc.On("Ping", mock.Anything).Return(nil) node.declareAlive() tests.AssertLogCountEventually(t, observedLogs, fmt.Sprintf("Poll failure, RPC endpoint %s failed to respond properly", node.String()), pollFailureThreshold) - tests.AssertLogCountEventually(t, observedLogs, "Version poll successful", 2) + tests.AssertLogCountEventually(t, observedLogs, "Ping successful", 2) assert.True(t, ensuredAlive.Load(), "expected to ensure that node was alive") }) t.Run("with threshold poll failures, transitions to unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}) lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) const pollFailureThreshold = 3 @@ -168,10 +168,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) defer func() { assert.NoError(t, node.close()) }() pollError := errors.New("failed to get ClientVersion") - rpc.On("ClientVersion", mock.Anything).Return("", pollError) - // disconnects all on transfer to unreachable - rpc.On("DisconnectAll").Once() - // might be called in unreachable loop + rpc.On("Ping", mock.Anything).Return(pollError) rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() tests.AssertLogCountEventually(t, observedLogs, fmt.Sprintf("Poll failure, RPC endpoint %s failed to respond properly", node.String()), pollFailureThreshold) @@ -181,7 +178,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("with threshold poll failures, but we are the last node alive, forcibly keeps it alive", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) const pollFailureThreshold = 3 node := newSubscribedNode(t, testNodeOpts{ @@ -200,14 +197,14 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { node.SetPoolChainInfoProvider(poolInfo) rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: 20}, ChainInfo{BlockNumber: 20}) pollError := errors.New("failed to get ClientVersion") - rpc.On("ClientVersion", mock.Anything).Return("", pollError) + rpc.On("Ping", mock.Anything).Return(pollError) node.declareAlive() tests.AssertLogEventually(t, observedLogs, fmt.Sprintf("RPC endpoint failed to respond to %d consecutive polls", pollFailureThreshold)) assert.Equal(t, nodeStateAlive, node.State()) }) t.Run("when behind more than SyncThreshold, transitions to out of sync", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) const syncThreshold = 10 node := newSubscribedNode(t, testNodeOpts{ @@ -220,6 +217,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() + rpc.On("Ping", mock.Anything).Return(nil) const mostRecentBlock = 20 rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: mostRecentBlock}, ChainInfo{BlockNumber: 30}) poolInfo := newMockPoolChainInfoProvider(t) @@ -228,14 +226,11 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { TotalDifficulty: big.NewInt(10), }).Once() node.SetPoolChainInfoProvider(poolInfo) - rpc.On("ClientVersion", mock.Anything).Return("", nil) // tries to redial in outOfSync rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Run(func(_ mock.Arguments) { assert.Equal(t, nodeStateOutOfSync, node.State()) }).Once() - // disconnects all on transfer to unreachable or outOfSync - rpc.On("DisconnectAll").Maybe() - // might be called in unreachable loop + rpc.On("Close").Maybe() rpc.On("Dial", mock.Anything).Run(func(_ mock.Arguments) { require.Equal(t, nodeStateOutOfSync, node.State()) }).Return(errors.New("failed to dial")).Maybe() @@ -244,7 +239,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("when behind more than SyncThreshold but we are the last live node, forcibly stays alive", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) const syncThreshold = 10 node := newSubscribedNode(t, testNodeOpts{ @@ -257,6 +252,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() + rpc.On("Ping", mock.Anything).Return(nil) const mostRecentBlock = 20 rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: mostRecentBlock}, ChainInfo{BlockNumber: 30}) poolInfo := newMockPoolChainInfoProvider(t) @@ -265,13 +261,12 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { TotalDifficulty: big.NewInt(10), }).Once() node.SetPoolChainInfoProvider(poolInfo) - rpc.On("ClientVersion", mock.Anything).Return("", nil) node.declareAlive() tests.AssertLogEventually(t, observedLogs, fmt.Sprintf("RPC endpoint has fallen behind; %s %s", msgCannotDisable, msgDegradedState)) }) t.Run("when behind but SyncThreshold=0, stay alive", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newSubscribedNode(t, testNodeOpts{ config: testNodeConfig{ @@ -283,16 +278,16 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() + rpc.On("Ping", mock.Anything).Return(nil) const mostRecentBlock = 20 rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: mostRecentBlock}, ChainInfo{BlockNumber: 30}) - rpc.On("ClientVersion", mock.Anything).Return("", nil) node.declareAlive() - tests.AssertLogCountEventually(t, observedLogs, "Version poll successful", 2) + tests.AssertLogCountEventually(t, observedLogs, "Ping successful", 2) assert.Equal(t, nodeStateAlive, node.State()) }) t.Run("when no new heads received for threshold, transitions to out of sync", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() node := newSubscribedNode(t, testNodeOpts{ config: testNodeConfig{}, @@ -306,9 +301,6 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Run(func(_ mock.Arguments) { assert.Equal(t, nodeStateOutOfSync, node.State()) }).Once() - // disconnects all on transfer to unreachable or outOfSync - rpc.On("DisconnectAll").Maybe() - // might be called in unreachable loop rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() tests.AssertEventually(t, func() bool { @@ -319,7 +311,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("when no new heads received for threshold but we are the last live node, forcibly stays alive", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newSubscribedNode(t, testNodeOpts{ @@ -341,21 +333,16 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { tests.AssertLogEventually(t, observedLogs, fmt.Sprintf("RPC endpoint detected out of sync; %s %s", msgCannotDisable, msgDegradedState)) assert.Equal(t, nodeStateAlive, node.State()) }) - newSub := func(t *testing.T) *mocks.Subscription { - sub := mocks.NewSubscription(t) - sub.On("Err").Return((<-chan error)(nil)) - sub.On("Unsubscribe").Once() - return sub - } + t.Run("rpc closed head channel", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) + sub := newSub(t) + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() ch := make(chan Head) rpc.On("SubscribeToHeads", mock.Anything).Run(func(args mock.Arguments) { close(ch) - }).Return((<-chan Head)(ch), newSub(t), nil).Once() - rpc.On("SetAliveLoopSub", mock.Anything).Once() - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() + }).Return((<-chan Head)(ch), sub, nil).Once() lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel) node := newDialedNode(t, testNodeOpts{ lggr: lggr, @@ -366,9 +353,6 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { rpc: rpc, }) defer func() { assert.NoError(t, node.close()) }() - // disconnects all on transfer to unreachable or outOfSync - rpc.On("DisconnectAll").Once() - // might be called in unreachable loop rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() tests.AssertLogEventually(t, observedLogs, "Subscription channel unexpectedly closed") @@ -376,19 +360,16 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("If finality tag is not enabled updates finalized block metric using finality depth and latest head", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) - sub := mocks.NewSubscription(t) - sub.On("Err").Return((<-chan error)(nil)) - sub.On("Unsubscribe").Once() + rpc := newMockRPCClient[types.ID, Head](t) + sub := newSub(t) const blockNumber = 1000 const finalityDepth = 10 const expectedBlock = 990 - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() ch := make(chan Head) rpc.On("SubscribeToHeads", mock.Anything).Run(func(args mock.Arguments) { + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() go writeHeads(t, ch, head{BlockNumber: blockNumber - 1}, head{BlockNumber: blockNumber}, head{BlockNumber: blockNumber - 1}) }).Return((<-chan Head)(ch), sub, nil).Once() - rpc.On("SetAliveLoopSub", sub).Once() name := "node-" + rand.Str(5) node := newDialedNode(t, testNodeOpts{ config: testNodeConfig{}, @@ -407,13 +388,15 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { return float64(expectedBlock) == m.Gauge.GetValue() }) }) - t.Run("If fails to subscribe to latest finalized blocks, transitions to unreachable ", func(t *testing.T) { + t.Run("If fails to subscribe to latest finalized blocks, transitions to unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) + sub := newSub(t) + rpc.On("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), sub, nil).Once() expectedError := errors.New("failed to subscribe to finalized heads") - rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return(nil, mocks.NewSubscription(t), expectedError).Once() - lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - node := newSubscribedNode(t, testNodeOpts{ + rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return(nil, sub, expectedError).Once() + lggr, _ := logger.TestObserved(t, zap.DebugLevel) + node := newDialedNode(t, testNodeOpts{ config: testNodeConfig{ finalizedBlockPollInterval: tests.TestInterval, }, @@ -424,28 +407,24 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() - // disconnects all on transfer to unreachable or outOfSync - rpc.On("DisconnectAll").Once() - // might be called in unreachable loop - rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() - tests.AssertLogEventually(t, observedLogs, "Failed to subscribe to finalized heads") tests.AssertEventually(t, func() bool { - return nodeStateUnreachable == node.State() + return node.State() == nodeStateUnreachable }) }) t.Run("Logs warning if latest finalized block is not valid", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) + sub := newSub(t) + rpc.On("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), sub, nil).Once() ch := make(chan Head, 1) head := newMockHead(t) head.On("IsValid").Return(false) rpc.On("SubscribeToFinalizedHeads", mock.Anything).Run(func(args mock.Arguments) { ch <- head - }).Return((<-chan Head)(ch), newSub(t), nil).Once() + }).Return((<-chan Head)(ch), sub, nil).Once() + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() - rpc.On("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), newSub(t), nil).Once() - rpc.On("SetAliveLoopSub", mock.Anything).Once() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newDialedNode(t, testNodeOpts{ config: testNodeConfig{}, @@ -461,7 +440,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("On new finalized block updates corresponding metric", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) const expectedBlock = 1101 const finalityDepth = 10 ch := make(chan Head) @@ -496,7 +475,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("If finalized heads channel is closed, transitions to unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() ch := make(chan Head) close(ch) @@ -510,9 +489,6 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() - // disconnects all on transfer to unreachable or outOfSync - rpc.On("DisconnectAll").Once() - // might be called in unreachable loop rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() tests.AssertLogEventually(t, observedLogs, "Finalized heads subscription channel unexpectedly closed") @@ -522,7 +498,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("when no new finalized heads received for threshold, transitions to out of sync", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() ch := make(chan Head, 1) ch <- head{BlockNumber: 10}.ToMockHead(t) @@ -543,9 +519,6 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Run(func(_ mock.Arguments) { assert.Equal(t, nodeStateOutOfSync, node.State()) }).Once() - // disconnects all on transfer to unreachable or outOfSync - rpc.On("DisconnectAll").Maybe() - // might be called in unreachable loop rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() tests.AssertLogEventually(t, observed, fmt.Sprintf("RPC's finalized state is out of sync; no new finalized heads received for %s (last finalized head received was 10)", noNewFinalizedHeadsThreshold)) @@ -557,7 +530,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("when no new finalized heads received for threshold but we are the last live node, forcibly stays alive", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return(make(<-chan Head), newSub(t), nil).Once() lggr, observed := logger.TestObserved(t, zap.DebugLevel) @@ -584,8 +557,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("If finalized subscription returns an error, transitions to unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) - rpc.On("DisconnectAll").Once() + rpc := newMockRPCClient[types.ID, Head](t) rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() sub := mocks.NewSubscription(t) errCh := make(chan error, 1) @@ -602,8 +574,6 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() - // disconnects all on transfer to unreachable or outOfSync - // might be called in unreachable loop rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() tests.AssertLogEventually(t, observedLogs, "Finalized heads subscription was terminated") @@ -637,7 +607,7 @@ func writeHeads(t *testing.T, ch chan<- Head, heads ...head) { } } -func setupRPCForAliveLoop(t *testing.T, rpc *mockNodeClient[types.ID, Head]) { +func setupRPCForAliveLoop(t *testing.T, rpc *mockRPCClient[types.ID, Head]) { rpc.On("Dial", mock.Anything).Return(nil).Maybe() aliveSubscription := mocks.NewSubscription(t) aliveSubscription.On("Err").Return(nil).Maybe() @@ -653,9 +623,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { newAliveNode := func(t *testing.T, opts testNodeOpts) testNode { node := newTestNode(t, opts) - opts.rpc.On("Close").Return(nil).Once() - // disconnects all on transfer to unreachable or outOfSync - opts.rpc.On("DisconnectAll") + opts.rpc.On("Close").Return(nil) node.setState(nodeStateAlive) return node } @@ -669,7 +637,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) t.Run("on old blocks stays outOfSync and returns on close", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr := logger.Test(t) node := newAliveNode(t, testNodeOpts{ @@ -681,6 +649,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { rpc.On("Dial", mock.Anything).Return(nil).Once() rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: 0}, ChainInfo{BlockNumber: 13}).Once() outOfSyncSubscription := mocks.NewSubscription(t) @@ -706,7 +675,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) t.Run("if initial dial fails, transitions to unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) node := newAliveNode(t, testNodeOpts{ rpc: rpc, }) @@ -715,6 +684,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { expectedError := errors.New("failed to dial rpc") // might be called again in unreachable loop, so no need to set once rpc.On("Dial", mock.Anything).Return(expectedError) + node.declareOutOfSync(syncStatusNoNewHead) tests.AssertEventually(t, func() bool { return node.State() == nodeStateUnreachable @@ -722,7 +692,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) t.Run("if fail to get chainID, transitions to unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) node := newAliveNode(t, testNodeOpts{ rpc: rpc, }) @@ -732,6 +702,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { rpc.On("Dial", mock.Anything).Return(nil).Once() // for unreachable rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() + expectedError := errors.New("failed to get chain ID") // might be called multiple times rpc.On("ChainID", mock.Anything).Return(types.NewIDFromInt(0), expectedError) @@ -742,7 +713,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) t.Run("if chainID does not match, transitions to invalidChainID", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.NewIDFromInt(10) rpcChainID := types.NewIDFromInt(11) node := newAliveNode(t, testNodeOpts{ @@ -753,6 +724,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { // one for out-of-sync & one for invalid chainID rpc.On("Dial", mock.Anything).Return(nil).Twice() + // might be called multiple times rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil) node.declareOutOfSync(syncStatusNoNewHead) @@ -762,7 +734,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) t.Run("if syncing, transitions to syncing", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.NewIDFromInt(10) node := newAliveNode(t, testNodeOpts{ rpc: rpc, @@ -773,6 +745,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { rpc.On("Dial", mock.Anything).Return(nil) rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil) + // might be called multiple times rpc.On("IsSyncing", mock.Anything).Return(true, nil) node.declareOutOfSync(syncStatusNoNewHead) @@ -782,7 +755,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) t.Run("if fails to fetch syncing status, transitions to unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.NewIDFromInt(10) node := newAliveNode(t, testNodeOpts{ rpc: rpc, @@ -793,6 +766,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { // one for out-of-sync rpc.On("Dial", mock.Anything).Return(nil).Once() + // for unreachable rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() @@ -805,7 +779,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) t.Run("if fails to subscribe, becomes unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() node := newAliveNode(t, testNodeOpts{ rpc: rpc, @@ -817,6 +791,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() expectedError := errors.New("failed to subscribe") rpc.On("SubscribeToHeads", mock.Anything).Return(nil, nil, expectedError).Once() + rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe() node.declareOutOfSync(syncStatusNoNewHead) tests.AssertEventually(t, func() bool { @@ -825,7 +800,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) t.Run("on subscription termination becomes unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel) node := newAliveNode(t, testNodeOpts{ @@ -837,6 +812,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { rpc.On("Dial", mock.Anything).Return(nil).Once() rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() sub := mocks.NewSubscription(t) errChan := make(chan error, 1) @@ -853,7 +829,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) t.Run("becomes unreachable if head channel is closed", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel) node := newAliveNode(t, testNodeOpts{ @@ -865,11 +841,10 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { rpc.On("Dial", mock.Anything).Return(nil).Once() rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() - sub := mocks.NewSubscription(t) - sub.On("Err").Return((<-chan error)(nil)) - sub.On("Unsubscribe").Once() + sub := newSub(t) ch := make(chan Head) rpc.On("SubscribeToHeads", mock.Anything).Run(func(args mock.Arguments) { close(ch) @@ -883,7 +858,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) t.Run("becomes alive if it receives a newer head", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newAliveNode(t, testNodeOpts{ @@ -916,7 +891,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) t.Run("becomes alive if there is no other nodes", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newAliveNode(t, testNodeOpts{ @@ -953,7 +928,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) t.Run("Stays out-of-sync if received new head, but lags behind pool", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newAliveNode(t, testNodeOpts{ @@ -998,13 +973,11 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) // creates RPC mock with all calls necessary to create heads subscription that won't produce any events - newRPCWithNoOpHeads := func(t *testing.T, chainID types.ID) *mockNodeClient[types.ID, Head] { - rpc := newMockNodeClient[types.ID, Head](t) + newRPCWithNoOpHeads := func(t *testing.T, chainID types.ID) *mockRPCClient[types.ID, Head] { + rpc := newMockRPCClient[types.ID, Head](t) rpc.On("Dial", mock.Anything).Return(nil).Once() rpc.On("ChainID", mock.Anything).Return(chainID, nil).Once() - sub := mocks.NewSubscription(t) - sub.On("Err").Return((<-chan error)(nil)) - sub.On("Unsubscribe").Once() + sub := newSub(t) rpc.On("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), sub, nil).Once() return rpc } @@ -1053,6 +1026,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { sub.On("Unsubscribe").Once() rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return(make(<-chan Head), sub, nil).Once() rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() + // unreachable rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe() node.declareOutOfSync(syncStatusNoNewHead) @@ -1076,9 +1050,8 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) defer func() { assert.NoError(t, node.close()) }() - sub := mocks.NewSubscription(t) - sub.On("Err").Return((<-chan error)(nil)) - sub.On("Unsubscribe").Once() + sub := newSub(t) + ch := make(chan Head) rpc.On("SubscribeToFinalizedHeads", mock.Anything).Run(func(args mock.Arguments) { close(ch) @@ -1168,9 +1141,7 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { newAliveNode := func(t *testing.T, opts testNodeOpts) testNode { node := newTestNode(t, opts) - opts.rpc.On("Close").Return(nil).Once() - // disconnects all on transfer to unreachable - opts.rpc.On("DisconnectAll") + opts.rpc.On("Close").Return(nil) node.setState(nodeStateAlive) return node @@ -1184,7 +1155,7 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { }) t.Run("on failed redial, keeps trying", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newAliveNode(t, testNodeOpts{ @@ -1200,7 +1171,7 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { }) t.Run("on failed chainID verification, keep trying", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newAliveNode(t, testNodeOpts{ @@ -1219,7 +1190,7 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { }) t.Run("on chain ID mismatch transitions to invalidChainID", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.NewIDFromInt(10) rpcChainID := types.NewIDFromInt(11) node := newAliveNode(t, testNodeOpts{ @@ -1230,6 +1201,7 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { rpc.On("Dial", mock.Anything).Return(nil) rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil) + node.declareUnreachable() tests.AssertEventually(t, func() bool { return node.State() == nodeStateInvalidChainID @@ -1237,7 +1209,7 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { }) t.Run("on syncing status check failure, keeps trying", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newAliveNode(t, testNodeOpts{ @@ -1258,7 +1230,7 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { }) t.Run("on syncing, transitions to syncing state", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() node := newAliveNode(t, testNodeOpts{ rpc: rpc, @@ -1280,7 +1252,7 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { }) t.Run("on successful verification becomes alive", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() node := newAliveNode(t, testNodeOpts{ rpc: rpc, @@ -1289,10 +1261,8 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { }) defer func() { assert.NoError(t, node.close()) }() - rpc.On("Dial", mock.Anything).Return(nil) rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil) rpc.On("IsSyncing", mock.Anything).Return(false, nil) - setupRPCForAliveLoop(t, rpc) node.declareUnreachable() @@ -1302,7 +1272,7 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { }) t.Run("on successful verification without isSyncing becomes alive", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() node := newAliveNode(t, testNodeOpts{ rpc: rpc, @@ -1326,8 +1296,7 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { t.Parallel() newDialedNode := func(t *testing.T, opts testNodeOpts) testNode { node := newTestNode(t, opts) - opts.rpc.On("Close").Return(nil).Once() - opts.rpc.On("DisconnectAll") + opts.rpc.On("Close").Return(nil) node.setState(nodeStateDialed) return node @@ -1341,7 +1310,7 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { }) t.Run("on invalid dial becomes unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() node := newDialedNode(t, testNodeOpts{ rpc: rpc, @@ -1350,6 +1319,8 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")) + rpc.On("Close") + node.declareInvalidChainID() tests.AssertEventually(t, func() bool { return node.State() == nodeStateUnreachable @@ -1357,7 +1328,7 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { }) t.Run("on failed chainID call becomes unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newDialedNode(t, testNodeOpts{ @@ -1371,6 +1342,7 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { // once for chainID and maybe another one for unreachable rpc.On("Dial", mock.Anything).Return(nil).Once() rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() + node.declareInvalidChainID() tests.AssertLogEventually(t, observedLogs, "Failed to verify chain ID for node") tests.AssertEventually(t, func() bool { @@ -1379,7 +1351,7 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { }) t.Run("on chainID mismatch keeps trying", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.NewIDFromInt(10) rpcChainID := types.NewIDFromInt(11) lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) @@ -1392,6 +1364,7 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { rpc.On("Dial", mock.Anything).Return(nil).Once() rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil) + node.declareInvalidChainID() tests.AssertLogCountEventually(t, observedLogs, "Failed to verify RPC node; remote endpoint returned the wrong chain ID", 2) tests.AssertEventually(t, func() bool { @@ -1400,7 +1373,7 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { }) t.Run("on successful verification without isSyncing becomes alive", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.NewIDFromInt(10) rpcChainID := types.NewIDFromInt(11) node := newDialedNode(t, testNodeOpts{ @@ -1409,12 +1382,10 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { }) defer func() { assert.NoError(t, node.close()) }() - rpc.On("Dial", mock.Anything).Return(nil).Once() + setupRPCForAliveLoop(t, rpc) rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil).Once() rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() - setupRPCForAliveLoop(t, rpc) - node.declareInvalidChainID() tests.AssertEventually(t, func() bool { return node.State() == nodeStateAlive @@ -1422,7 +1393,7 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { }) t.Run("on successful verification becomes alive", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.NewIDFromInt(10) rpcChainID := types.NewIDFromInt(11) node := newDialedNode(t, testNodeOpts{ @@ -1432,7 +1403,6 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { }) defer func() { assert.NoError(t, node.close()) }() - rpc.On("Dial", mock.Anything).Return(nil).Once() rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil).Once() rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() rpc.On("IsSyncing", mock.Anything).Return(false, nil).Once() @@ -1451,13 +1421,13 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { newNode := func(t *testing.T, opts testNodeOpts) testNode { node := newTestNode(t, opts) - opts.rpc.On("Close").Return(nil).Once() + opts.rpc.On("Close").Return(nil) return node } t.Run("if fails on initial dial, becomes unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newNode(t, testNodeOpts{ @@ -1468,8 +1438,6 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")) - // disconnects all on transfer to unreachable - rpc.On("DisconnectAll") err := node.Start(tests.Context(t)) assert.NoError(t, err) tests.AssertLogEventually(t, observedLogs, "Dial failed: Node is unreachable") @@ -1479,7 +1447,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { }) t.Run("if chainID verification fails, becomes unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newNode(t, testNodeOpts{ @@ -1493,8 +1461,6 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { rpc.On("ChainID", mock.Anything).Run(func(_ mock.Arguments) { assert.Equal(t, nodeStateDialed, node.State()) }).Return(nodeChainID, errors.New("failed to get chain id")) - // disconnects all on transfer to unreachable - rpc.On("DisconnectAll") err := node.Start(tests.Context(t)) assert.NoError(t, err) tests.AssertLogEventually(t, observedLogs, "Failed to verify chain ID for node") @@ -1504,7 +1470,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { }) t.Run("on chain ID mismatch transitions to invalidChainID", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.NewIDFromInt(10) rpcChainID := types.NewIDFromInt(11) node := newNode(t, testNodeOpts{ @@ -1514,9 +1480,8 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() rpc.On("Dial", mock.Anything).Return(nil) + rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil) - // disconnects all on transfer to unreachable - rpc.On("DisconnectAll") err := node.Start(tests.Context(t)) assert.NoError(t, err) tests.AssertEventually(t, func() bool { @@ -1525,7 +1490,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { }) t.Run("if syncing verification fails, becomes unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newNode(t, testNodeOpts{ @@ -1537,13 +1502,11 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() rpc.On("Dial", mock.Anything).Return(nil).Once() + rpc.On("ChainID", mock.Anything).Run(func(_ mock.Arguments) { assert.Equal(t, nodeStateDialed, node.State()) }).Return(nodeChainID, nil).Once() rpc.On("IsSyncing", mock.Anything).Return(false, errors.New("failed to check syncing status")) - // disconnects all on transfer to unreachable - rpc.On("DisconnectAll") - // fail to redial to stay in unreachable state rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")) err := node.Start(tests.Context(t)) assert.NoError(t, err) @@ -1554,7 +1517,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { }) t.Run("on isSyncing transitions to syncing", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.NewIDFromInt(10) node := newNode(t, testNodeOpts{ rpc: rpc, @@ -1564,10 +1527,9 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() rpc.On("Dial", mock.Anything).Return(nil) + rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil) rpc.On("IsSyncing", mock.Anything).Return(true, nil) - // disconnects all on transfer to unreachable - rpc.On("DisconnectAll") err := node.Start(tests.Context(t)) assert.NoError(t, err) tests.AssertEventually(t, func() bool { @@ -1576,7 +1538,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { }) t.Run("on successful verification becomes alive", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() node := newNode(t, testNodeOpts{ rpc: rpc, @@ -1585,10 +1547,8 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { }) defer func() { assert.NoError(t, node.close()) }() - rpc.On("Dial", mock.Anything).Return(nil) rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil) rpc.On("IsSyncing", mock.Anything).Return(false, nil) - setupRPCForAliveLoop(t, rpc) err := node.Start(tests.Context(t)) @@ -1599,7 +1559,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { }) t.Run("on successful verification without isSyncing becomes alive", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() node := newNode(t, testNodeOpts{ rpc: rpc, @@ -1607,9 +1567,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { }) defer func() { assert.NoError(t, node.close()) }() - rpc.On("Dial", mock.Anything).Return(nil) rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil) - setupRPCForAliveLoop(t, rpc) err := node.Start(tests.Context(t)) @@ -1765,8 +1723,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { newDialedNode := func(t *testing.T, opts testNodeOpts) testNode { opts.config.nodeIsSyncingEnabled = true node := newTestNode(t, opts) - opts.rpc.On("Close").Return(nil).Once() - opts.rpc.On("DisconnectAll") + opts.rpc.On("Close").Return(nil) node.setState(nodeStateDialed) return node @@ -1780,7 +1737,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { }) t.Run("on invalid dial becomes unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() node := newDialedNode(t, testNodeOpts{ rpc: rpc, @@ -1789,6 +1746,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")) + node.declareSyncing() tests.AssertEventually(t, func() bool { return node.State() == nodeStateUnreachable @@ -1796,7 +1754,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { }) t.Run("on failed chainID call becomes unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newDialedNode(t, testNodeOpts{ @@ -1807,6 +1765,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() rpc.On("ChainID", mock.Anything).Return(nodeChainID, errors.New("failed to get chain id")) + // once for syncing and maybe another one for unreachable rpc.On("Dial", mock.Anything).Return(nil).Once() rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() @@ -1818,7 +1777,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { }) t.Run("on chainID mismatch transitions to invalidChainID", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.NewIDFromInt(10) rpcChainID := types.NewIDFromInt(11) lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) @@ -1830,6 +1789,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() rpc.On("Dial", mock.Anything).Return(nil).Twice() + rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil) node.declareSyncing() tests.AssertLogCountEventually(t, observedLogs, "Failed to verify RPC node; remote endpoint returned the wrong chain ID", 2) @@ -1839,7 +1799,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { }) t.Run("on failed Syncing check - becomes unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newDialedNode(t, testNodeOpts{ @@ -1855,6 +1815,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { rpc.On("IsSyncing", mock.Anything).Return(false, errors.New("failed to check if syncing")).Once() rpc.On("Dial", mock.Anything).Return(nil).Once() rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() + node.declareSyncing() tests.AssertLogEventually(t, observedLogs, "Unexpected error while verifying RPC node synchronization status") tests.AssertEventually(t, func() bool { @@ -1863,7 +1824,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { }) t.Run("on IsSyncing - keeps trying", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newDialedNode(t, testNodeOpts{ @@ -1876,6 +1837,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() rpc.On("IsSyncing", mock.Anything).Return(true, nil) rpc.On("Dial", mock.Anything).Return(nil).Once() + node.declareSyncing() tests.AssertLogCountEventually(t, observedLogs, "Verification failed: Node is syncing", 2) tests.AssertEventually(t, func() bool { @@ -1884,7 +1846,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { }) t.Run("on successful verification becomes alive", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() node := newDialedNode(t, testNodeOpts{ rpc: rpc, @@ -1893,6 +1855,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() rpc.On("Dial", mock.Anything).Return(nil).Once() + rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() rpc.On("IsSyncing", mock.Anything).Return(true, nil).Once() rpc.On("IsSyncing", mock.Anything).Return(false, nil).Once() @@ -1980,7 +1943,7 @@ func TestNode_State(t *testing.T) { } for _, tc := range testCases { t.Run(tc.Name, func(t *testing.T) { - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) rpc.On("GetInterceptedChainInfo").Return(tc.NodeChainInfo, tc.PoolChainInfo).Once() node := newTestNode(t, testNodeOpts{ config: testNodeConfig{ diff --git a/common/client/node_selector.go b/common/client/node_selector.go index 4aae3734eea..372b521bb1c 100644 --- a/common/client/node_selector.go +++ b/common/client/node_selector.go @@ -15,30 +15,28 @@ const ( type NodeSelector[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], + RPC any, ] interface { // Select returns a Node, or nil if none can be selected. // Implementation must be thread-safe. - Select() Node[CHAIN_ID, HEAD, RPC] + Select() Node[CHAIN_ID, RPC] // Name returns the strategy name, e.g. "HighestHead" or "RoundRobin" Name() string } func newNodeSelector[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], -](selectionMode string, nodes []Node[CHAIN_ID, HEAD, RPC]) NodeSelector[CHAIN_ID, HEAD, RPC] { + RPC any, +](selectionMode string, nodes []Node[CHAIN_ID, RPC]) NodeSelector[CHAIN_ID, RPC] { switch selectionMode { case NodeSelectionModeHighestHead: - return NewHighestHeadNodeSelector[CHAIN_ID, HEAD, RPC](nodes) + return NewHighestHeadNodeSelector[CHAIN_ID, RPC](nodes) case NodeSelectionModeRoundRobin: - return NewRoundRobinSelector[CHAIN_ID, HEAD, RPC](nodes) + return NewRoundRobinSelector[CHAIN_ID, RPC](nodes) case NodeSelectionModeTotalDifficulty: - return NewTotalDifficultyNodeSelector[CHAIN_ID, HEAD, RPC](nodes) + return NewTotalDifficultyNodeSelector[CHAIN_ID, RPC](nodes) case NodeSelectionModePriorityLevel: - return NewPriorityLevelNodeSelector[CHAIN_ID, HEAD, RPC](nodes) + return NewPriorityLevelNodeSelector[CHAIN_ID, RPC](nodes) default: panic(fmt.Sprintf("unsupported NodeSelectionMode: %s", selectionMode)) } diff --git a/common/client/node_selector_highest_head.go b/common/client/node_selector_highest_head.go index 25a931fc01b..454584a77e1 100644 --- a/common/client/node_selector_highest_head.go +++ b/common/client/node_selector_highest_head.go @@ -8,21 +8,19 @@ import ( type highestHeadNodeSelector[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], -] []Node[CHAIN_ID, HEAD, RPC] + RPC any, +] []Node[CHAIN_ID, RPC] func NewHighestHeadNodeSelector[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], -](nodes []Node[CHAIN_ID, HEAD, RPC]) NodeSelector[CHAIN_ID, HEAD, RPC] { - return highestHeadNodeSelector[CHAIN_ID, HEAD, RPC](nodes) + RPC any, +](nodes []Node[CHAIN_ID, RPC]) NodeSelector[CHAIN_ID, RPC] { + return highestHeadNodeSelector[CHAIN_ID, RPC](nodes) } -func (s highestHeadNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HEAD, RPC] { +func (s highestHeadNodeSelector[CHAIN_ID, RPC]) Select() Node[CHAIN_ID, RPC] { var highestHeadNumber int64 = math.MinInt64 - var highestHeadNodes []Node[CHAIN_ID, HEAD, RPC] + var highestHeadNodes []Node[CHAIN_ID, RPC] for _, n := range s { state, currentChainInfo := n.StateAndLatest() currentHeadNumber := currentChainInfo.BlockNumber @@ -37,6 +35,6 @@ func (s highestHeadNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HE return firstOrHighestPriority(highestHeadNodes) } -func (s highestHeadNodeSelector[CHAIN_ID, HEAD, RPC]) Name() string { +func (s highestHeadNodeSelector[CHAIN_ID, RPC]) Name() string { return NodeSelectionModeHighestHead } diff --git a/common/client/node_selector_highest_head_test.go b/common/client/node_selector_highest_head_test.go index e245924589c..ebee3f403e2 100644 --- a/common/client/node_selector_highest_head_test.go +++ b/common/client/node_selector_highest_head_test.go @@ -9,19 +9,19 @@ import ( ) func TestHighestHeadNodeSelectorName(t *testing.T) { - selector := newNodeSelector[types.ID, Head, NodeClient[types.ID, Head]](NodeSelectionModeHighestHead, nil) + selector := newNodeSelector[types.ID, RPCClient[types.ID, Head]](NodeSelectionModeHighestHead, nil) assert.Equal(t, selector.Name(), NodeSelectionModeHighestHead) } func TestHighestHeadNodeSelector(t *testing.T) { t.Parallel() - type nodeClient NodeClient[types.ID, Head] + type nodeClient RPCClient[types.ID, Head] - var nodes []Node[types.ID, Head, nodeClient] + var nodes []Node[types.ID, nodeClient] for i := 0; i < 3; i++ { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) if i == 0 { // first node is out of sync node.On("StateAndLatest").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: int64(-1)}) @@ -36,11 +36,11 @@ func TestHighestHeadNodeSelector(t *testing.T) { nodes = append(nodes, node) } - selector := newNodeSelector[types.ID, Head, nodeClient](NodeSelectionModeHighestHead, nodes) + selector := newNodeSelector[types.ID, nodeClient](NodeSelectionModeHighestHead, nodes) assert.Same(t, nodes[2], selector.Select()) t.Run("stick to the same node", func(t *testing.T) { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) // fourth node is alive, LatestReceivedBlockNumber = 2 (same as 3rd) node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(2)}) node.On("Order").Return(int32(1)) @@ -51,7 +51,7 @@ func TestHighestHeadNodeSelector(t *testing.T) { }) t.Run("another best node", func(t *testing.T) { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) // fifth node is alive, LatestReceivedBlockNumber = 3 (better than 3rd and 4th) node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) node.On("Order").Return(int32(1)) @@ -62,13 +62,13 @@ func TestHighestHeadNodeSelector(t *testing.T) { }) t.Run("nodes never update latest block number", func(t *testing.T) { - node1 := newMockNode[types.ID, Head, nodeClient](t) + node1 := newMockNode[types.ID, nodeClient](t) node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(-1)}) node1.On("Order").Return(int32(1)) - node2 := newMockNode[types.ID, Head, nodeClient](t) + node2 := newMockNode[types.ID, nodeClient](t) node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(-1)}) node2.On("Order").Return(int32(1)) - selector := newNodeSelector(NodeSelectionModeHighestHead, []Node[types.ID, Head, nodeClient]{node1, node2}) + selector := newNodeSelector(NodeSelectionModeHighestHead, []Node[types.ID, nodeClient]{node1, node2}) assert.Same(t, node1, selector.Select()) }) } @@ -76,11 +76,11 @@ func TestHighestHeadNodeSelector(t *testing.T) { func TestHighestHeadNodeSelector_None(t *testing.T) { t.Parallel() - type nodeClient NodeClient[types.ID, Head] - var nodes []Node[types.ID, Head, nodeClient] + type nodeClient RPCClient[types.ID, Head] + var nodes []Node[types.ID, nodeClient] for i := 0; i < 3; i++ { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) if i == 0 { // first node is out of sync node.On("StateAndLatest").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: int64(-1)}) @@ -98,12 +98,12 @@ func TestHighestHeadNodeSelector_None(t *testing.T) { func TestHighestHeadNodeSelectorWithOrder(t *testing.T) { t.Parallel() - type nodeClient NodeClient[types.ID, Head] - var nodes []Node[types.ID, Head, nodeClient] + type nodeClient RPCClient[types.ID, Head] + var nodes []Node[types.ID, nodeClient] t.Run("same head and order", func(t *testing.T) { for i := 0; i < 3; i++ { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(1)}) node.On("Order").Return(int32(2)) nodes = append(nodes, node) @@ -114,61 +114,61 @@ func TestHighestHeadNodeSelectorWithOrder(t *testing.T) { }) t.Run("same head but different order", func(t *testing.T) { - node1 := newMockNode[types.ID, Head, nodeClient](t) + node1 := newMockNode[types.ID, nodeClient](t) node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) node1.On("Order").Return(int32(3)) - node2 := newMockNode[types.ID, Head, nodeClient](t) + node2 := newMockNode[types.ID, nodeClient](t) node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) node2.On("Order").Return(int32(1)) - node3 := newMockNode[types.ID, Head, nodeClient](t) + node3 := newMockNode[types.ID, nodeClient](t) node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) node3.On("Order").Return(int32(2)) - nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3} + nodes := []Node[types.ID, nodeClient]{node1, node2, node3} selector := newNodeSelector(NodeSelectionModeHighestHead, nodes) //Should select the second node as it has the highest priority assert.Same(t, nodes[1], selector.Select()) }) t.Run("different head but same order", func(t *testing.T) { - node1 := newMockNode[types.ID, Head, nodeClient](t) + node1 := newMockNode[types.ID, nodeClient](t) node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(1)}) node1.On("Order").Maybe().Return(int32(3)) - node2 := newMockNode[types.ID, Head, nodeClient](t) + node2 := newMockNode[types.ID, nodeClient](t) node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(2)}) node2.On("Order").Maybe().Return(int32(3)) - node3 := newMockNode[types.ID, Head, nodeClient](t) + node3 := newMockNode[types.ID, nodeClient](t) node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) node3.On("Order").Return(int32(3)) - nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3} + nodes := []Node[types.ID, nodeClient]{node1, node2, node3} selector := newNodeSelector(NodeSelectionModeHighestHead, nodes) //Should select the third node as it has the highest head assert.Same(t, nodes[2], selector.Select()) }) t.Run("different head and different order", func(t *testing.T) { - node1 := newMockNode[types.ID, Head, nodeClient](t) + node1 := newMockNode[types.ID, nodeClient](t) node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(10)}) node1.On("Order").Maybe().Return(int32(3)) - node2 := newMockNode[types.ID, Head, nodeClient](t) + node2 := newMockNode[types.ID, nodeClient](t) node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(11)}) node2.On("Order").Maybe().Return(int32(4)) - node3 := newMockNode[types.ID, Head, nodeClient](t) + node3 := newMockNode[types.ID, nodeClient](t) node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(11)}) node3.On("Order").Maybe().Return(int32(3)) - node4 := newMockNode[types.ID, Head, nodeClient](t) + node4 := newMockNode[types.ID, nodeClient](t) node4.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(10)}) node4.On("Order").Maybe().Return(int32(1)) - nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3, node4} + nodes := []Node[types.ID, nodeClient]{node1, node2, node3, node4} selector := newNodeSelector(NodeSelectionModeHighestHead, nodes) //Should select the third node as it has the highest head and will win the priority tie-breaker assert.Same(t, nodes[2], selector.Select()) diff --git a/common/client/node_selector_priority_level.go b/common/client/node_selector_priority_level.go index 45cc62de077..6d6784fb216 100644 --- a/common/client/node_selector_priority_level.go +++ b/common/client/node_selector_priority_level.go @@ -10,34 +10,31 @@ import ( type priorityLevelNodeSelector[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], + RPC any, ] struct { - nodes []Node[CHAIN_ID, HEAD, RPC] + nodes []Node[CHAIN_ID, RPC] roundRobinCount []atomic.Uint32 } type nodeWithPriority[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], + RPC any, ] struct { - node Node[CHAIN_ID, HEAD, RPC] + node Node[CHAIN_ID, RPC] priority int32 } func NewPriorityLevelNodeSelector[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], -](nodes []Node[CHAIN_ID, HEAD, RPC]) NodeSelector[CHAIN_ID, HEAD, RPC] { - return &priorityLevelNodeSelector[CHAIN_ID, HEAD, RPC]{ + RPC any, +](nodes []Node[CHAIN_ID, RPC]) NodeSelector[CHAIN_ID, RPC] { + return &priorityLevelNodeSelector[CHAIN_ID, RPC]{ nodes: nodes, roundRobinCount: make([]atomic.Uint32, nrOfPriorityTiers(nodes)), } } -func (s priorityLevelNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HEAD, RPC] { +func (s priorityLevelNodeSelector[CHAIN_ID, RPC]) Select() Node[CHAIN_ID, RPC] { nodes := s.getHighestPriorityAliveTier() if len(nodes) == 0 { @@ -52,17 +49,17 @@ func (s priorityLevelNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, return nodes[idx].node } -func (s priorityLevelNodeSelector[CHAIN_ID, HEAD, RPC]) Name() string { +func (s priorityLevelNodeSelector[CHAIN_ID, RPC]) Name() string { return NodeSelectionModePriorityLevel } // getHighestPriorityAliveTier filters nodes that are not in state nodeStateAlive and // returns only the highest tier of alive nodes -func (s priorityLevelNodeSelector[CHAIN_ID, HEAD, RPC]) getHighestPriorityAliveTier() []nodeWithPriority[CHAIN_ID, HEAD, RPC] { - var nodes []nodeWithPriority[CHAIN_ID, HEAD, RPC] +func (s priorityLevelNodeSelector[CHAIN_ID, RPC]) getHighestPriorityAliveTier() []nodeWithPriority[CHAIN_ID, RPC] { + var nodes []nodeWithPriority[CHAIN_ID, RPC] for _, n := range s.nodes { if n.State() == nodeStateAlive { - nodes = append(nodes, nodeWithPriority[CHAIN_ID, HEAD, RPC]{n, n.Order()}) + nodes = append(nodes, nodeWithPriority[CHAIN_ID, RPC]{n, n.Order()}) } } @@ -76,14 +73,13 @@ func (s priorityLevelNodeSelector[CHAIN_ID, HEAD, RPC]) getHighestPriorityAliveT // removeLowerTiers take a slice of nodeWithPriority[CHAIN_ID, BLOCK_HASH, HEAD, RPC] and keeps only the highest tier func removeLowerTiers[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], -](nodes []nodeWithPriority[CHAIN_ID, HEAD, RPC]) []nodeWithPriority[CHAIN_ID, HEAD, RPC] { + RPC any, +](nodes []nodeWithPriority[CHAIN_ID, RPC]) []nodeWithPriority[CHAIN_ID, RPC] { sort.SliceStable(nodes, func(i, j int) bool { return nodes[i].priority > nodes[j].priority }) - var nodes2 []nodeWithPriority[CHAIN_ID, HEAD, RPC] + var nodes2 []nodeWithPriority[CHAIN_ID, RPC] currentPriority := nodes[len(nodes)-1].priority for _, n := range nodes { @@ -98,9 +94,8 @@ func removeLowerTiers[ // nrOfPriorityTiers calculates the total number of priority tiers func nrOfPriorityTiers[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], -](nodes []Node[CHAIN_ID, HEAD, RPC]) int32 { + RPC any, +](nodes []Node[CHAIN_ID, RPC]) int32 { highestPriority := int32(0) for _, n := range nodes { priority := n.Order() @@ -114,11 +109,10 @@ func nrOfPriorityTiers[ // firstOrHighestPriority takes a list of nodes and returns the first one with the highest priority func firstOrHighestPriority[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], -](nodes []Node[CHAIN_ID, HEAD, RPC]) Node[CHAIN_ID, HEAD, RPC] { + RPC any, +](nodes []Node[CHAIN_ID, RPC]) Node[CHAIN_ID, RPC] { hp := int32(math.MaxInt32) - var node Node[CHAIN_ID, HEAD, RPC] + var node Node[CHAIN_ID, RPC] for _, n := range nodes { if n.Order() < hp { hp = n.Order() diff --git a/common/client/node_selector_priority_level_test.go b/common/client/node_selector_priority_level_test.go index 15a7a7ac60b..67aac97be1b 100644 --- a/common/client/node_selector_priority_level_test.go +++ b/common/client/node_selector_priority_level_test.go @@ -9,14 +9,14 @@ import ( ) func TestPriorityLevelNodeSelectorName(t *testing.T) { - selector := newNodeSelector[types.ID, Head, NodeClient[types.ID, Head]](NodeSelectionModePriorityLevel, nil) + selector := newNodeSelector[types.ID, RPCClient[types.ID, Head]](NodeSelectionModePriorityLevel, nil) assert.Equal(t, selector.Name(), NodeSelectionModePriorityLevel) } func TestPriorityLevelNodeSelector(t *testing.T) { t.Parallel() - type nodeClient NodeClient[types.ID, Head] + type nodeClient RPCClient[types.ID, Head] type testNode struct { order int32 state nodeState @@ -66,9 +66,9 @@ func TestPriorityLevelNodeSelector(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - var nodes []Node[types.ID, Head, nodeClient] + var nodes []Node[types.ID, nodeClient] for _, tn := range tc.nodes { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) node.On("State").Return(tn.state) node.On("Order").Return(tn.order) nodes = append(nodes, node) diff --git a/common/client/node_selector_round_robin.go b/common/client/node_selector_round_robin.go index 5cdad7f52ee..18cea03ebd5 100644 --- a/common/client/node_selector_round_robin.go +++ b/common/client/node_selector_round_robin.go @@ -8,25 +8,23 @@ import ( type roundRobinSelector[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], + RPC any, ] struct { - nodes []Node[CHAIN_ID, HEAD, RPC] + nodes []Node[CHAIN_ID, RPC] roundRobinCount atomic.Uint32 } func NewRoundRobinSelector[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], -](nodes []Node[CHAIN_ID, HEAD, RPC]) NodeSelector[CHAIN_ID, HEAD, RPC] { - return &roundRobinSelector[CHAIN_ID, HEAD, RPC]{ + RPC any, +](nodes []Node[CHAIN_ID, RPC]) NodeSelector[CHAIN_ID, RPC] { + return &roundRobinSelector[CHAIN_ID, RPC]{ nodes: nodes, } } -func (s *roundRobinSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HEAD, RPC] { - var liveNodes []Node[CHAIN_ID, HEAD, RPC] +func (s *roundRobinSelector[CHAIN_ID, RPC]) Select() Node[CHAIN_ID, RPC] { + var liveNodes []Node[CHAIN_ID, RPC] for _, n := range s.nodes { if n.State() == nodeStateAlive { liveNodes = append(liveNodes, n) @@ -45,6 +43,6 @@ func (s *roundRobinSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HEAD, return liveNodes[idx] } -func (s *roundRobinSelector[CHAIN_ID, HEAD, RPC]) Name() string { +func (s *roundRobinSelector[CHAIN_ID, RPC]) Name() string { return NodeSelectionModeRoundRobin } diff --git a/common/client/node_selector_round_robin_test.go b/common/client/node_selector_round_robin_test.go index e5078d858f1..189b58da9ea 100644 --- a/common/client/node_selector_round_robin_test.go +++ b/common/client/node_selector_round_robin_test.go @@ -9,18 +9,18 @@ import ( ) func TestRoundRobinNodeSelectorName(t *testing.T) { - selector := newNodeSelector[types.ID, Head, NodeClient[types.ID, Head]](NodeSelectionModeRoundRobin, nil) + selector := newNodeSelector[types.ID, RPCClient[types.ID, Head]](NodeSelectionModeRoundRobin, nil) assert.Equal(t, selector.Name(), NodeSelectionModeRoundRobin) } func TestRoundRobinNodeSelector(t *testing.T) { t.Parallel() - type nodeClient NodeClient[types.ID, Head] - var nodes []Node[types.ID, Head, nodeClient] + type nodeClient RPCClient[types.ID, Head] + var nodes []Node[types.ID, nodeClient] for i := 0; i < 3; i++ { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) if i == 0 { // first node is out of sync node.On("State").Return(nodeStateOutOfSync) @@ -41,11 +41,11 @@ func TestRoundRobinNodeSelector(t *testing.T) { func TestRoundRobinNodeSelector_None(t *testing.T) { t.Parallel() - type nodeClient NodeClient[types.ID, Head] - var nodes []Node[types.ID, Head, nodeClient] + type nodeClient RPCClient[types.ID, Head] + var nodes []Node[types.ID, nodeClient] for i := 0; i < 3; i++ { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) if i == 0 { // first node is out of sync node.On("State").Return(nodeStateOutOfSync) diff --git a/common/client/node_selector_test.go b/common/client/node_selector_test.go index 226cb67168d..f652bfc50ad 100644 --- a/common/client/node_selector_test.go +++ b/common/client/node_selector_test.go @@ -12,7 +12,7 @@ func TestNodeSelector(t *testing.T) { // rest of the tests are located in specific node selectors tests t.Run("panics on unknown type", func(t *testing.T) { assert.Panics(t, func() { - _ = newNodeSelector[types.ID, Head, NodeClient[types.ID, Head]]("unknown", nil) + _ = newNodeSelector[types.ID, RPCClient[types.ID, Head]]("unknown", nil) }) }) } diff --git a/common/client/node_selector_total_difficulty.go b/common/client/node_selector_total_difficulty.go index 6b45e75528b..7defcd741ca 100644 --- a/common/client/node_selector_total_difficulty.go +++ b/common/client/node_selector_total_difficulty.go @@ -8,23 +8,21 @@ import ( type totalDifficultyNodeSelector[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], -] []Node[CHAIN_ID, HEAD, RPC] + RPC any, +] []Node[CHAIN_ID, RPC] func NewTotalDifficultyNodeSelector[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], -](nodes []Node[CHAIN_ID, HEAD, RPC]) NodeSelector[CHAIN_ID, HEAD, RPC] { - return totalDifficultyNodeSelector[CHAIN_ID, HEAD, RPC](nodes) + RPC any, +](nodes []Node[CHAIN_ID, RPC]) NodeSelector[CHAIN_ID, RPC] { + return totalDifficultyNodeSelector[CHAIN_ID, RPC](nodes) } -func (s totalDifficultyNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HEAD, RPC] { +func (s totalDifficultyNodeSelector[CHAIN_ID, RPC]) Select() Node[CHAIN_ID, RPC] { // NodeNoNewHeadsThreshold may not be enabled, in this case all nodes have td == nil var highestTD *big.Int - var nodes []Node[CHAIN_ID, HEAD, RPC] - var aliveNodes []Node[CHAIN_ID, HEAD, RPC] + var nodes []Node[CHAIN_ID, RPC] + var aliveNodes []Node[CHAIN_ID, RPC] for _, n := range s { state, currentChainInfo := n.StateAndLatest() @@ -50,6 +48,6 @@ func (s totalDifficultyNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID return firstOrHighestPriority(nodes) } -func (s totalDifficultyNodeSelector[CHAIN_ID, HEAD, RPC]) Name() string { +func (s totalDifficultyNodeSelector[CHAIN_ID, RPC]) Name() string { return NodeSelectionModeTotalDifficulty } diff --git a/common/client/node_selector_total_difficulty_test.go b/common/client/node_selector_total_difficulty_test.go index 0bc214918d7..ed45cfdf9ac 100644 --- a/common/client/node_selector_total_difficulty_test.go +++ b/common/client/node_selector_total_difficulty_test.go @@ -10,18 +10,18 @@ import ( ) func TestTotalDifficultyNodeSelectorName(t *testing.T) { - selector := newNodeSelector[types.ID, Head, NodeClient[types.ID, Head]](NodeSelectionModeTotalDifficulty, nil) + selector := newNodeSelector[types.ID, RPCClient[types.ID, Head]](NodeSelectionModeTotalDifficulty, nil) assert.Equal(t, selector.Name(), NodeSelectionModeTotalDifficulty) } func TestTotalDifficultyNodeSelector(t *testing.T) { t.Parallel() - type nodeClient NodeClient[types.ID, Head] - var nodes []Node[types.ID, Head, nodeClient] + type nodeClient RPCClient[types.ID, Head] + var nodes []Node[types.ID, nodeClient] for i := 0; i < 3; i++ { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) if i == 0 { // first node is out of sync node.On("StateAndLatest").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: -1}) @@ -40,7 +40,7 @@ func TestTotalDifficultyNodeSelector(t *testing.T) { assert.Same(t, nodes[2], selector.Select()) t.Run("stick to the same node", func(t *testing.T) { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) // fourth node is alive (same as 3rd) node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 2, TotalDifficulty: big.NewInt(8)}) node.On("Order").Maybe().Return(int32(1)) @@ -51,7 +51,7 @@ func TestTotalDifficultyNodeSelector(t *testing.T) { }) t.Run("another best node", func(t *testing.T) { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) // fifth node is alive (better than 3rd and 4th) node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(11)}) node.On("Order").Maybe().Return(int32(1)) @@ -62,13 +62,13 @@ func TestTotalDifficultyNodeSelector(t *testing.T) { }) t.Run("nodes never update latest block number", func(t *testing.T) { - node1 := newMockNode[types.ID, Head, nodeClient](t) + node1 := newMockNode[types.ID, nodeClient](t) node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: -1, TotalDifficulty: nil}) node1.On("Order").Maybe().Return(int32(1)) - node2 := newMockNode[types.ID, Head, nodeClient](t) + node2 := newMockNode[types.ID, nodeClient](t) node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: -1, TotalDifficulty: nil}) node2.On("Order").Maybe().Return(int32(1)) - nodes := []Node[types.ID, Head, nodeClient]{node1, node2} + nodes := []Node[types.ID, nodeClient]{node1, node2} selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes) assert.Same(t, node1, selector.Select()) @@ -78,11 +78,11 @@ func TestTotalDifficultyNodeSelector(t *testing.T) { func TestTotalDifficultyNodeSelector_None(t *testing.T) { t.Parallel() - type nodeClient NodeClient[types.ID, Head] - var nodes []Node[types.ID, Head, nodeClient] + type nodeClient RPCClient[types.ID, Head] + var nodes []Node[types.ID, nodeClient] for i := 0; i < 3; i++ { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) if i == 0 { // first node is out of sync node.On("StateAndLatest").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: -1, TotalDifficulty: nil}) @@ -100,12 +100,12 @@ func TestTotalDifficultyNodeSelector_None(t *testing.T) { func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { t.Parallel() - type nodeClient NodeClient[types.ID, Head] - var nodes []Node[types.ID, Head, nodeClient] + type nodeClient RPCClient[types.ID, Head] + var nodes []Node[types.ID, nodeClient] t.Run("same td and order", func(t *testing.T) { for i := 0; i < 3; i++ { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(10)}) node.On("Order").Return(int32(2)) nodes = append(nodes, node) @@ -116,61 +116,61 @@ func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { }) t.Run("same td but different order", func(t *testing.T) { - node1 := newMockNode[types.ID, Head, nodeClient](t) + node1 := newMockNode[types.ID, nodeClient](t) node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(10)}) node1.On("Order").Return(int32(3)) - node2 := newMockNode[types.ID, Head, nodeClient](t) + node2 := newMockNode[types.ID, nodeClient](t) node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(10)}) node2.On("Order").Return(int32(1)) - node3 := newMockNode[types.ID, Head, nodeClient](t) + node3 := newMockNode[types.ID, nodeClient](t) node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(10)}) node3.On("Order").Return(int32(2)) - nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3} + nodes := []Node[types.ID, nodeClient]{node1, node2, node3} selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes) //Should select the second node as it has the highest priority assert.Same(t, nodes[1], selector.Select()) }) t.Run("different td but same order", func(t *testing.T) { - node1 := newMockNode[types.ID, Head, nodeClient](t) + node1 := newMockNode[types.ID, nodeClient](t) node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(10)}) node1.On("Order").Maybe().Return(int32(3)) - node2 := newMockNode[types.ID, Head, nodeClient](t) + node2 := newMockNode[types.ID, nodeClient](t) node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(11)}) node2.On("Order").Maybe().Return(int32(3)) - node3 := newMockNode[types.ID, Head, nodeClient](t) + node3 := newMockNode[types.ID, nodeClient](t) node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(12)}) node3.On("Order").Return(int32(3)) - nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3} + nodes := []Node[types.ID, nodeClient]{node1, node2, node3} selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes) //Should select the third node as it has the highest td assert.Same(t, nodes[2], selector.Select()) }) t.Run("different head and different order", func(t *testing.T) { - node1 := newMockNode[types.ID, Head, nodeClient](t) + node1 := newMockNode[types.ID, nodeClient](t) node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(100)}) node1.On("Order").Maybe().Return(int32(4)) - node2 := newMockNode[types.ID, Head, nodeClient](t) + node2 := newMockNode[types.ID, nodeClient](t) node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(110)}) node2.On("Order").Maybe().Return(int32(5)) - node3 := newMockNode[types.ID, Head, nodeClient](t) + node3 := newMockNode[types.ID, nodeClient](t) node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(110)}) node3.On("Order").Maybe().Return(int32(1)) - node4 := newMockNode[types.ID, Head, nodeClient](t) + node4 := newMockNode[types.ID, nodeClient](t) node4.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(105)}) node4.On("Order").Maybe().Return(int32(2)) - nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3, node4} + nodes := []Node[types.ID, nodeClient]{node1, node2, node3, node4} selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes) //Should select the third node as it has the highest td and will win the priority tie-breaker assert.Same(t, nodes[2], selector.Select()) diff --git a/common/client/node_test.go b/common/client/node_test.go index 539964691c0..6d98c2d9ea4 100644 --- a/common/client/node_test.go +++ b/common/client/node_test.go @@ -60,7 +60,7 @@ func (n testNodeConfig) DeathDeclarationDelay() time.Duration { } type testNode struct { - *node[types.ID, Head, NodeClient[types.ID, Head]] + *node[types.ID, Head, RPCClient[types.ID, Head]] } type testNodeOpts struct { @@ -73,7 +73,7 @@ type testNodeOpts struct { id int chainID types.ID nodeOrder int32 - rpc *mockNodeClient[types.ID, Head] + rpc *mockRPCClient[types.ID, Head] chainFamily string } @@ -98,10 +98,10 @@ func newTestNode(t *testing.T, opts testNodeOpts) testNode { opts.id = 42 } - nodeI := NewNode[types.ID, Head, NodeClient[types.ID, Head]](opts.config, opts.chainConfig, opts.lggr, + nodeI := NewNode[types.ID, Head, RPCClient[types.ID, Head]](opts.config, opts.chainConfig, opts.lggr, opts.wsuri, opts.httpuri, opts.name, opts.id, opts.chainID, opts.nodeOrder, opts.rpc, opts.chainFamily) return testNode{ - nodeI.(*node[types.ID, Head, NodeClient[types.ID, Head]]), + nodeI.(*node[types.ID, Head, RPCClient[types.ID, Head]]), } } diff --git a/common/client/send_only_node.go b/common/client/send_only_node.go index 85e3a6fc2cc..0a1715fa191 100644 --- a/common/client/send_only_node.go +++ b/common/client/send_only_node.go @@ -17,13 +17,13 @@ type sendOnlyClient[ ] interface { Close() ChainID(context.Context) (CHAIN_ID, error) - DialHTTP() error + Dial(ctx context.Context) error } // SendOnlyNode represents one node used as a sendonly type SendOnlyNode[ CHAIN_ID types.ID, - RPC sendOnlyClient[CHAIN_ID], + RPC any, ] interface { // Start may attempt to connect to the node, but should only return error for misconfiguration - never for temporary errors. Start(context.Context) error @@ -97,7 +97,7 @@ func (s *sendOnlyNode[CHAIN_ID, RPC]) start(startCtx context.Context) { panic(fmt.Sprintf("cannot dial node with state %v", s.state)) } - err := s.rpc.DialHTTP() + err := s.rpc.Dial(startCtx) if err != nil { promPoolRPCNodeTransitionsToUnusable.WithLabelValues(s.chainID.String(), s.name).Inc() s.log.Errorw("Dial failed: SendOnly Node is unusable", "err", err) diff --git a/common/client/send_only_node_test.go b/common/client/send_only_node_test.go index 79f4bfd60e3..532946da48f 100644 --- a/common/client/send_only_node_test.go +++ b/common/client/send_only_node_test.go @@ -46,7 +46,7 @@ func TestStartSendOnlyNode(t *testing.T) { client := newMockSendOnlyClient[types.ID](t) client.On("Close").Once() expectedError := errors.New("some http error") - client.On("DialHTTP").Return(expectedError).Once() + client.On("Dial", mock.Anything).Return(expectedError).Once() s := NewSendOnlyNode(lggr, url.URL{}, t.Name(), types.RandomID(), client) defer func() { assert.NoError(t, s.Close()) }() @@ -61,7 +61,7 @@ func TestStartSendOnlyNode(t *testing.T) { lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) client := newMockSendOnlyClient[types.ID](t) client.On("Close").Once() - client.On("DialHTTP").Return(nil).Once() + client.On("Dial", mock.Anything).Return(nil).Once() s := NewSendOnlyNode(lggr, url.URL{}, t.Name(), types.NewIDFromInt(0), client) defer func() { assert.NoError(t, s.Close()) }() @@ -76,7 +76,7 @@ func TestStartSendOnlyNode(t *testing.T) { lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) client := newMockSendOnlyClient[types.ID](t) client.On("Close").Once() - client.On("DialHTTP").Return(nil) + client.On("Dial", mock.Anything).Return(nil) expectedError := errors.New("failed to get chain ID") chainID := types.RandomID() const failuresCount = 2 @@ -100,7 +100,7 @@ func TestStartSendOnlyNode(t *testing.T) { lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) client := newMockSendOnlyClient[types.ID](t) client.On("Close").Once() - client.On("DialHTTP").Return(nil).Once() + client.On("Dial", mock.Anything).Return(nil).Once() configuredChainID := types.NewIDFromInt(11) rpcChainID := types.NewIDFromInt(20) const failuresCount = 2 @@ -123,7 +123,7 @@ func TestStartSendOnlyNode(t *testing.T) { lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) client := newMockSendOnlyClient[types.ID](t) client.On("Close").Once() - client.On("DialHTTP").Return(nil).Once() + client.On("Dial", mock.Anything).Return(nil).Once() configuredChainID := types.RandomID() client.On("ChainID", mock.Anything).Return(configuredChainID, nil) s := NewSendOnlyNode(lggr, url.URL{}, t.Name(), configuredChainID, client) diff --git a/common/client/transaction_sender.go b/common/client/transaction_sender.go new file mode 100644 index 00000000000..9365a82b290 --- /dev/null +++ b/common/client/transaction_sender.go @@ -0,0 +1,280 @@ +package client + +import ( + "context" + "errors" + "fmt" + "math" + "slices" + "sync" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/common/types" +) + +var ( + // PromMultiNodeInvariantViolations reports violation of our assumptions + PromMultiNodeInvariantViolations = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "multi_node_invariant_violations", + Help: "The number of invariant violations", + }, []string{"network", "chainId", "invariant"}) +) + +// TxErrorClassifier - defines interface of a function that transforms raw RPC error into the SendTxReturnCode enum +// (e.g. Successful, Fatal, Retryable, etc.) +type TxErrorClassifier[TX any] func(tx TX, err error) SendTxReturnCode + +type sendTxResult struct { + Err error + ResultCode SendTxReturnCode +} + +const sendTxQuorum = 0.7 + +// SendTxRPCClient - defines interface of an RPC used by TransactionSender to broadcast transaction +type SendTxRPCClient[TX any] interface { + // SendTransaction errors returned should include name or other unique identifier of the RPC + SendTransaction(ctx context.Context, tx TX) error +} + +func NewTransactionSender[TX any, CHAIN_ID types.ID, RPC SendTxRPCClient[TX]]( + lggr logger.Logger, + chainID CHAIN_ID, + chainFamily string, + multiNode *MultiNode[CHAIN_ID, RPC], + txErrorClassifier TxErrorClassifier[TX], + sendTxSoftTimeout time.Duration, +) *TransactionSender[TX, CHAIN_ID, RPC] { + if sendTxSoftTimeout == 0 { + sendTxSoftTimeout = QueryTimeout / 2 + } + return &TransactionSender[TX, CHAIN_ID, RPC]{ + chainID: chainID, + chainFamily: chainFamily, + lggr: logger.Sugared(lggr).Named("TransactionSender").With("chainID", chainID.String()), + multiNode: multiNode, + txErrorClassifier: txErrorClassifier, + sendTxSoftTimeout: sendTxSoftTimeout, + chStop: make(services.StopChan), + } +} + +type TransactionSender[TX any, CHAIN_ID types.ID, RPC SendTxRPCClient[TX]] struct { + services.StateMachine + chainID CHAIN_ID + chainFamily string + lggr logger.SugaredLogger + multiNode *MultiNode[CHAIN_ID, RPC] + txErrorClassifier TxErrorClassifier[TX] + sendTxSoftTimeout time.Duration // defines max waiting time from first response til responses evaluation + + wg sync.WaitGroup // waits for all reporting goroutines to finish + chStop services.StopChan +} + +// SendTransaction - broadcasts transaction to all the send-only and primary nodes in MultiNode. +// A returned nil or error does not guarantee that the transaction will or won't be included. Additional checks must be +// performed to determine the final state. +// +// Send-only nodes' results are ignored as they tend to return false-positive responses. Broadcast to them is necessary +// to speed up the propagation of TX in the network. +// +// Handling of primary nodes' results consists of collection and aggregation. +// In the collection step, we gather as many results as possible while minimizing waiting time. This operation succeeds +// on one of the following conditions: +// * Received at least one success +// * Received at least one result and `sendTxSoftTimeout` expired +// * Received results from the sufficient number of nodes defined by sendTxQuorum. +// The aggregation is based on the following conditions: +// * If there is at least one success - returns success +// * If there is at least one terminal error - returns terminal error +// * If there is both success and terminal error - returns success and reports invariant violation +// * Otherwise, returns any (effectively random) of the errors. +func (txSender *TransactionSender[TX, CHAIN_ID, RPC]) SendTransaction(ctx context.Context, tx TX) (SendTxReturnCode, error) { + txResults := make(chan sendTxResult) + txResultsToReport := make(chan sendTxResult) + primaryNodeWg := sync.WaitGroup{} + + if txSender.State() != "Started" { + return Retryable, errors.New("TransactionSender not started") + } + + healthyNodesNum := 0 + err := txSender.multiNode.DoAll(ctx, func(ctx context.Context, rpc RPC, isSendOnly bool) { + if isSendOnly { + txSender.wg.Add(1) + go func() { + defer txSender.wg.Done() + // Send-only nodes' results are ignored as they tend to return false-positive responses. + // Broadcast to them is necessary to speed up the propagation of TX in the network. + _ = txSender.broadcastTxAsync(ctx, rpc, tx) + }() + return + } + + // Primary Nodes + healthyNodesNum++ + primaryNodeWg.Add(1) + go func() { + defer primaryNodeWg.Done() + result := txSender.broadcastTxAsync(ctx, rpc, tx) + select { + case <-ctx.Done(): + return + case txResults <- result: + } + + select { + case <-ctx.Done(): + return + case txResultsToReport <- result: + } + }() + }) + + // This needs to be done in parallel so the reporting knows when it's done (when the channel is closed) + txSender.wg.Add(1) + go func() { + defer txSender.wg.Done() + primaryNodeWg.Wait() + close(txResultsToReport) + close(txResults) + }() + + if err != nil { + return Retryable, err + } + + txSender.wg.Add(1) + go txSender.reportSendTxAnomalies(tx, txResultsToReport) + + return txSender.collectTxResults(ctx, tx, healthyNodesNum, txResults) +} + +func (txSender *TransactionSender[TX, CHAIN_ID, RPC]) broadcastTxAsync(ctx context.Context, rpc RPC, tx TX) sendTxResult { + txErr := rpc.SendTransaction(ctx, tx) + txSender.lggr.Debugw("Node sent transaction", "tx", tx, "err", txErr) + resultCode := txSender.txErrorClassifier(tx, txErr) + if !slices.Contains(sendTxSuccessfulCodes, resultCode) { + txSender.lggr.Warnw("RPC returned error", "tx", tx, "err", txErr) + } + return sendTxResult{Err: txErr, ResultCode: resultCode} +} + +func (txSender *TransactionSender[TX, CHAIN_ID, RPC]) reportSendTxAnomalies(tx TX, txResults <-chan sendTxResult) { + defer txSender.wg.Done() + resultsByCode := sendTxResults{} + // txResults eventually will be closed + for txResult := range txResults { + resultsByCode[txResult.ResultCode] = append(resultsByCode[txResult.ResultCode], txResult.Err) + } + + _, _, criticalErr := aggregateTxResults(resultsByCode) + if criticalErr != nil { + txSender.lggr.Criticalw("observed invariant violation on SendTransaction", "tx", tx, "resultsByCode", resultsByCode, "err", criticalErr) + PromMultiNodeInvariantViolations.WithLabelValues(txSender.chainFamily, txSender.chainID.String(), criticalErr.Error()).Inc() + } +} + +type sendTxResults map[SendTxReturnCode][]error + +func aggregateTxResults(resultsByCode sendTxResults) (returnCode SendTxReturnCode, txResult error, err error) { + severeCode, severeErrors, hasSevereErrors := findFirstIn(resultsByCode, sendTxSevereErrors) + successCode, successResults, hasSuccess := findFirstIn(resultsByCode, sendTxSuccessfulCodes) + if hasSuccess { + // We assume that primary node would never report false positive txResult for a transaction. + // Thus, if such case occurs it's probably due to misconfiguration or a bug and requires manual intervention. + if hasSevereErrors { + const errMsg = "found contradictions in nodes replies on SendTransaction: got success and severe error" + // return success, since at least 1 node has accepted our broadcasted Tx, and thus it can now be included onchain + return successCode, successResults[0], errors.New(errMsg) + } + + // other errors are temporary - we are safe to return success + return successCode, successResults[0], nil + } + + if hasSevereErrors { + return severeCode, severeErrors[0], nil + } + + // return temporary error + for code, result := range resultsByCode { + return code, result[0], nil + } + + err = fmt.Errorf("expected at least one response on SendTransaction") + return Retryable, err, err +} + +func (txSender *TransactionSender[TX, CHAIN_ID, RPC]) collectTxResults(ctx context.Context, tx TX, healthyNodesNum int, txResults <-chan sendTxResult) (SendTxReturnCode, error) { + if healthyNodesNum == 0 { + return Retryable, ErroringNodeError + } + ctx, cancel := txSender.chStop.Ctx(ctx) + defer cancel() + requiredResults := int(math.Ceil(float64(healthyNodesNum) * sendTxQuorum)) + errorsByCode := sendTxResults{} + var softTimeoutChan <-chan time.Time + var resultsCount int +loop: + for { + select { + case <-ctx.Done(): + txSender.lggr.Debugw("Failed to collect of the results before context was done", "tx", tx, "errorsByCode", errorsByCode) + return Retryable, ctx.Err() + case result := <-txResults: + errorsByCode[result.ResultCode] = append(errorsByCode[result.ResultCode], result.Err) + resultsCount++ + if slices.Contains(sendTxSuccessfulCodes, result.ResultCode) || resultsCount >= requiredResults { + break loop + } + case <-softTimeoutChan: + txSender.lggr.Debugw("Send Tx soft timeout expired - returning responses we've collected so far", "tx", tx, "resultsCount", resultsCount, "requiredResults", requiredResults) + break loop + } + + if softTimeoutChan == nil { + tm := time.NewTimer(txSender.sendTxSoftTimeout) + softTimeoutChan = tm.C + // we are fine with stopping timer at the end of function + //nolint + defer tm.Stop() + } + } + + // ignore critical error as it's reported in reportSendTxAnomalies + returnCode, result, _ := aggregateTxResults(errorsByCode) + return returnCode, result +} + +func (txSender *TransactionSender[TX, CHAIN_ID, RPC]) Start(ctx context.Context) error { + return txSender.StartOnce("TransactionSender", func() error { + return nil + }) +} + +func (txSender *TransactionSender[TX, CHAIN_ID, RPC]) Close() error { + return txSender.StopOnce("TransactionSender", func() error { + close(txSender.chStop) + txSender.wg.Wait() + return nil + }) +} + +// findFirstIn - returns the first existing key and value for the slice of keys +func findFirstIn[K comparable, V any](set map[K]V, keys []K) (K, V, bool) { + for _, k := range keys { + if v, ok := set[k]; ok { + return k, v, true + } + } + var zeroK K + var zeroV V + return zeroK, zeroV, false +} diff --git a/common/client/transaction_sender_test.go b/common/client/transaction_sender_test.go new file mode 100644 index 00000000000..5517a0c8dda --- /dev/null +++ b/common/client/transaction_sender_test.go @@ -0,0 +1,384 @@ +package client + +import ( + "context" + "fmt" + "testing" + + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink/v2/common/types" +) + +type sendTxMultiNode struct { + *MultiNode[types.ID, SendTxRPCClient[any]] +} + +type sendTxRPC struct { + sendTxRun func(args mock.Arguments) + sendTxErr error +} + +var _ SendTxRPCClient[any] = (*sendTxRPC)(nil) + +func newSendTxRPC(sendTxErr error, sendTxRun func(args mock.Arguments)) *sendTxRPC { + return &sendTxRPC{sendTxErr: sendTxErr, sendTxRun: sendTxRun} +} + +func (rpc *sendTxRPC) SendTransaction(ctx context.Context, _ any) error { + if rpc.sendTxRun != nil { + rpc.sendTxRun(mock.Arguments{ctx}) + } + return rpc.sendTxErr +} + +func newTestTransactionSender(t *testing.T, chainID types.ID, lggr logger.Logger, + nodes []Node[types.ID, SendTxRPCClient[any]], + sendOnlyNodes []SendOnlyNode[types.ID, SendTxRPCClient[any]], +) (*sendTxMultiNode, *TransactionSender[any, types.ID, SendTxRPCClient[any]]) { + mn := sendTxMultiNode{NewMultiNode[types.ID, SendTxRPCClient[any]]( + lggr, NodeSelectionModeRoundRobin, 0, nodes, sendOnlyNodes, chainID, "chainFamily", 0)} + err := mn.StartOnce("startedTestMultiNode", func() error { return nil }) + require.NoError(t, err) + + txSender := NewTransactionSender[any, types.ID, SendTxRPCClient[any]](lggr, chainID, mn.chainFamily, mn.MultiNode, classifySendTxError, tests.TestInterval) + err = txSender.Start(tests.Context(t)) + require.NoError(t, err) + + t.Cleanup(func() { + err := mn.Close() + if err != nil { + // Allow MultiNode to be closed early for testing + require.EqualError(t, err, "MultiNode has already been stopped: already stopped") + } + err = txSender.Close() + if err != nil { + // Allow TransactionSender to be closed early for testing + require.EqualError(t, err, "TransactionSender has already been stopped: already stopped") + } + }) + return &mn, txSender +} + +func classifySendTxError(_ any, err error) SendTxReturnCode { + if err != nil { + return Fatal + } + return Successful +} + +func TestTransactionSender_SendTransaction(t *testing.T) { + t.Parallel() + + newNodeWithState := func(t *testing.T, state nodeState, txErr error, sendTxRun func(args mock.Arguments)) *mockNode[types.ID, SendTxRPCClient[any]] { + rpc := newSendTxRPC(txErr, sendTxRun) + node := newMockNode[types.ID, SendTxRPCClient[any]](t) + node.On("String").Return("node name").Maybe() + node.On("RPC").Return(rpc).Maybe() + node.On("State").Return(state).Maybe() + node.On("Close").Return(nil).Once() + return node + } + + newNode := func(t *testing.T, txErr error, sendTxRun func(args mock.Arguments)) *mockNode[types.ID, SendTxRPCClient[any]] { + return newNodeWithState(t, nodeStateAlive, txErr, sendTxRun) + } + + t.Run("Fails if there is no nodes available", func(t *testing.T) { + lggr, _ := logger.TestObserved(t, zap.DebugLevel) + _, txSender := newTestTransactionSender(t, types.RandomID(), lggr, nil, nil) + _, err := txSender.SendTransaction(tests.Context(t), nil) + assert.EqualError(t, err, ErroringNodeError.Error()) + }) + + t.Run("Transaction failure happy path", func(t *testing.T) { + expectedError := errors.New("transaction failed") + mainNode := newNode(t, expectedError, nil) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) + + _, txSender := newTestTransactionSender(t, types.RandomID(), lggr, + []Node[types.ID, SendTxRPCClient[any]]{mainNode}, + []SendOnlyNode[types.ID, SendTxRPCClient[any]]{newNode(t, errors.New("unexpected error"), nil)}) + + result, sendErr := txSender.SendTransaction(tests.Context(t), nil) + require.ErrorIs(t, sendErr, expectedError) + require.Equal(t, Fatal, result) + tests.AssertLogCountEventually(t, observedLogs, "Node sent transaction", 2) + tests.AssertLogCountEventually(t, observedLogs, "RPC returned error", 2) + }) + + t.Run("Transaction success happy path", func(t *testing.T) { + mainNode := newNode(t, nil, nil) + + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) + _, txSender := newTestTransactionSender(t, types.RandomID(), lggr, + []Node[types.ID, SendTxRPCClient[any]]{mainNode}, + []SendOnlyNode[types.ID, SendTxRPCClient[any]]{newNode(t, errors.New("unexpected error"), nil)}) + + result, sendErr := txSender.SendTransaction(tests.Context(t), nil) + require.NoError(t, sendErr) + require.Equal(t, Successful, result) + tests.AssertLogCountEventually(t, observedLogs, "Node sent transaction", 2) + tests.AssertLogCountEventually(t, observedLogs, "RPC returned error", 1) + }) + + t.Run("Context expired before collecting sufficient results", func(t *testing.T) { + testContext, testCancel := context.WithCancel(tests.Context(t)) + defer testCancel() + + mainNode := newNode(t, nil, func(_ mock.Arguments) { + // block caller til end of the test + <-testContext.Done() + }) + + lggr, _ := logger.TestObserved(t, zap.DebugLevel) + + _, txSender := newTestTransactionSender(t, types.RandomID(), lggr, + []Node[types.ID, SendTxRPCClient[any]]{mainNode}, nil) + + requestContext, cancel := context.WithCancel(tests.Context(t)) + cancel() + _, sendErr := txSender.SendTransaction(requestContext, nil) + require.EqualError(t, sendErr, "context canceled") + }) + + t.Run("Soft timeout stops results collection", func(t *testing.T) { + chainID := types.RandomID() + expectedError := errors.New("transaction failed") + fastNode := newNode(t, expectedError, nil) + + // hold reply from the node till end of the test + testContext, testCancel := context.WithCancel(tests.Context(t)) + defer testCancel() + slowNode := newNode(t, errors.New("transaction failed"), func(_ mock.Arguments) { + // block caller til end of the test + <-testContext.Done() + }) + + lggr, _ := logger.TestObserved(t, zap.DebugLevel) + + _, txSender := newTestTransactionSender(t, chainID, lggr, []Node[types.ID, SendTxRPCClient[any]]{fastNode, slowNode}, nil) + _, sendErr := txSender.SendTransaction(tests.Context(t), nil) + require.EqualError(t, sendErr, expectedError.Error()) + }) + t.Run("Returns success without waiting for the rest of the nodes", func(t *testing.T) { + chainID := types.RandomID() + fastNode := newNode(t, nil, nil) + // hold reply from the node till end of the test + testContext, testCancel := context.WithCancel(tests.Context(t)) + defer testCancel() + slowNode := newNode(t, errors.New("transaction failed"), func(_ mock.Arguments) { + // block caller til end of the test + <-testContext.Done() + }) + slowSendOnly := newNode(t, errors.New("send only failed"), func(_ mock.Arguments) { + // block caller til end of the test + <-testContext.Done() + }) + lggr, _ := logger.TestObserved(t, zap.WarnLevel) + mn, txSender := newTestTransactionSender(t, chainID, lggr, + []Node[types.ID, SendTxRPCClient[any]]{fastNode, slowNode}, + []SendOnlyNode[types.ID, SendTxRPCClient[any]]{slowSendOnly}) + + rtnCode, err := txSender.SendTransaction(tests.Context(t), nil) + require.NoError(t, err) + require.Equal(t, Successful, rtnCode) + require.NoError(t, mn.Close()) + }) + t.Run("Fails when multinode is closed", func(t *testing.T) { + chainID := types.RandomID() + fastNode := newNode(t, nil, nil) + // hold reply from the node till end of the test + testContext, testCancel := context.WithCancel(tests.Context(t)) + defer testCancel() + slowNode := newNode(t, errors.New("transaction failed"), func(_ mock.Arguments) { + // block caller til end of the test + <-testContext.Done() + }) + slowSendOnly := newNode(t, errors.New("send only failed"), func(_ mock.Arguments) { + // block caller til end of the test + <-testContext.Done() + }) + + lggr, _ := logger.TestObserved(t, zap.DebugLevel) + + mn, txSender := newTestTransactionSender(t, chainID, lggr, + []Node[types.ID, SendTxRPCClient[any]]{fastNode, slowNode}, + []SendOnlyNode[types.ID, SendTxRPCClient[any]]{slowSendOnly}) + + require.NoError(t, mn.Close()) + _, err := txSender.SendTransaction(tests.Context(t), nil) + require.EqualError(t, err, "MultiNode is stopped") + }) + t.Run("Fails when closed", func(t *testing.T) { + chainID := types.RandomID() + fastNode := newNode(t, nil, nil) + // hold reply from the node till end of the test + testContext, testCancel := context.WithCancel(tests.Context(t)) + defer testCancel() + slowNode := newNode(t, errors.New("transaction failed"), func(_ mock.Arguments) { + // block caller til end of the test + <-testContext.Done() + }) + slowSendOnly := newNode(t, errors.New("send only failed"), func(_ mock.Arguments) { + // block caller til end of the test + <-testContext.Done() + }) + + lggr, _ := logger.TestObserved(t, zap.DebugLevel) + + _, txSender := newTestTransactionSender(t, chainID, lggr, + []Node[types.ID, SendTxRPCClient[any]]{fastNode, slowNode}, + []SendOnlyNode[types.ID, SendTxRPCClient[any]]{slowSendOnly}) + + require.NoError(t, txSender.Close()) + _, err := txSender.SendTransaction(tests.Context(t), nil) + require.EqualError(t, err, "TransactionSender not started") + }) + t.Run("Returns error if there is no healthy primary nodes", func(t *testing.T) { + chainID := types.RandomID() + primary := newNodeWithState(t, nodeStateUnreachable, nil, nil) + sendOnly := newNodeWithState(t, nodeStateUnreachable, nil, nil) + + lggr, _ := logger.TestObserved(t, zap.DebugLevel) + + _, txSender := newTestTransactionSender(t, chainID, lggr, + []Node[types.ID, SendTxRPCClient[any]]{primary}, + []SendOnlyNode[types.ID, SendTxRPCClient[any]]{sendOnly}) + + _, sendErr := txSender.SendTransaction(tests.Context(t), nil) + assert.EqualError(t, sendErr, ErroringNodeError.Error()) + }) + + t.Run("Transaction success even if one of the nodes is unhealthy", func(t *testing.T) { + chainID := types.RandomID() + mainNode := newNode(t, nil, nil) + unexpectedCall := func(args mock.Arguments) { + panic("SendTx must not be called for unhealthy node") + } + unhealthyNode := newNodeWithState(t, nodeStateUnreachable, nil, unexpectedCall) + unhealthySendOnlyNode := newNodeWithState(t, nodeStateUnreachable, nil, unexpectedCall) + + lggr, _ := logger.TestObserved(t, zap.DebugLevel) + + _, txSender := newTestTransactionSender(t, chainID, lggr, + []Node[types.ID, SendTxRPCClient[any]]{mainNode, unhealthyNode}, + []SendOnlyNode[types.ID, SendTxRPCClient[any]]{unhealthySendOnlyNode}) + + returnCode, sendErr := txSender.SendTransaction(tests.Context(t), nil) + require.NoError(t, sendErr) + require.Equal(t, Successful, returnCode) + }) +} + +func TestTransactionSender_SendTransaction_aggregateTxResults(t *testing.T) { + t.Parallel() + // ensure failure on new SendTxReturnCode + codesToCover := map[SendTxReturnCode]struct{}{} + for code := Successful; code < sendTxReturnCodeLen; code++ { + codesToCover[code] = struct{}{} + } + + testCases := []struct { + Name string + ExpectedTxResult string + ExpectedCriticalErr string + ResultsByCode sendTxResults + }{ + { + Name: "Returns success and logs critical error on success and Fatal", + ExpectedTxResult: "success", + ExpectedCriticalErr: "found contradictions in nodes replies on SendTransaction: got success and severe error", + ResultsByCode: sendTxResults{ + Successful: {errors.New("success")}, + Fatal: {errors.New("fatal")}, + }, + }, + { + Name: "Returns TransactionAlreadyKnown and logs critical error on TransactionAlreadyKnown and Fatal", + ExpectedTxResult: "tx_already_known", + ExpectedCriticalErr: "found contradictions in nodes replies on SendTransaction: got success and severe error", + ResultsByCode: sendTxResults{ + TransactionAlreadyKnown: {errors.New("tx_already_known")}, + Unsupported: {errors.New("unsupported")}, + }, + }, + { + Name: "Prefers sever error to temporary", + ExpectedTxResult: "underpriced", + ExpectedCriticalErr: "", + ResultsByCode: sendTxResults{ + Retryable: {errors.New("retryable")}, + Underpriced: {errors.New("underpriced")}, + }, + }, + { + Name: "Returns temporary error", + ExpectedTxResult: "retryable", + ExpectedCriticalErr: "", + ResultsByCode: sendTxResults{ + Retryable: {errors.New("retryable")}, + }, + }, + { + Name: "Insufficient funds is treated as error", + ExpectedTxResult: "", + ExpectedCriticalErr: "", + ResultsByCode: sendTxResults{ + Successful: {nil}, + InsufficientFunds: {errors.New("insufficientFunds")}, + }, + }, + { + Name: "Logs critical error on empty ResultsByCode", + ExpectedTxResult: "expected at least one response on SendTransaction", + ExpectedCriticalErr: "expected at least one response on SendTransaction", + ResultsByCode: sendTxResults{}, + }, + { + Name: "Zk terminally stuck", + ExpectedTxResult: "not enough keccak counters to continue the execution", + ExpectedCriticalErr: "", + ResultsByCode: sendTxResults{ + TerminallyStuck: {errors.New("not enough keccak counters to continue the execution")}, + }, + }, + } + + for _, testCase := range testCases { + for code := range testCase.ResultsByCode { + delete(codesToCover, code) + } + + t.Run(testCase.Name, func(t *testing.T) { + _, txResult, err := aggregateTxResults(testCase.ResultsByCode) + if testCase.ExpectedTxResult == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, txResult, testCase.ExpectedTxResult) + } + + logger.Sugared(logger.Test(t)).Info("Map: " + fmt.Sprint(testCase.ResultsByCode)) + logger.Sugared(logger.Test(t)).Criticalw("observed invariant violation on SendTransaction", "resultsByCode", testCase.ResultsByCode, "err", err) + + if testCase.ExpectedCriticalErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, testCase.ExpectedCriticalErr) + } + }) + } + + // explicitly signal that following codes are properly handled in aggregateTxResults, + // but dedicated test cases won't be beneficial + for _, codeToIgnore := range []SendTxReturnCode{Unknown, ExceedsMaxFee, FeeOutOfValidRange} { + delete(codesToCover, codeToIgnore) + } + assert.Empty(t, codesToCover, "all of the SendTxReturnCode must be covered by this test") +} diff --git a/common/client/types.go b/common/client/types.go index c9b6a3580eb..38880397442 100644 --- a/common/client/types.go +++ b/common/client/types.go @@ -4,71 +4,30 @@ import ( "context" "math/big" - "github.com/smartcontractkit/chainlink-common/pkg/assets" - - feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/common/types" ) -// RPC includes all the necessary methods for a multi-node client to interact directly with any RPC endpoint. -type RPC[ - CHAIN_ID types.ID, - SEQ types.Sequence, - ADDR types.Hashable, - BLOCK_HASH types.Hashable, - TX any, - TX_HASH types.Hashable, - EVENT any, - EVENT_OPS any, - TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], - FEE feetypes.Fee, - HEAD types.Head[BLOCK_HASH], - BATCH_ELEM any, -] interface { - NodeClient[ - CHAIN_ID, - HEAD, - ] - clientAPI[ - CHAIN_ID, - SEQ, - ADDR, - BLOCK_HASH, - TX, - TX_HASH, - EVENT, - EVENT_OPS, - TX_RECEIPT, - FEE, - HEAD, - BATCH_ELEM, - ] -} - -// Head is the interface required by the NodeClient -type Head interface { - BlockNumber() int64 - BlockDifficulty() *big.Int - IsValid() bool -} - -// NodeClient includes all the necessary RPC methods required by a node. -type NodeClient[ +// RPCClient includes all the necessary generalized RPC methods used by Node to perform health checks +type RPCClient[ CHAIN_ID types.ID, HEAD Head, ] interface { - connection[CHAIN_ID, HEAD] - - DialHTTP() error - // DisconnectAll - cancels all inflight requests, terminates all subscriptions and resets latest ChainInfo. - DisconnectAll() - Close() - ClientVersion(context.Context) (string, error) - SubscribersCount() int32 - SetAliveLoopSub(types.Subscription) - UnsubscribeAllExceptAliveLoop() + // ChainID - fetches ChainID from the RPC to verify that it matches config + ChainID(ctx context.Context) (CHAIN_ID, error) + // Dial - prepares the RPC for usage. Can be called on fresh or closed RPC + Dial(ctx context.Context) error + // SubscribeToHeads - returns channel and subscription for new heads. + SubscribeToHeads(ctx context.Context) (<-chan HEAD, types.Subscription, error) + // SubscribeToFinalizedHeads - returns channel and subscription for finalized heads. + SubscribeToFinalizedHeads(ctx context.Context) (<-chan HEAD, types.Subscription, error) + // Ping - returns error if RPC is not reachable + Ping(context.Context) error + // IsSyncing - returns true if the RPC is in Syncing state and can not process calls IsSyncing(ctx context.Context) (bool, error) - SubscribeToFinalizedHeads(_ context.Context) (<-chan HEAD, types.Subscription, error) + // UnsubscribeAllExcept - close all subscriptions except `subs` + UnsubscribeAllExcept(subs ...types.Subscription) + // Close - closes all subscriptions and aborts all RPC calls + Close() // GetInterceptedChainInfo - returns latest and highest observed by application layer ChainInfo. // latest ChainInfo is the most recent value received within a NodeClient's current lifecycle between Dial and DisconnectAll. // highestUserObservations ChainInfo is the highest ChainInfo observed excluding health checks calls. @@ -82,78 +41,11 @@ type NodeClient[ GetInterceptedChainInfo() (latest, highestUserObservations ChainInfo) } -// clientAPI includes all the direct RPC methods required by the generalized common client to implement its own. -type clientAPI[ - CHAIN_ID types.ID, - SEQ types.Sequence, - ADDR types.Hashable, - BLOCK_HASH types.Hashable, - TX any, - TX_HASH types.Hashable, - EVENT any, - EVENT_OPS any, // event filter query options - TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], - FEE feetypes.Fee, - HEAD types.Head[BLOCK_HASH], - BATCH_ELEM any, -] interface { - connection[CHAIN_ID, HEAD] - - // Account - BalanceAt(ctx context.Context, accountAddress ADDR, blockNumber *big.Int) (*big.Int, error) - TokenBalance(ctx context.Context, accountAddress ADDR, tokenAddress ADDR) (*big.Int, error) - SequenceAt(ctx context.Context, accountAddress ADDR, blockNumber *big.Int) (SEQ, error) - LINKBalance(ctx context.Context, accountAddress ADDR, linkAddress ADDR) (*assets.Link, error) - PendingSequenceAt(ctx context.Context, addr ADDR) (SEQ, error) - EstimateGas(ctx context.Context, call any) (gas uint64, err error) - - // Transactions - SendTransaction(ctx context.Context, tx TX) error - SimulateTransaction(ctx context.Context, tx TX) error - TransactionByHash(ctx context.Context, txHash TX_HASH) (TX, error) - TransactionReceipt(ctx context.Context, txHash TX_HASH) (TX_RECEIPT, error) - SendEmptyTransaction( - ctx context.Context, - newTxAttempt func(seq SEQ, feeLimit uint32, fee FEE, fromAddress ADDR) (attempt any, err error), - seq SEQ, - gasLimit uint32, - fee FEE, - fromAddress ADDR, - ) (txhash string, err error) - - // Blocks - BlockByNumber(ctx context.Context, number *big.Int) (HEAD, error) - BlockByHash(ctx context.Context, hash BLOCK_HASH) (HEAD, error) - LatestBlockHeight(context.Context) (*big.Int, error) - LatestFinalizedBlock(ctx context.Context) (HEAD, error) - - // Events - FilterEvents(ctx context.Context, query EVENT_OPS) ([]EVENT, error) - - // Misc - BatchCallContext(ctx context.Context, b []BATCH_ELEM) error - CallContract( - ctx context.Context, - msg interface{}, - blockNumber *big.Int, - ) (rpcErr []byte, extractErr error) - PendingCallContract( - ctx context.Context, - msg interface{}, - ) (rpcErr []byte, extractErr error) - CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error - CodeAt(ctx context.Context, account ADDR, blockNumber *big.Int) ([]byte, error) -} - -type connection[ - CHAIN_ID types.ID, - HEAD Head, -] interface { - ChainID(ctx context.Context) (CHAIN_ID, error) - Dial(ctx context.Context) error - SubscribeToHeads(ctx context.Context) (ch <-chan HEAD, sub types.Subscription, err error) - // TODO: remove as part of merge with BCI-2875 - SubscribeNewHead(ctx context.Context, channel chan<- HEAD) (s types.Subscription, err error) +// Head is the interface required by the NodeClient +type Head interface { + BlockNumber() int64 + BlockDifficulty() *big.Int + IsValid() bool } // PoolChainInfoProvider - provides aggregation of nodes pool ChainInfo diff --git a/common/headtracker/head_listener.go b/common/headtracker/head_listener.go index d240caab3c3..5f967706e04 100644 --- a/common/headtracker/head_listener.go +++ b/common/headtracker/head_listener.go @@ -62,7 +62,7 @@ type headListener[ client htrktypes.Client[HTH, S, ID, BLOCK_HASH] onSubscription func(context.Context) handleNewHead HeadHandler[HTH, BLOCK_HASH] - chHeaders chan HTH + chHeaders <-chan HTH headSubscription types.Subscription connected atomic.Bool receivingHeads atomic.Bool @@ -227,13 +227,10 @@ func (hl *headListener[HTH, S, ID, BLOCK_HASH]) subscribe(ctx context.Context) b } func (hl *headListener[HTH, S, ID, BLOCK_HASH]) subscribeToHead(ctx context.Context) error { - hl.chHeaders = make(chan HTH) - var err error - hl.headSubscription, err = hl.client.SubscribeNewHead(ctx, hl.chHeaders) + hl.chHeaders, hl.headSubscription, err = hl.client.SubscribeToHeads(ctx) if err != nil { - close(hl.chHeaders) - return fmt.Errorf("Client#SubscribeNewHead: %w", err) + return fmt.Errorf("Client#SubscribeToHeads: %w", err) } hl.connected.Store(true) diff --git a/common/headtracker/types/client.go b/common/headtracker/types/client.go index a1e419809b5..e1b7aad08df 100644 --- a/common/headtracker/types/client.go +++ b/common/headtracker/types/client.go @@ -12,9 +12,9 @@ type Client[H types.Head[BLOCK_HASH], S types.Subscription, ID types.ID, BLOCK_H HeadByHash(ctx context.Context, hash BLOCK_HASH) (head H, err error) // ConfiguredChainID returns the chain ID that the node is configured to connect to ConfiguredChainID() (id ID) - // SubscribeNewHead is the method in which the client receives new Head. + // SubscribeToHeads is the method in which the client receives new Head. // It can be implemented differently for each chain i.e websocket, polling, etc - SubscribeNewHead(ctx context.Context, ch chan<- H) (S, error) + SubscribeToHeads(ctx context.Context) (<-chan H, S, error) // LatestFinalizedBlock - returns the latest block that was marked as finalized LatestFinalizedBlock(ctx context.Context) (head H, err error) } diff --git a/common/types/subscription.go b/common/types/subscription.go index b9b44e41943..3c4fd4c39f1 100644 --- a/common/types/subscription.go +++ b/common/types/subscription.go @@ -5,7 +5,8 @@ package types // This is a generic interface for Subscription to represent used by clients. type Subscription interface { // Unsubscribe cancels the sending of events to the data channel - // and closes the error channel. + // and closes the error channel. Unsubscribe should be callable multiple + // times without causing an error. Unsubscribe() // Err returns the subscription error channel. The error channel receives // a value if there is an issue with the subscription (e.g. the network connection diff --git a/core/chains/evm/client/chain_client.go b/core/chains/evm/client/chain_client.go index d5c9cd32bce..6334fb78423 100644 --- a/core/chains/evm/client/chain_client.go +++ b/core/chains/evm/client/chain_client.go @@ -3,6 +3,7 @@ package client import ( "context" "math/big" + "sync" "time" "github.com/ethereum/go-ethereum" @@ -12,9 +13,7 @@ import ( commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink-common/pkg/logger" - commonclient "github.com/smartcontractkit/chainlink/v2/common/client" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -30,8 +29,6 @@ type Client interface { Close() // ChainID locally stored for quick access ConfiguredChainID() *big.Int - // ChainID RPC call - ChainID() (*big.Int, error) // NodeStates returns a map of node Name->node state // It might be nil or empty, e.g. for mock clients etc @@ -56,7 +53,7 @@ type Client interface { // correct hash from the RPC response. HeadByNumber(ctx context.Context, n *big.Int) (*evmtypes.Head, error) HeadByHash(ctx context.Context, n common.Hash) (*evmtypes.Head, error) - SubscribeNewHead(ctx context.Context, ch chan<- *evmtypes.Head) (ethereum.Subscription, error) + SubscribeToHeads(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) // LatestFinalizedBlock - returns the latest finalized block as it's returned from an RPC. // CAUTION: Using this method might cause local finality violations. It's highly recommended // to use HeadTracker to get latest finalized block. @@ -103,21 +100,11 @@ func ContextWithDefaultTimeout() (ctx context.Context, cancel context.CancelFunc } type chainClient struct { - multiNode commonclient.MultiNode[ + multiNode *commonclient.MultiNode[ *big.Int, - evmtypes.Nonce, - common.Address, - common.Hash, - *types.Transaction, - common.Hash, - types.Log, - ethereum.FilterQuery, - *evmtypes.Receipt, - *assets.Wei, - *evmtypes.Head, - RPCClient, - rpc.BatchElem, + *RPCClient, ] + txSender *commonclient.TransactionSender[*types.Transaction, *big.Int, *RPCClient] logger logger.SugaredLogger chainType chaintype.ChainType clientErrors evmconfig.ClientErrors @@ -127,31 +114,41 @@ func NewChainClient( lggr logger.Logger, selectionMode string, leaseDuration time.Duration, - noNewHeadsThreshold time.Duration, - nodes []commonclient.Node[*big.Int, *evmtypes.Head, RPCClient], - sendonlys []commonclient.SendOnlyNode[*big.Int, RPCClient], + nodes []commonclient.Node[*big.Int, *RPCClient], + sendonlys []commonclient.SendOnlyNode[*big.Int, *RPCClient], chainID *big.Int, - chainType chaintype.ChainType, clientErrors evmconfig.ClientErrors, deathDeclarationDelay time.Duration, + chainType chaintype.ChainType, ) Client { - multiNode := commonclient.NewMultiNode( + chainFamily := "EVM" + multiNode := commonclient.NewMultiNode[*big.Int, *RPCClient]( lggr, selectionMode, leaseDuration, - noNewHeadsThreshold, nodes, sendonlys, chainID, - "EVM", - func(tx *types.Transaction, err error) commonclient.SendTxReturnCode { - return ClassifySendError(err, clientErrors, logger.Sugared(logger.Nop()), tx, common.Address{}, chainType.IsL2()) - }, - 0, // use the default value provided by the implementation + chainFamily, deathDeclarationDelay, ) + + classifySendError := func(tx *types.Transaction, err error) commonclient.SendTxReturnCode { + return ClassifySendError(err, clientErrors, logger.Sugared(logger.Nop()), tx, common.Address{}, chainType.IsL2()) + } + + txSender := commonclient.NewTransactionSender[*types.Transaction, *big.Int, *RPCClient]( + lggr, + chainID, + chainFamily, + multiNode, + classifySendError, + 0, // use the default value provided by the implementation + ) + return &chainClient{ multiNode: multiNode, + txSender: txSender, logger: logger.Sugared(lggr), chainType: chainType, clientErrors: clientErrors, @@ -159,7 +156,11 @@ func NewChainClient( } func (c *chainClient) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) { - return c.multiNode.BalanceAt(ctx, account, blockNumber) + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, err + } + return r.BalanceAt(ctx, account, blockNumber) } // BatchCallContext - sends all given requests as a single batch. @@ -170,103 +171,170 @@ func (c *chainClient) BalanceAt(ctx context.Context, account common.Address, blo // might not be properly handled and returned results might have weaker finality guarantees. It's highly recommended // to use HeadTracker to identify latest finalized block. func (c *chainClient) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error { - return c.multiNode.BatchCallContext(ctx, b) + r, err := c.multiNode.SelectRPC() + if err != nil { + return err + } + return r.BatchCallContext(ctx, b) } // Similar to BatchCallContext, ensure the provided BatchElem slice is passed through func (c *chainClient) BatchCallContextAll(ctx context.Context, b []rpc.BatchElem) error { if c.chainType == chaintype.ChainHedera { - activeRPC, err := c.multiNode.SelectNodeRPC() + activeRPC, err := c.multiNode.SelectRPC() if err != nil { return err } return activeRPC.BatchCallContext(ctx, b) } - return c.multiNode.BatchCallContextAll(ctx, b) + + var wg sync.WaitGroup + defer wg.Wait() + + // Select main RPC to use for return value + main, selectionErr := c.multiNode.SelectRPC() + + doFunc := func(ctx context.Context, rpc *RPCClient, isSendOnly bool) { + if rpc == main { + return + } + // Parallel call made to all other nodes with ignored return value + wg.Add(1) + go func(rpc *RPCClient) { + defer wg.Done() + err := rpc.BatchCallContext(ctx, b) + if err != nil { + c.logger.Debugw("Secondary node BatchCallContext failed", "err", err) + } else { + c.logger.Debug("Secondary node BatchCallContext success") + } + }(rpc) + } + + if err := c.multiNode.DoAll(ctx, doFunc); err != nil { + return err + } + + if selectionErr != nil { + return selectionErr + } + + return main.BatchCallContext(ctx, b) } // TODO-1663: return custom Block type instead of geth's once client.go is deprecated. func (c *chainClient) BlockByHash(ctx context.Context, hash common.Hash) (b *types.Block, err error) { - rpc, err := c.multiNode.SelectNodeRPC() + r, err := c.multiNode.SelectRPC() if err != nil { return b, err } - return rpc.BlockByHashGeth(ctx, hash) + return r.BlockByHashGeth(ctx, hash) } // TODO-1663: return custom Block type instead of geth's once client.go is deprecated. func (c *chainClient) BlockByNumber(ctx context.Context, number *big.Int) (b *types.Block, err error) { - rpc, err := c.multiNode.SelectNodeRPC() + r, err := c.multiNode.SelectRPC() if err != nil { return b, err } - return rpc.BlockByNumberGeth(ctx, number) + return r.BlockByNumberGeth(ctx, number) } func (c *chainClient) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { - return c.multiNode.CallContext(ctx, result, method, args...) + r, err := c.multiNode.SelectRPC() + if err != nil { + return err + } + return r.CallContext(ctx, result, method, args...) } func (c *chainClient) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { - return c.multiNode.CallContract(ctx, msg, blockNumber) + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, err + } + return r.CallContract(ctx, msg, blockNumber) } func (c *chainClient) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) { - return c.multiNode.PendingCallContract(ctx, msg) -} - -// TODO-1663: change this to actual ChainID() call once client.go is deprecated. -func (c *chainClient) ChainID() (*big.Int, error) { - //return c.multiNode.ChainID(ctx), nil - return c.multiNode.ConfiguredChainID(), nil + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, err + } + return r.PendingCallContract(ctx, msg) } func (c *chainClient) Close() { - c.multiNode.Close() + _ = c.txSender.Close() + _ = c.multiNode.Close() } func (c *chainClient) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) { - return c.multiNode.CodeAt(ctx, account, blockNumber) + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, err + } + return r.CodeAt(ctx, account, blockNumber) } func (c *chainClient) ConfiguredChainID() *big.Int { - return c.multiNode.ConfiguredChainID() + return c.multiNode.ChainID() } func (c *chainClient) Dial(ctx context.Context) error { - return c.multiNode.Dial(ctx) + err := c.multiNode.Start(ctx) + if err != nil { + return err + } + return c.txSender.Start(ctx) } func (c *chainClient) EstimateGas(ctx context.Context, call ethereum.CallMsg) (uint64, error) { - return c.multiNode.EstimateGas(ctx, call) + r, err := c.multiNode.SelectRPC() + if err != nil { + return 0, err + } + return r.EstimateGas(ctx, call) } func (c *chainClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { - return c.multiNode.FilterEvents(ctx, q) + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, err + } + return r.FilterEvents(ctx, q) } func (c *chainClient) HeaderByHash(ctx context.Context, h common.Hash) (head *types.Header, err error) { - rpc, err := c.multiNode.SelectNodeRPC() + r, err := c.multiNode.SelectRPC() if err != nil { return head, err } - return rpc.HeaderByHash(ctx, h) + return r.HeaderByHash(ctx, h) } func (c *chainClient) HeaderByNumber(ctx context.Context, n *big.Int) (head *types.Header, err error) { - rpc, err := c.multiNode.SelectNodeRPC() + r, err := c.multiNode.SelectRPC() if err != nil { return head, err } - return rpc.HeaderByNumber(ctx, n) + return r.HeaderByNumber(ctx, n) } func (c *chainClient) HeadByHash(ctx context.Context, h common.Hash) (*evmtypes.Head, error) { - return c.multiNode.BlockByHash(ctx, h) + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, err + } + return r.BlockByHash(ctx, h) } func (c *chainClient) HeadByNumber(ctx context.Context, n *big.Int) (*evmtypes.Head, error) { - return c.multiNode.BlockByNumber(ctx, n) + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, err + } + return r.BlockByNumber(ctx, n) } func (c *chainClient) IsL2() bool { @@ -274,11 +342,19 @@ func (c *chainClient) IsL2() bool { } func (c *chainClient) LINKBalance(ctx context.Context, address common.Address, linkAddress common.Address) (*commonassets.Link, error) { - return c.multiNode.LINKBalance(ctx, address, linkAddress) + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, err + } + return r.LINKBalance(ctx, address, linkAddress) } func (c *chainClient) LatestBlockHeight(ctx context.Context) (*big.Int, error) { - return c.multiNode.LatestBlockHeight(ctx) + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, err + } + return r.LatestBlockHeight(ctx) } func (c *chainClient) NodeStates() map[string]string { @@ -286,28 +362,33 @@ func (c *chainClient) NodeStates() map[string]string { } func (c *chainClient) PendingCodeAt(ctx context.Context, account common.Address) (b []byte, err error) { - rpc, err := c.multiNode.SelectNodeRPC() + r, err := c.multiNode.SelectRPC() if err != nil { return b, err } - return rpc.PendingCodeAt(ctx, account) + return r.PendingCodeAt(ctx, account) } // TODO-1663: change this to evmtypes.Nonce(int64) once client.go is deprecated. func (c *chainClient) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { - n, err := c.multiNode.PendingSequenceAt(ctx, account) + r, err := c.multiNode.SelectRPC() + if err != nil { + return 0, err + } + n, err := r.PendingSequenceAt(ctx, account) return uint64(n), err } func (c *chainClient) SendTransaction(ctx context.Context, tx *types.Transaction) error { if c.chainType == chaintype.ChainHedera { - activeRPC, err := c.multiNode.SelectNodeRPC() + activeRPC, err := c.multiNode.SelectRPC() if err != nil { return err } return activeRPC.SendTransaction(ctx, tx) } - return c.multiNode.SendTransaction(ctx, tx) + _, err := c.txSender.SendTransaction(ctx, tx) + return err } func (c *chainClient) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (commonclient.SendTxReturnCode, error) { @@ -317,65 +398,91 @@ func (c *chainClient) SendTransactionReturnCode(ctx context.Context, tx *types.T } func (c *chainClient) SequenceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (evmtypes.Nonce, error) { - return c.multiNode.SequenceAt(ctx, account, blockNumber) + r, err := c.multiNode.SelectRPC() + if err != nil { + return 0, err + } + return r.SequenceAt(ctx, account, blockNumber) } func (c *chainClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (s ethereum.Subscription, err error) { - rpc, err := c.multiNode.SelectNodeRPC() + r, err := c.multiNode.SelectRPC() if err != nil { return s, err } - return rpc.SubscribeFilterLogs(ctx, q, ch) + return r.SubscribeFilterLogs(ctx, q, ch) } -func (c *chainClient) SubscribeNewHead(ctx context.Context, ch chan<- *evmtypes.Head) (ethereum.Subscription, error) { - return c.multiNode.SubscribeNewHead(ctx, ch) +func (c *chainClient) SubscribeToHeads(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) { + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, nil, err + } + + ch, sub, err := r.SubscribeToHeads(ctx) + if err != nil { + return nil, nil, err + } + + return ch, sub, nil } func (c *chainClient) SuggestGasPrice(ctx context.Context) (p *big.Int, err error) { - rpc, err := c.multiNode.SelectNodeRPC() + r, err := c.multiNode.SelectRPC() if err != nil { return p, err } - return rpc.SuggestGasPrice(ctx) + return r.SuggestGasPrice(ctx) } func (c *chainClient) SuggestGasTipCap(ctx context.Context) (t *big.Int, err error) { - rpc, err := c.multiNode.SelectNodeRPC() + r, err := c.multiNode.SelectRPC() if err != nil { return t, err } - return rpc.SuggestGasTipCap(ctx) + return r.SuggestGasTipCap(ctx) } func (c *chainClient) TokenBalance(ctx context.Context, address common.Address, contractAddress common.Address) (*big.Int, error) { - return c.multiNode.TokenBalance(ctx, address, contractAddress) + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, err + } + return r.TokenBalance(ctx, address, contractAddress) } func (c *chainClient) TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, error) { - return c.multiNode.TransactionByHash(ctx, txHash) + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, err + } + return r.TransactionByHash(ctx, txHash) } // TODO-1663: return custom Receipt type instead of geth's once client.go is deprecated. -func (c *chainClient) TransactionReceipt(ctx context.Context, txHash common.Hash) (r *types.Receipt, err error) { - rpc, err := c.multiNode.SelectNodeRPC() +func (c *chainClient) TransactionReceipt(ctx context.Context, txHash common.Hash) (receipt *types.Receipt, err error) { + r, err := c.multiNode.SelectRPC() if err != nil { - return r, err + return receipt, err } //return rpc.TransactionReceipt(ctx, txHash) - return rpc.TransactionReceiptGeth(ctx, txHash) + return r.TransactionReceiptGeth(ctx, txHash) } func (c *chainClient) LatestFinalizedBlock(ctx context.Context) (*evmtypes.Head, error) { - return c.multiNode.LatestFinalizedBlock(ctx) + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, err + } + return r.LatestFinalizedBlock(ctx) } func (c *chainClient) FeeHistory(ctx context.Context, blockCount uint64, rewardPercentiles []float64) (feeHistory *ethereum.FeeHistory, err error) { - rpc, err := c.multiNode.SelectNodeRPC() + r, err := c.multiNode.SelectRPC() if err != nil { return feeHistory, err } - return rpc.FeeHistory(ctx, blockCount, rewardPercentiles) + return r.FeeHistory(ctx, blockCount, rewardPercentiles) } func (c *chainClient) CheckTxValidity(ctx context.Context, from common.Address, to common.Address, data []byte) *SendError { diff --git a/core/chains/evm/client/chain_client_test.go b/core/chains/evm/client/chain_client_test.go index f6041228214..5562046d96a 100644 --- a/core/chains/evm/client/chain_client_test.go +++ b/core/chains/evm/client/chain_client_test.go @@ -21,16 +21,13 @@ import ( "github.com/ethereum/go-ethereum/rpc" pkgerrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/tidwall/gjson" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/testutils" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" @@ -451,6 +448,20 @@ func TestEthClient_SendTransaction_WithSecondaryURLs(t *testing.T) { require.Eventually(t, func() bool { return service.sentCount.Load() == int32(2) }, tests.WaitTimeout(t), 500*time.Millisecond) } +type sendTxService struct { + chainID *big.Int + sentCount atomic.Int32 +} + +func (x *sendTxService) ChainID(ctx context.Context) (*hexutil.Big, error) { + return (*hexutil.Big)(x.chainID), nil +} + +func (x *sendTxService) SendRawTransaction(ctx context.Context, signRawTx hexutil.Bytes) error { + x.sentCount.Add(1) + return nil +} + func TestEthClient_SendTransactionReturnCode(t *testing.T) { t.Parallel() @@ -691,20 +702,6 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { }) } -type sendTxService struct { - chainID *big.Int - sentCount atomic.Int32 -} - -func (x *sendTxService) ChainId(ctx context.Context) (*hexutil.Big, error) { - return (*hexutil.Big)(x.chainID), nil -} - -func (x *sendTxService) SendRawTransaction(ctx context.Context, signRawTx hexutil.Bytes) error { - x.sentCount.Add(1) - return nil -} - func TestEthClient_SubscribeNewHead(t *testing.T) { t.Parallel() @@ -729,8 +726,7 @@ func TestEthClient_SubscribeNewHead(t *testing.T) { err := ethClient.Dial(tests.Context(t)) require.NoError(t, err) - headCh := make(chan *evmtypes.Head) - sub, err := ethClient.SubscribeNewHead(ctx, headCh) + headCh, sub, err := ethClient.SubscribeToHeads(ctx) require.NoError(t, err) select { @@ -745,55 +741,49 @@ func TestEthClient_SubscribeNewHead(t *testing.T) { sub.Unsubscribe() } -func newMockRpc(t *testing.T) *mocks.RPCClient { - mockRpc := mocks.NewRPCClient(t) - mockRpc.On("Dial", mock.Anything).Return(nil).Once() - mockRpc.On("Close").Return(nil).Once() - mockRpc.On("ChainID", mock.Anything).Return(testutils.FixtureChainID, nil).Once() - // node does not always manage to fully setup aliveLoop, so we have to make calls optional to avoid flakes - mockRpc.On("SubscribeToHeads", mock.Anything).Return(nil, client.NewMockSubscription(), nil).Maybe() - mockRpc.On("SetAliveLoopSub", mock.Anything).Return().Maybe() - return mockRpc -} - -func TestChainClient_BatchCallContext(t *testing.T) { +func TestEthClient_BatchCallContext(t *testing.T) { t.Parallel() t.Run("batch requests return errors", func(t *testing.T) { - ctx := tests.Context(t) rpcError := errors.New("something went wrong") - blockNumResp := "" - blockNum := hexutil.EncodeBig(big.NewInt(42)) b := []rpc.BatchElem{ { - Method: "eth_getBlockByNumber", - Args: []interface{}{blockNum, true}, - Result: &types.Block{}, + Method: "eth_call", + Args: []interface{}{0}, + Result: "", }, { - Method: "eth_blockNumber", - Result: &blockNumResp, + Method: "eth_call", + Args: []interface{}{1}, + Result: "", }, } - mockRpc := newMockRpc(t) - mockRpc.On("GetInterceptedChainInfo").Return(commonclient.ChainInfo{}, commonclient.ChainInfo{}).Maybe() - mockRpc.On("BatchCallContext", mock.Anything, b).Run(func(args mock.Arguments) { - reqs := args.Get(1).([]rpc.BatchElem) - for i := 0; i < len(reqs); i++ { - elem := &reqs[i] - elem.Error = rpcError + wsURL := testutils.NewWSServer(t, testutils.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + switch method { + case "eth_subscribe": + resp.Result = `"0x00"` + resp.Notify = headResult + return + case "eth_unsubscribe": + resp.Result = "true" + return } - }).Return(nil).Once() + require.Equal(t, "eth_call", method) + resp.Error.Code = -1 + resp.Error.Message = rpcError.Error() + resp.Result = "" + return + }).WSURL().String() - client := client.NewChainClientWithMockedRpc(t, commonclient.NodeSelectionModeRoundRobin, time.Second*0, time.Second*0, testutils.FixtureChainID, mockRpc) - err := client.Dial(ctx) + ethClient := mustNewChainClient(t, wsURL) + err := ethClient.Dial(tests.Context(t)) require.NoError(t, err) - err = client.BatchCallContext(ctx, b) + err = ethClient.BatchCallContext(context.Background(), b) require.NoError(t, err) for _, elem := range b { - require.ErrorIs(t, rpcError, elem.Error) + require.Equal(t, elem.Error.Error(), rpcError.Error()) } }) } @@ -826,11 +816,8 @@ func TestEthClient_ErroringClient(t *testing.T) { _, err = erroringClient.CallContract(ctx, ethereum.CallMsg{}, nil) require.Equal(t, err, commonclient.ErroringNodeError) - // TODO-1663: test actual ChainID() call once client.go is deprecated. - id, err := erroringClient.ChainID() - require.Equal(t, id, testutils.FixtureChainID) - //require.Equal(t, err, commonclient.ErroringNodeError) - require.Equal(t, err, nil) + id := erroringClient.ConfiguredChainID() + require.Equal(t, id, big.NewInt(0)) _, err = erroringClient.CodeAt(ctx, common.Address{}, nil) require.Equal(t, err, commonclient.ErroringNodeError) @@ -871,13 +858,14 @@ func TestEthClient_ErroringClient(t *testing.T) { _, err = erroringClient.PendingNonceAt(ctx, common.Address{}) require.Equal(t, err, commonclient.ErroringNodeError) + txSenderNotStarted := errors.New("TransactionSender not started") err = erroringClient.SendTransaction(ctx, nil) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, err, txSenderNotStarted) tx := testutils.NewLegacyTransaction(uint64(42), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) code, err := erroringClient.SendTransactionReturnCode(ctx, tx, common.Address{}) - require.Equal(t, code, commonclient.Retryable) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, code, commonclient.Unknown) + require.Equal(t, err, txSenderNotStarted) _, err = erroringClient.SequenceAt(ctx, common.Address{}, nil) require.Equal(t, err, commonclient.ErroringNodeError) @@ -885,7 +873,7 @@ func TestEthClient_ErroringClient(t *testing.T) { _, err = erroringClient.SubscribeFilterLogs(ctx, ethereum.FilterQuery{}, nil) require.Equal(t, err, commonclient.ErroringNodeError) - _, err = erroringClient.SubscribeNewHead(ctx, nil) + _, _, err = erroringClient.SubscribeToHeads(ctx) require.Equal(t, err, commonclient.ErroringNodeError) _, err = erroringClient.SuggestGasPrice(ctx) diff --git a/core/chains/evm/client/config_builder_test.go b/core/chains/evm/client/config_builder_test.go index 22956fb0185..856242edada 100644 --- a/core/chains/evm/client/config_builder_test.go +++ b/core/chains/evm/client/config_builder_test.go @@ -69,7 +69,7 @@ func TestClientConfigBuilder(t *testing.T) { require.Equal(t, noNewFinalizedBlocksThreshold, chainCfg.NoNewFinalizedHeadsThreshold()) // let combiler tell us, when we do not have sufficient data to create evm client - _ = client.NewEvmClient(nodePool, chainCfg, nil, logger.Test(t), big.NewInt(10), nodes, chaintype.ChainType(chainTypeStr)) + _, _ = client.NewEvmClient(nodePool, chainCfg, nil, logger.Test(t), big.NewInt(10), nodes, chaintype.ChainType(chainTypeStr)) } func TestNodeConfigs(t *testing.T) { diff --git a/core/chains/evm/client/evm_client.go b/core/chains/evm/client/evm_client.go index c596bbc3a95..18206265fd7 100644 --- a/core/chains/evm/client/evm_client.go +++ b/core/chains/evm/client/evm_client.go @@ -11,36 +11,32 @@ import ( evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" - evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ) -func NewEvmClient(cfg evmconfig.NodePool, chainCfg commonclient.ChainConfig, clientErrors evmconfig.ClientErrors, lggr logger.Logger, chainID *big.Int, nodes []*toml.Node, chainType chaintype.ChainType) Client { - var primaries []commonclient.Node[*big.Int, *evmtypes.Head, RPCClient] - var sendonlys []commonclient.SendOnlyNode[*big.Int, RPCClient] +func NewEvmClient(cfg evmconfig.NodePool, chainCfg commonclient.ChainConfig, clientErrors evmconfig.ClientErrors, lggr logger.Logger, chainID *big.Int, nodes []*toml.Node, chainType chaintype.ChainType) (Client, error) { + var primaries []commonclient.Node[*big.Int, *RPCClient] + var sendonlys []commonclient.SendOnlyNode[*big.Int, *RPCClient] largePayloadRPCTimeout, defaultRPCTimeout := getRPCTimeouts(chainType) + for i, node := range nodes { - var ws *url.URL - if node.WSURL != nil { - ws = (*url.URL)(node.WSURL) - } if node.SendOnly != nil && *node.SendOnly { - rpc := NewRPCClient(lggr, nil, (*url.URL)(node.HTTPURL), *node.Name, i, chainID, - commonclient.Secondary, cfg.FinalizedBlockPollInterval(), cfg.NewHeadsPollInterval(), largePayloadRPCTimeout, defaultRPCTimeout, chainType) + rpc := NewRPCClient(cfg, lggr, nil, node.HTTPURL.URL(), *node.Name, i, chainID, + commonclient.Secondary, largePayloadRPCTimeout, defaultRPCTimeout, chainType) sendonly := commonclient.NewSendOnlyNode(lggr, (url.URL)(*node.HTTPURL), *node.Name, chainID, rpc) sendonlys = append(sendonlys, sendonly) } else { - rpc := NewRPCClient(lggr, ws, (*url.URL)(node.HTTPURL), *node.Name, i, - chainID, commonclient.Primary, cfg.FinalizedBlockPollInterval(), cfg.NewHeadsPollInterval(), largePayloadRPCTimeout, defaultRPCTimeout, chainType) + rpc := NewRPCClient(cfg, lggr, node.WSURL.URL(), node.HTTPURL.URL(), *node.Name, i, + chainID, commonclient.Primary, largePayloadRPCTimeout, defaultRPCTimeout, chainType) primaryNode := commonclient.NewNode(cfg, chainCfg, - lggr, ws, (*url.URL)(node.HTTPURL), *node.Name, i, chainID, *node.Order, + lggr, node.WSURL.URL(), node.HTTPURL.URL(), *node.Name, i, chainID, *node.Order, rpc, "EVM") primaries = append(primaries, primaryNode) } } - return NewChainClient(lggr, cfg.SelectionMode(), cfg.LeaseDuration(), chainCfg.NodeNoNewHeadsThreshold(), - primaries, sendonlys, chainID, chainType, clientErrors, cfg.DeathDeclarationDelay()) + return NewChainClient(lggr, cfg.SelectionMode(), cfg.LeaseDuration(), + primaries, sendonlys, chainID, clientErrors, cfg.DeathDeclarationDelay(), chainType), nil } func getRPCTimeouts(chainType chaintype.ChainType) (largePayload, defaultTimeout time.Duration) { diff --git a/core/chains/evm/client/evm_client_test.go b/core/chains/evm/client/evm_client_test.go index b762c14653c..9deca8edeee 100644 --- a/core/chains/evm/client/evm_client_test.go +++ b/core/chains/evm/client/evm_client_test.go @@ -45,6 +45,7 @@ func TestNewEvmClient(t *testing.T) { finalizedBlockPollInterval, newHeadsPollInterval) require.NoError(t, err) - client := client.NewEvmClient(nodePool, chainCfg, nil, logger.Test(t), testutils.FixtureChainID, nodes, chaintype.ChainType(chainTypeStr)) + client, err := client.NewEvmClient(nodePool, chainCfg, nil, logger.Test(t), testutils.FixtureChainID, nodes, chaintype.ChainType(chainTypeStr)) require.NotNil(t, client) + require.NoError(t, err) } diff --git a/core/chains/evm/client/helpers_test.go b/core/chains/evm/client/helpers_test.go index 1a6090e4a01..f9751be765c 100644 --- a/core/chains/evm/client/helpers_test.go +++ b/core/chains/evm/client/helpers_test.go @@ -14,7 +14,6 @@ import ( commonclient "github.com/smartcontractkit/chainlink/v2/common/client" clientMocks "github.com/smartcontractkit/chainlink/v2/common/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ) @@ -148,27 +147,28 @@ func NewChainClientWithTestNode( } lggr := logger.Test(t) - rpc := NewRPCClient(lggr, parsed, rpcHTTPURL, "eth-primary-rpc-0", id, chainID, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + nodePoolCfg := TestNodePoolConfig{ + NodeFinalizedBlockPollInterval: 1 * time.Second, + } + rpc := NewRPCClient(nodePoolCfg, lggr, parsed, rpcHTTPURL, "eth-primary-rpc-0", id, chainID, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") - n := commonclient.NewNode[*big.Int, *evmtypes.Head, RPCClient]( + n := commonclient.NewNode[*big.Int, *evmtypes.Head, *RPCClient]( nodeCfg, clientMocks.ChainConfig{NoNewHeadsThresholdVal: noNewHeadsThreshold}, lggr, parsed, rpcHTTPURL, "eth-primary-node-0", id, chainID, 1, rpc, "EVM") - primaries := []commonclient.Node[*big.Int, *evmtypes.Head, RPCClient]{n} + primaries := []commonclient.Node[*big.Int, *RPCClient]{n} - var sendonlys []commonclient.SendOnlyNode[*big.Int, RPCClient] + var sendonlys []commonclient.SendOnlyNode[*big.Int, *RPCClient] for i, u := range sendonlyRPCURLs { if u.Scheme != "http" && u.Scheme != "https" { return nil, pkgerrors.Errorf("sendonly ethereum rpc url scheme must be http(s): %s", u.String()) } - var empty url.URL - rpc := NewRPCClient(lggr, &empty, &sendonlyRPCURLs[i], fmt.Sprintf("eth-sendonly-rpc-%d", i), id, chainID, commonclient.Secondary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") - s := commonclient.NewSendOnlyNode[*big.Int, RPCClient]( + rpc := NewRPCClient(nodePoolCfg, lggr, nil, &sendonlyRPCURLs[i], fmt.Sprintf("eth-sendonly-rpc-%d", i), id, chainID, commonclient.Secondary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + s := commonclient.NewSendOnlyNode[*big.Int, *RPCClient]( lggr, u, fmt.Sprintf("eth-sendonly-%d", i), chainID, rpc) sendonlys = append(sendonlys, s) } - var chainType chaintype.ChainType clientErrors := NewTestClientErrors() - c := NewChainClient(lggr, nodeCfg.SelectionMode(), leaseDuration, noNewHeadsThreshold, primaries, sendonlys, chainID, chainType, &clientErrors, 0) + c := NewChainClient(lggr, nodeCfg.SelectionMode(), leaseDuration, primaries, sendonlys, chainID, &clientErrors, 0, "") t.Cleanup(c.Close) return c, nil } @@ -182,8 +182,7 @@ func NewChainClientWithEmptyNode( ) Client { lggr := logger.Test(t) - var chainType chaintype.ChainType - c := NewChainClient(lggr, selectionMode, leaseDuration, noNewHeadsThreshold, nil, nil, chainID, chainType, nil, 0) + c := NewChainClient(lggr, selectionMode, leaseDuration, nil, nil, chainID, nil, 0, "") t.Cleanup(c.Close) return c } @@ -194,22 +193,20 @@ func NewChainClientWithMockedRpc( leaseDuration time.Duration, noNewHeadsThreshold time.Duration, chainID *big.Int, - rpc RPCClient, + rpc *RPCClient, ) Client { lggr := logger.Test(t) - var chainType chaintype.ChainType - cfg := TestNodePoolConfig{ NodeSelectionMode: commonclient.NodeSelectionModeRoundRobin, } parsed, _ := url.ParseRequestURI("ws://test") - n := commonclient.NewNode[*big.Int, *evmtypes.Head, RPCClient]( + n := commonclient.NewNode[*big.Int, *evmtypes.Head, *RPCClient]( cfg, clientMocks.ChainConfig{NoNewHeadsThresholdVal: noNewHeadsThreshold}, lggr, parsed, nil, "eth-primary-node-0", 1, chainID, 1, rpc, "EVM") - primaries := []commonclient.Node[*big.Int, *evmtypes.Head, RPCClient]{n} + primaries := []commonclient.Node[*big.Int, *RPCClient]{n} clientErrors := NewTestClientErrors() - c := NewChainClient(lggr, selectionMode, leaseDuration, noNewHeadsThreshold, primaries, nil, chainID, chainType, &clientErrors, 0) + c := NewChainClient(lggr, selectionMode, leaseDuration, primaries, nil, chainID, &clientErrors, 0, "") t.Cleanup(c.Close) return c } diff --git a/core/chains/evm/client/mocks/client.go b/core/chains/evm/client/mocks/client.go index da034d95774..e3350cf8a14 100644 --- a/core/chains/evm/client/mocks/client.go +++ b/core/chains/evm/client/mocks/client.go @@ -430,63 +430,6 @@ func (_c *Client_CallContract_Call) RunAndReturn(run func(context.Context, ether return _c } -// ChainID provides a mock function with given fields: -func (_m *Client) ChainID() (*big.Int, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ChainID") - } - - var r0 *big.Int - var r1 error - if rf, ok := ret.Get(0).(func() (*big.Int, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() *big.Int); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*big.Int) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Client_ChainID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChainID' -type Client_ChainID_Call struct { - *mock.Call -} - -// ChainID is a helper method to define mock.On call -func (_e *Client_Expecter) ChainID() *Client_ChainID_Call { - return &Client_ChainID_Call{Call: _e.mock.On("ChainID")} -} - -func (_c *Client_ChainID_Call) Run(run func()) *Client_ChainID_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *Client_ChainID_Call) Return(_a0 *big.Int, _a1 error) *Client_ChainID_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *Client_ChainID_Call) RunAndReturn(run func() (*big.Int, error)) *Client_ChainID_Call { - _c.Call.Return(run) - return _c -} - // CheckTxValidity provides a mock function with given fields: ctx, from, to, data func (_m *Client) CheckTxValidity(ctx context.Context, from common.Address, to common.Address, data []byte) *client.SendError { ret := _m.Called(ctx, from, to, data) @@ -1801,61 +1744,69 @@ func (_c *Client_SubscribeFilterLogs_Call) RunAndReturn(run func(context.Context return _c } -// SubscribeNewHead provides a mock function with given fields: ctx, ch -func (_m *Client) SubscribeNewHead(ctx context.Context, ch chan<- *evmtypes.Head) (ethereum.Subscription, error) { - ret := _m.Called(ctx, ch) +// SubscribeToHeads provides a mock function with given fields: ctx +func (_m *Client) SubscribeToHeads(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) { + ret := _m.Called(ctx) if len(ret) == 0 { - panic("no return value specified for SubscribeNewHead") + panic("no return value specified for SubscribeToHeads") } - var r0 ethereum.Subscription - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, chan<- *evmtypes.Head) (ethereum.Subscription, error)); ok { - return rf(ctx, ch) + var r0 <-chan *evmtypes.Head + var r1 ethereum.Subscription + var r2 error + if rf, ok := ret.Get(0).(func(context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error)); ok { + return rf(ctx) } - if rf, ok := ret.Get(0).(func(context.Context, chan<- *evmtypes.Head) ethereum.Subscription); ok { - r0 = rf(ctx, ch) + if rf, ok := ret.Get(0).(func(context.Context) <-chan *evmtypes.Head); ok { + r0 = rf(ctx) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(ethereum.Subscription) + r0 = ret.Get(0).(<-chan *evmtypes.Head) } } - if rf, ok := ret.Get(1).(func(context.Context, chan<- *evmtypes.Head) error); ok { - r1 = rf(ctx, ch) + if rf, ok := ret.Get(1).(func(context.Context) ethereum.Subscription); ok { + r1 = rf(ctx) } else { - r1 = ret.Error(1) + if ret.Get(1) != nil { + r1 = ret.Get(1).(ethereum.Subscription) + } } - return r0, r1 + if rf, ok := ret.Get(2).(func(context.Context) error); ok { + r2 = rf(ctx) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 } -// Client_SubscribeNewHead_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeNewHead' -type Client_SubscribeNewHead_Call struct { +// Client_SubscribeToHeads_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeToHeads' +type Client_SubscribeToHeads_Call struct { *mock.Call } -// SubscribeNewHead is a helper method to define mock.On call +// SubscribeToHeads is a helper method to define mock.On call // - ctx context.Context -// - ch chan<- *evmtypes.Head -func (_e *Client_Expecter) SubscribeNewHead(ctx interface{}, ch interface{}) *Client_SubscribeNewHead_Call { - return &Client_SubscribeNewHead_Call{Call: _e.mock.On("SubscribeNewHead", ctx, ch)} +func (_e *Client_Expecter) SubscribeToHeads(ctx interface{}) *Client_SubscribeToHeads_Call { + return &Client_SubscribeToHeads_Call{Call: _e.mock.On("SubscribeToHeads", ctx)} } -func (_c *Client_SubscribeNewHead_Call) Run(run func(ctx context.Context, ch chan<- *evmtypes.Head)) *Client_SubscribeNewHead_Call { +func (_c *Client_SubscribeToHeads_Call) Run(run func(ctx context.Context)) *Client_SubscribeToHeads_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(chan<- *evmtypes.Head)) + run(args[0].(context.Context)) }) return _c } -func (_c *Client_SubscribeNewHead_Call) Return(_a0 ethereum.Subscription, _a1 error) *Client_SubscribeNewHead_Call { - _c.Call.Return(_a0, _a1) +func (_c *Client_SubscribeToHeads_Call) Return(_a0 <-chan *evmtypes.Head, _a1 ethereum.Subscription, _a2 error) *Client_SubscribeToHeads_Call { + _c.Call.Return(_a0, _a1, _a2) return _c } -func (_c *Client_SubscribeNewHead_Call) RunAndReturn(run func(context.Context, chan<- *evmtypes.Head) (ethereum.Subscription, error)) *Client_SubscribeNewHead_Call { +func (_c *Client_SubscribeToHeads_Call) RunAndReturn(run func(context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error)) *Client_SubscribeToHeads_Call { _c.Call.Return(run) return _c } diff --git a/core/chains/evm/client/mocks/rpc_client.go b/core/chains/evm/client/mocks/rpc_client.go deleted file mode 100644 index 5567b3f8978..00000000000 --- a/core/chains/evm/client/mocks/rpc_client.go +++ /dev/null @@ -1,2532 +0,0 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. - -package mocks - -import ( - big "math/big" - - assets "github.com/smartcontractkit/chainlink-common/pkg/assets" - - common "github.com/ethereum/go-ethereum/common" - - commonclient "github.com/smartcontractkit/chainlink/v2/common/client" - - commontypes "github.com/smartcontractkit/chainlink/v2/common/types" - - context "context" - - coretypes "github.com/ethereum/go-ethereum/core/types" - - ethereum "github.com/ethereum/go-ethereum" - - evmassets "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" - - mock "github.com/stretchr/testify/mock" - - rpc "github.com/ethereum/go-ethereum/rpc" - - types "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" -) - -// RPCClient is an autogenerated mock type for the RPCClient type -type RPCClient struct { - mock.Mock -} - -type RPCClient_Expecter struct { - mock *mock.Mock -} - -func (_m *RPCClient) EXPECT() *RPCClient_Expecter { - return &RPCClient_Expecter{mock: &_m.Mock} -} - -// BalanceAt provides a mock function with given fields: ctx, accountAddress, blockNumber -func (_m *RPCClient) BalanceAt(ctx context.Context, accountAddress common.Address, blockNumber *big.Int) (*big.Int, error) { - ret := _m.Called(ctx, accountAddress, blockNumber) - - if len(ret) == 0 { - panic("no return value specified for BalanceAt") - } - - var r0 *big.Int - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) (*big.Int, error)); ok { - return rf(ctx, accountAddress, blockNumber) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) *big.Int); ok { - r0 = rf(ctx, accountAddress, blockNumber) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*big.Int) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Address, *big.Int) error); ok { - r1 = rf(ctx, accountAddress, blockNumber) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_BalanceAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BalanceAt' -type RPCClient_BalanceAt_Call struct { - *mock.Call -} - -// BalanceAt is a helper method to define mock.On call -// - ctx context.Context -// - accountAddress common.Address -// - blockNumber *big.Int -func (_e *RPCClient_Expecter) BalanceAt(ctx interface{}, accountAddress interface{}, blockNumber interface{}) *RPCClient_BalanceAt_Call { - return &RPCClient_BalanceAt_Call{Call: _e.mock.On("BalanceAt", ctx, accountAddress, blockNumber)} -} - -func (_c *RPCClient_BalanceAt_Call) Run(run func(ctx context.Context, accountAddress common.Address, blockNumber *big.Int)) *RPCClient_BalanceAt_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Address), args[2].(*big.Int)) - }) - return _c -} - -func (_c *RPCClient_BalanceAt_Call) Return(_a0 *big.Int, _a1 error) *RPCClient_BalanceAt_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_BalanceAt_Call) RunAndReturn(run func(context.Context, common.Address, *big.Int) (*big.Int, error)) *RPCClient_BalanceAt_Call { - _c.Call.Return(run) - return _c -} - -// BatchCallContext provides a mock function with given fields: ctx, b -func (_m *RPCClient) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error { - ret := _m.Called(ctx, b) - - if len(ret) == 0 { - panic("no return value specified for BatchCallContext") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, []rpc.BatchElem) error); ok { - r0 = rf(ctx, b) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// RPCClient_BatchCallContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BatchCallContext' -type RPCClient_BatchCallContext_Call struct { - *mock.Call -} - -// BatchCallContext is a helper method to define mock.On call -// - ctx context.Context -// - b []rpc.BatchElem -func (_e *RPCClient_Expecter) BatchCallContext(ctx interface{}, b interface{}) *RPCClient_BatchCallContext_Call { - return &RPCClient_BatchCallContext_Call{Call: _e.mock.On("BatchCallContext", ctx, b)} -} - -func (_c *RPCClient_BatchCallContext_Call) Run(run func(ctx context.Context, b []rpc.BatchElem)) *RPCClient_BatchCallContext_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].([]rpc.BatchElem)) - }) - return _c -} - -func (_c *RPCClient_BatchCallContext_Call) Return(_a0 error) *RPCClient_BatchCallContext_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *RPCClient_BatchCallContext_Call) RunAndReturn(run func(context.Context, []rpc.BatchElem) error) *RPCClient_BatchCallContext_Call { - _c.Call.Return(run) - return _c -} - -// BlockByHash provides a mock function with given fields: ctx, hash -func (_m *RPCClient) BlockByHash(ctx context.Context, hash common.Hash) (*types.Head, error) { - ret := _m.Called(ctx, hash) - - if len(ret) == 0 { - panic("no return value specified for BlockByHash") - } - - var r0 *types.Head - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*types.Head, error)); ok { - return rf(ctx, hash) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *types.Head); ok { - r0 = rf(ctx, hash) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*types.Head) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { - r1 = rf(ctx, hash) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_BlockByHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockByHash' -type RPCClient_BlockByHash_Call struct { - *mock.Call -} - -// BlockByHash is a helper method to define mock.On call -// - ctx context.Context -// - hash common.Hash -func (_e *RPCClient_Expecter) BlockByHash(ctx interface{}, hash interface{}) *RPCClient_BlockByHash_Call { - return &RPCClient_BlockByHash_Call{Call: _e.mock.On("BlockByHash", ctx, hash)} -} - -func (_c *RPCClient_BlockByHash_Call) Run(run func(ctx context.Context, hash common.Hash)) *RPCClient_BlockByHash_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Hash)) - }) - return _c -} - -func (_c *RPCClient_BlockByHash_Call) Return(_a0 *types.Head, _a1 error) *RPCClient_BlockByHash_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_BlockByHash_Call) RunAndReturn(run func(context.Context, common.Hash) (*types.Head, error)) *RPCClient_BlockByHash_Call { - _c.Call.Return(run) - return _c -} - -// BlockByHashGeth provides a mock function with given fields: ctx, hash -func (_m *RPCClient) BlockByHashGeth(ctx context.Context, hash common.Hash) (*coretypes.Block, error) { - ret := _m.Called(ctx, hash) - - if len(ret) == 0 { - panic("no return value specified for BlockByHashGeth") - } - - var r0 *coretypes.Block - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*coretypes.Block, error)); ok { - return rf(ctx, hash) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *coretypes.Block); ok { - r0 = rf(ctx, hash) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*coretypes.Block) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { - r1 = rf(ctx, hash) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_BlockByHashGeth_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockByHashGeth' -type RPCClient_BlockByHashGeth_Call struct { - *mock.Call -} - -// BlockByHashGeth is a helper method to define mock.On call -// - ctx context.Context -// - hash common.Hash -func (_e *RPCClient_Expecter) BlockByHashGeth(ctx interface{}, hash interface{}) *RPCClient_BlockByHashGeth_Call { - return &RPCClient_BlockByHashGeth_Call{Call: _e.mock.On("BlockByHashGeth", ctx, hash)} -} - -func (_c *RPCClient_BlockByHashGeth_Call) Run(run func(ctx context.Context, hash common.Hash)) *RPCClient_BlockByHashGeth_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Hash)) - }) - return _c -} - -func (_c *RPCClient_BlockByHashGeth_Call) Return(b *coretypes.Block, err error) *RPCClient_BlockByHashGeth_Call { - _c.Call.Return(b, err) - return _c -} - -func (_c *RPCClient_BlockByHashGeth_Call) RunAndReturn(run func(context.Context, common.Hash) (*coretypes.Block, error)) *RPCClient_BlockByHashGeth_Call { - _c.Call.Return(run) - return _c -} - -// BlockByNumber provides a mock function with given fields: ctx, number -func (_m *RPCClient) BlockByNumber(ctx context.Context, number *big.Int) (*types.Head, error) { - ret := _m.Called(ctx, number) - - if len(ret) == 0 { - panic("no return value specified for BlockByNumber") - } - - var r0 *types.Head - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*types.Head, error)); ok { - return rf(ctx, number) - } - if rf, ok := ret.Get(0).(func(context.Context, *big.Int) *types.Head); ok { - r0 = rf(ctx, number) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*types.Head) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { - r1 = rf(ctx, number) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_BlockByNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockByNumber' -type RPCClient_BlockByNumber_Call struct { - *mock.Call -} - -// BlockByNumber is a helper method to define mock.On call -// - ctx context.Context -// - number *big.Int -func (_e *RPCClient_Expecter) BlockByNumber(ctx interface{}, number interface{}) *RPCClient_BlockByNumber_Call { - return &RPCClient_BlockByNumber_Call{Call: _e.mock.On("BlockByNumber", ctx, number)} -} - -func (_c *RPCClient_BlockByNumber_Call) Run(run func(ctx context.Context, number *big.Int)) *RPCClient_BlockByNumber_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*big.Int)) - }) - return _c -} - -func (_c *RPCClient_BlockByNumber_Call) Return(_a0 *types.Head, _a1 error) *RPCClient_BlockByNumber_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_BlockByNumber_Call) RunAndReturn(run func(context.Context, *big.Int) (*types.Head, error)) *RPCClient_BlockByNumber_Call { - _c.Call.Return(run) - return _c -} - -// BlockByNumberGeth provides a mock function with given fields: ctx, number -func (_m *RPCClient) BlockByNumberGeth(ctx context.Context, number *big.Int) (*coretypes.Block, error) { - ret := _m.Called(ctx, number) - - if len(ret) == 0 { - panic("no return value specified for BlockByNumberGeth") - } - - var r0 *coretypes.Block - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*coretypes.Block, error)); ok { - return rf(ctx, number) - } - if rf, ok := ret.Get(0).(func(context.Context, *big.Int) *coretypes.Block); ok { - r0 = rf(ctx, number) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*coretypes.Block) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { - r1 = rf(ctx, number) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_BlockByNumberGeth_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockByNumberGeth' -type RPCClient_BlockByNumberGeth_Call struct { - *mock.Call -} - -// BlockByNumberGeth is a helper method to define mock.On call -// - ctx context.Context -// - number *big.Int -func (_e *RPCClient_Expecter) BlockByNumberGeth(ctx interface{}, number interface{}) *RPCClient_BlockByNumberGeth_Call { - return &RPCClient_BlockByNumberGeth_Call{Call: _e.mock.On("BlockByNumberGeth", ctx, number)} -} - -func (_c *RPCClient_BlockByNumberGeth_Call) Run(run func(ctx context.Context, number *big.Int)) *RPCClient_BlockByNumberGeth_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*big.Int)) - }) - return _c -} - -func (_c *RPCClient_BlockByNumberGeth_Call) Return(b *coretypes.Block, err error) *RPCClient_BlockByNumberGeth_Call { - _c.Call.Return(b, err) - return _c -} - -func (_c *RPCClient_BlockByNumberGeth_Call) RunAndReturn(run func(context.Context, *big.Int) (*coretypes.Block, error)) *RPCClient_BlockByNumberGeth_Call { - _c.Call.Return(run) - return _c -} - -// CallContext provides a mock function with given fields: ctx, result, method, args -func (_m *RPCClient) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { - var _ca []interface{} - _ca = append(_ca, ctx, result, method) - _ca = append(_ca, args...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for CallContext") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, interface{}, string, ...interface{}) error); ok { - r0 = rf(ctx, result, method, args...) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// RPCClient_CallContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CallContext' -type RPCClient_CallContext_Call struct { - *mock.Call -} - -// CallContext is a helper method to define mock.On call -// - ctx context.Context -// - result interface{} -// - method string -// - args ...interface{} -func (_e *RPCClient_Expecter) CallContext(ctx interface{}, result interface{}, method interface{}, args ...interface{}) *RPCClient_CallContext_Call { - return &RPCClient_CallContext_Call{Call: _e.mock.On("CallContext", - append([]interface{}{ctx, result, method}, args...)...)} -} - -func (_c *RPCClient_CallContext_Call) Run(run func(ctx context.Context, result interface{}, method string, args ...interface{})) *RPCClient_CallContext_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]interface{}, len(args)-3) - for i, a := range args[3:] { - if a != nil { - variadicArgs[i] = a.(interface{}) - } - } - run(args[0].(context.Context), args[1].(interface{}), args[2].(string), variadicArgs...) - }) - return _c -} - -func (_c *RPCClient_CallContext_Call) Return(_a0 error) *RPCClient_CallContext_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *RPCClient_CallContext_Call) RunAndReturn(run func(context.Context, interface{}, string, ...interface{}) error) *RPCClient_CallContext_Call { - _c.Call.Return(run) - return _c -} - -// CallContract provides a mock function with given fields: ctx, msg, blockNumber -func (_m *RPCClient) CallContract(ctx context.Context, msg interface{}, blockNumber *big.Int) ([]byte, error) { - ret := _m.Called(ctx, msg, blockNumber) - - if len(ret) == 0 { - panic("no return value specified for CallContract") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, interface{}, *big.Int) ([]byte, error)); ok { - return rf(ctx, msg, blockNumber) - } - if rf, ok := ret.Get(0).(func(context.Context, interface{}, *big.Int) []byte); ok { - r0 = rf(ctx, msg, blockNumber) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, interface{}, *big.Int) error); ok { - r1 = rf(ctx, msg, blockNumber) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_CallContract_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CallContract' -type RPCClient_CallContract_Call struct { - *mock.Call -} - -// CallContract is a helper method to define mock.On call -// - ctx context.Context -// - msg interface{} -// - blockNumber *big.Int -func (_e *RPCClient_Expecter) CallContract(ctx interface{}, msg interface{}, blockNumber interface{}) *RPCClient_CallContract_Call { - return &RPCClient_CallContract_Call{Call: _e.mock.On("CallContract", ctx, msg, blockNumber)} -} - -func (_c *RPCClient_CallContract_Call) Run(run func(ctx context.Context, msg interface{}, blockNumber *big.Int)) *RPCClient_CallContract_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(interface{}), args[2].(*big.Int)) - }) - return _c -} - -func (_c *RPCClient_CallContract_Call) Return(rpcErr []byte, extractErr error) *RPCClient_CallContract_Call { - _c.Call.Return(rpcErr, extractErr) - return _c -} - -func (_c *RPCClient_CallContract_Call) RunAndReturn(run func(context.Context, interface{}, *big.Int) ([]byte, error)) *RPCClient_CallContract_Call { - _c.Call.Return(run) - return _c -} - -// ChainID provides a mock function with given fields: ctx -func (_m *RPCClient) ChainID(ctx context.Context) (*big.Int, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for ChainID") - } - - var r0 *big.Int - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (*big.Int, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*big.Int) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_ChainID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChainID' -type RPCClient_ChainID_Call struct { - *mock.Call -} - -// ChainID is a helper method to define mock.On call -// - ctx context.Context -func (_e *RPCClient_Expecter) ChainID(ctx interface{}) *RPCClient_ChainID_Call { - return &RPCClient_ChainID_Call{Call: _e.mock.On("ChainID", ctx)} -} - -func (_c *RPCClient_ChainID_Call) Run(run func(ctx context.Context)) *RPCClient_ChainID_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *RPCClient_ChainID_Call) Return(_a0 *big.Int, _a1 error) *RPCClient_ChainID_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_ChainID_Call) RunAndReturn(run func(context.Context) (*big.Int, error)) *RPCClient_ChainID_Call { - _c.Call.Return(run) - return _c -} - -// ClientVersion provides a mock function with given fields: _a0 -func (_m *RPCClient) ClientVersion(_a0 context.Context) (string, error) { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for ClientVersion") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (string, error)); ok { - return rf(_a0) - } - if rf, ok := ret.Get(0).(func(context.Context) string); ok { - r0 = rf(_a0) - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(_a0) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_ClientVersion_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ClientVersion' -type RPCClient_ClientVersion_Call struct { - *mock.Call -} - -// ClientVersion is a helper method to define mock.On call -// - _a0 context.Context -func (_e *RPCClient_Expecter) ClientVersion(_a0 interface{}) *RPCClient_ClientVersion_Call { - return &RPCClient_ClientVersion_Call{Call: _e.mock.On("ClientVersion", _a0)} -} - -func (_c *RPCClient_ClientVersion_Call) Run(run func(_a0 context.Context)) *RPCClient_ClientVersion_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *RPCClient_ClientVersion_Call) Return(_a0 string, _a1 error) *RPCClient_ClientVersion_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_ClientVersion_Call) RunAndReturn(run func(context.Context) (string, error)) *RPCClient_ClientVersion_Call { - _c.Call.Return(run) - return _c -} - -// Close provides a mock function with given fields: -func (_m *RPCClient) Close() { - _m.Called() -} - -// RPCClient_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' -type RPCClient_Close_Call struct { - *mock.Call -} - -// Close is a helper method to define mock.On call -func (_e *RPCClient_Expecter) Close() *RPCClient_Close_Call { - return &RPCClient_Close_Call{Call: _e.mock.On("Close")} -} - -func (_c *RPCClient_Close_Call) Run(run func()) *RPCClient_Close_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *RPCClient_Close_Call) Return() *RPCClient_Close_Call { - _c.Call.Return() - return _c -} - -func (_c *RPCClient_Close_Call) RunAndReturn(run func()) *RPCClient_Close_Call { - _c.Call.Return(run) - return _c -} - -// CodeAt provides a mock function with given fields: ctx, account, blockNumber -func (_m *RPCClient) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) { - ret := _m.Called(ctx, account, blockNumber) - - if len(ret) == 0 { - panic("no return value specified for CodeAt") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) ([]byte, error)); ok { - return rf(ctx, account, blockNumber) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) []byte); ok { - r0 = rf(ctx, account, blockNumber) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Address, *big.Int) error); ok { - r1 = rf(ctx, account, blockNumber) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_CodeAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CodeAt' -type RPCClient_CodeAt_Call struct { - *mock.Call -} - -// CodeAt is a helper method to define mock.On call -// - ctx context.Context -// - account common.Address -// - blockNumber *big.Int -func (_e *RPCClient_Expecter) CodeAt(ctx interface{}, account interface{}, blockNumber interface{}) *RPCClient_CodeAt_Call { - return &RPCClient_CodeAt_Call{Call: _e.mock.On("CodeAt", ctx, account, blockNumber)} -} - -func (_c *RPCClient_CodeAt_Call) Run(run func(ctx context.Context, account common.Address, blockNumber *big.Int)) *RPCClient_CodeAt_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Address), args[2].(*big.Int)) - }) - return _c -} - -func (_c *RPCClient_CodeAt_Call) Return(_a0 []byte, _a1 error) *RPCClient_CodeAt_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_CodeAt_Call) RunAndReturn(run func(context.Context, common.Address, *big.Int) ([]byte, error)) *RPCClient_CodeAt_Call { - _c.Call.Return(run) - return _c -} - -// Dial provides a mock function with given fields: ctx -func (_m *RPCClient) Dial(ctx context.Context) error { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for Dial") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context) error); ok { - r0 = rf(ctx) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// RPCClient_Dial_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Dial' -type RPCClient_Dial_Call struct { - *mock.Call -} - -// Dial is a helper method to define mock.On call -// - ctx context.Context -func (_e *RPCClient_Expecter) Dial(ctx interface{}) *RPCClient_Dial_Call { - return &RPCClient_Dial_Call{Call: _e.mock.On("Dial", ctx)} -} - -func (_c *RPCClient_Dial_Call) Run(run func(ctx context.Context)) *RPCClient_Dial_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *RPCClient_Dial_Call) Return(_a0 error) *RPCClient_Dial_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *RPCClient_Dial_Call) RunAndReturn(run func(context.Context) error) *RPCClient_Dial_Call { - _c.Call.Return(run) - return _c -} - -// DialHTTP provides a mock function with given fields: -func (_m *RPCClient) DialHTTP() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for DialHTTP") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// RPCClient_DialHTTP_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DialHTTP' -type RPCClient_DialHTTP_Call struct { - *mock.Call -} - -// DialHTTP is a helper method to define mock.On call -func (_e *RPCClient_Expecter) DialHTTP() *RPCClient_DialHTTP_Call { - return &RPCClient_DialHTTP_Call{Call: _e.mock.On("DialHTTP")} -} - -func (_c *RPCClient_DialHTTP_Call) Run(run func()) *RPCClient_DialHTTP_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *RPCClient_DialHTTP_Call) Return(_a0 error) *RPCClient_DialHTTP_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *RPCClient_DialHTTP_Call) RunAndReturn(run func() error) *RPCClient_DialHTTP_Call { - _c.Call.Return(run) - return _c -} - -// DisconnectAll provides a mock function with given fields: -func (_m *RPCClient) DisconnectAll() { - _m.Called() -} - -// RPCClient_DisconnectAll_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisconnectAll' -type RPCClient_DisconnectAll_Call struct { - *mock.Call -} - -// DisconnectAll is a helper method to define mock.On call -func (_e *RPCClient_Expecter) DisconnectAll() *RPCClient_DisconnectAll_Call { - return &RPCClient_DisconnectAll_Call{Call: _e.mock.On("DisconnectAll")} -} - -func (_c *RPCClient_DisconnectAll_Call) Run(run func()) *RPCClient_DisconnectAll_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *RPCClient_DisconnectAll_Call) Return() *RPCClient_DisconnectAll_Call { - _c.Call.Return() - return _c -} - -func (_c *RPCClient_DisconnectAll_Call) RunAndReturn(run func()) *RPCClient_DisconnectAll_Call { - _c.Call.Return(run) - return _c -} - -// EstimateGas provides a mock function with given fields: ctx, call -func (_m *RPCClient) EstimateGas(ctx context.Context, call interface{}) (uint64, error) { - ret := _m.Called(ctx, call) - - if len(ret) == 0 { - panic("no return value specified for EstimateGas") - } - - var r0 uint64 - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, interface{}) (uint64, error)); ok { - return rf(ctx, call) - } - if rf, ok := ret.Get(0).(func(context.Context, interface{}) uint64); ok { - r0 = rf(ctx, call) - } else { - r0 = ret.Get(0).(uint64) - } - - if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { - r1 = rf(ctx, call) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_EstimateGas_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EstimateGas' -type RPCClient_EstimateGas_Call struct { - *mock.Call -} - -// EstimateGas is a helper method to define mock.On call -// - ctx context.Context -// - call interface{} -func (_e *RPCClient_Expecter) EstimateGas(ctx interface{}, call interface{}) *RPCClient_EstimateGas_Call { - return &RPCClient_EstimateGas_Call{Call: _e.mock.On("EstimateGas", ctx, call)} -} - -func (_c *RPCClient_EstimateGas_Call) Run(run func(ctx context.Context, call interface{})) *RPCClient_EstimateGas_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(interface{})) - }) - return _c -} - -func (_c *RPCClient_EstimateGas_Call) Return(gas uint64, err error) *RPCClient_EstimateGas_Call { - _c.Call.Return(gas, err) - return _c -} - -func (_c *RPCClient_EstimateGas_Call) RunAndReturn(run func(context.Context, interface{}) (uint64, error)) *RPCClient_EstimateGas_Call { - _c.Call.Return(run) - return _c -} - -// FeeHistory provides a mock function with given fields: ctx, blockCount, rewardPercentiles -func (_m *RPCClient) FeeHistory(ctx context.Context, blockCount uint64, rewardPercentiles []float64) (*ethereum.FeeHistory, error) { - ret := _m.Called(ctx, blockCount, rewardPercentiles) - - if len(ret) == 0 { - panic("no return value specified for FeeHistory") - } - - var r0 *ethereum.FeeHistory - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, uint64, []float64) (*ethereum.FeeHistory, error)); ok { - return rf(ctx, blockCount, rewardPercentiles) - } - if rf, ok := ret.Get(0).(func(context.Context, uint64, []float64) *ethereum.FeeHistory); ok { - r0 = rf(ctx, blockCount, rewardPercentiles) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*ethereum.FeeHistory) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, uint64, []float64) error); ok { - r1 = rf(ctx, blockCount, rewardPercentiles) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_FeeHistory_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FeeHistory' -type RPCClient_FeeHistory_Call struct { - *mock.Call -} - -// FeeHistory is a helper method to define mock.On call -// - ctx context.Context -// - blockCount uint64 -// - rewardPercentiles []float64 -func (_e *RPCClient_Expecter) FeeHistory(ctx interface{}, blockCount interface{}, rewardPercentiles interface{}) *RPCClient_FeeHistory_Call { - return &RPCClient_FeeHistory_Call{Call: _e.mock.On("FeeHistory", ctx, blockCount, rewardPercentiles)} -} - -func (_c *RPCClient_FeeHistory_Call) Run(run func(ctx context.Context, blockCount uint64, rewardPercentiles []float64)) *RPCClient_FeeHistory_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(uint64), args[2].([]float64)) - }) - return _c -} - -func (_c *RPCClient_FeeHistory_Call) Return(feeHistory *ethereum.FeeHistory, err error) *RPCClient_FeeHistory_Call { - _c.Call.Return(feeHistory, err) - return _c -} - -func (_c *RPCClient_FeeHistory_Call) RunAndReturn(run func(context.Context, uint64, []float64) (*ethereum.FeeHistory, error)) *RPCClient_FeeHistory_Call { - _c.Call.Return(run) - return _c -} - -// FilterEvents provides a mock function with given fields: ctx, query -func (_m *RPCClient) FilterEvents(ctx context.Context, query ethereum.FilterQuery) ([]coretypes.Log, error) { - ret := _m.Called(ctx, query) - - if len(ret) == 0 { - panic("no return value specified for FilterEvents") - } - - var r0 []coretypes.Log - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery) ([]coretypes.Log, error)); ok { - return rf(ctx, query) - } - if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery) []coretypes.Log); ok { - r0 = rf(ctx, query) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]coretypes.Log) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, ethereum.FilterQuery) error); ok { - r1 = rf(ctx, query) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_FilterEvents_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FilterEvents' -type RPCClient_FilterEvents_Call struct { - *mock.Call -} - -// FilterEvents is a helper method to define mock.On call -// - ctx context.Context -// - query ethereum.FilterQuery -func (_e *RPCClient_Expecter) FilterEvents(ctx interface{}, query interface{}) *RPCClient_FilterEvents_Call { - return &RPCClient_FilterEvents_Call{Call: _e.mock.On("FilterEvents", ctx, query)} -} - -func (_c *RPCClient_FilterEvents_Call) Run(run func(ctx context.Context, query ethereum.FilterQuery)) *RPCClient_FilterEvents_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(ethereum.FilterQuery)) - }) - return _c -} - -func (_c *RPCClient_FilterEvents_Call) Return(_a0 []coretypes.Log, _a1 error) *RPCClient_FilterEvents_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_FilterEvents_Call) RunAndReturn(run func(context.Context, ethereum.FilterQuery) ([]coretypes.Log, error)) *RPCClient_FilterEvents_Call { - _c.Call.Return(run) - return _c -} - -// GetInterceptedChainInfo provides a mock function with given fields: -func (_m *RPCClient) GetInterceptedChainInfo() (commonclient.ChainInfo, commonclient.ChainInfo) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetInterceptedChainInfo") - } - - var r0 commonclient.ChainInfo - var r1 commonclient.ChainInfo - if rf, ok := ret.Get(0).(func() (commonclient.ChainInfo, commonclient.ChainInfo)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() commonclient.ChainInfo); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(commonclient.ChainInfo) - } - - if rf, ok := ret.Get(1).(func() commonclient.ChainInfo); ok { - r1 = rf() - } else { - r1 = ret.Get(1).(commonclient.ChainInfo) - } - - return r0, r1 -} - -// RPCClient_GetInterceptedChainInfo_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetInterceptedChainInfo' -type RPCClient_GetInterceptedChainInfo_Call struct { - *mock.Call -} - -// GetInterceptedChainInfo is a helper method to define mock.On call -func (_e *RPCClient_Expecter) GetInterceptedChainInfo() *RPCClient_GetInterceptedChainInfo_Call { - return &RPCClient_GetInterceptedChainInfo_Call{Call: _e.mock.On("GetInterceptedChainInfo")} -} - -func (_c *RPCClient_GetInterceptedChainInfo_Call) Run(run func()) *RPCClient_GetInterceptedChainInfo_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *RPCClient_GetInterceptedChainInfo_Call) Return(latest commonclient.ChainInfo, highestUserObservations commonclient.ChainInfo) *RPCClient_GetInterceptedChainInfo_Call { - _c.Call.Return(latest, highestUserObservations) - return _c -} - -func (_c *RPCClient_GetInterceptedChainInfo_Call) RunAndReturn(run func() (commonclient.ChainInfo, commonclient.ChainInfo)) *RPCClient_GetInterceptedChainInfo_Call { - _c.Call.Return(run) - return _c -} - -// HeaderByHash provides a mock function with given fields: ctx, h -func (_m *RPCClient) HeaderByHash(ctx context.Context, h common.Hash) (*coretypes.Header, error) { - ret := _m.Called(ctx, h) - - if len(ret) == 0 { - panic("no return value specified for HeaderByHash") - } - - var r0 *coretypes.Header - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*coretypes.Header, error)); ok { - return rf(ctx, h) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *coretypes.Header); ok { - r0 = rf(ctx, h) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*coretypes.Header) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { - r1 = rf(ctx, h) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_HeaderByHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HeaderByHash' -type RPCClient_HeaderByHash_Call struct { - *mock.Call -} - -// HeaderByHash is a helper method to define mock.On call -// - ctx context.Context -// - h common.Hash -func (_e *RPCClient_Expecter) HeaderByHash(ctx interface{}, h interface{}) *RPCClient_HeaderByHash_Call { - return &RPCClient_HeaderByHash_Call{Call: _e.mock.On("HeaderByHash", ctx, h)} -} - -func (_c *RPCClient_HeaderByHash_Call) Run(run func(ctx context.Context, h common.Hash)) *RPCClient_HeaderByHash_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Hash)) - }) - return _c -} - -func (_c *RPCClient_HeaderByHash_Call) Return(head *coretypes.Header, err error) *RPCClient_HeaderByHash_Call { - _c.Call.Return(head, err) - return _c -} - -func (_c *RPCClient_HeaderByHash_Call) RunAndReturn(run func(context.Context, common.Hash) (*coretypes.Header, error)) *RPCClient_HeaderByHash_Call { - _c.Call.Return(run) - return _c -} - -// HeaderByNumber provides a mock function with given fields: ctx, n -func (_m *RPCClient) HeaderByNumber(ctx context.Context, n *big.Int) (*coretypes.Header, error) { - ret := _m.Called(ctx, n) - - if len(ret) == 0 { - panic("no return value specified for HeaderByNumber") - } - - var r0 *coretypes.Header - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*coretypes.Header, error)); ok { - return rf(ctx, n) - } - if rf, ok := ret.Get(0).(func(context.Context, *big.Int) *coretypes.Header); ok { - r0 = rf(ctx, n) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*coretypes.Header) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { - r1 = rf(ctx, n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_HeaderByNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HeaderByNumber' -type RPCClient_HeaderByNumber_Call struct { - *mock.Call -} - -// HeaderByNumber is a helper method to define mock.On call -// - ctx context.Context -// - n *big.Int -func (_e *RPCClient_Expecter) HeaderByNumber(ctx interface{}, n interface{}) *RPCClient_HeaderByNumber_Call { - return &RPCClient_HeaderByNumber_Call{Call: _e.mock.On("HeaderByNumber", ctx, n)} -} - -func (_c *RPCClient_HeaderByNumber_Call) Run(run func(ctx context.Context, n *big.Int)) *RPCClient_HeaderByNumber_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*big.Int)) - }) - return _c -} - -func (_c *RPCClient_HeaderByNumber_Call) Return(head *coretypes.Header, err error) *RPCClient_HeaderByNumber_Call { - _c.Call.Return(head, err) - return _c -} - -func (_c *RPCClient_HeaderByNumber_Call) RunAndReturn(run func(context.Context, *big.Int) (*coretypes.Header, error)) *RPCClient_HeaderByNumber_Call { - _c.Call.Return(run) - return _c -} - -// IsSyncing provides a mock function with given fields: ctx -func (_m *RPCClient) IsSyncing(ctx context.Context) (bool, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for IsSyncing") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (bool, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) bool); ok { - r0 = rf(ctx) - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_IsSyncing_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsSyncing' -type RPCClient_IsSyncing_Call struct { - *mock.Call -} - -// IsSyncing is a helper method to define mock.On call -// - ctx context.Context -func (_e *RPCClient_Expecter) IsSyncing(ctx interface{}) *RPCClient_IsSyncing_Call { - return &RPCClient_IsSyncing_Call{Call: _e.mock.On("IsSyncing", ctx)} -} - -func (_c *RPCClient_IsSyncing_Call) Run(run func(ctx context.Context)) *RPCClient_IsSyncing_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *RPCClient_IsSyncing_Call) Return(_a0 bool, _a1 error) *RPCClient_IsSyncing_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_IsSyncing_Call) RunAndReturn(run func(context.Context) (bool, error)) *RPCClient_IsSyncing_Call { - _c.Call.Return(run) - return _c -} - -// LINKBalance provides a mock function with given fields: ctx, accountAddress, linkAddress -func (_m *RPCClient) LINKBalance(ctx context.Context, accountAddress common.Address, linkAddress common.Address) (*assets.Link, error) { - ret := _m.Called(ctx, accountAddress, linkAddress) - - if len(ret) == 0 { - panic("no return value specified for LINKBalance") - } - - var r0 *assets.Link - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Address, common.Address) (*assets.Link, error)); ok { - return rf(ctx, accountAddress, linkAddress) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Address, common.Address) *assets.Link); ok { - r0 = rf(ctx, accountAddress, linkAddress) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*assets.Link) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Address, common.Address) error); ok { - r1 = rf(ctx, accountAddress, linkAddress) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_LINKBalance_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LINKBalance' -type RPCClient_LINKBalance_Call struct { - *mock.Call -} - -// LINKBalance is a helper method to define mock.On call -// - ctx context.Context -// - accountAddress common.Address -// - linkAddress common.Address -func (_e *RPCClient_Expecter) LINKBalance(ctx interface{}, accountAddress interface{}, linkAddress interface{}) *RPCClient_LINKBalance_Call { - return &RPCClient_LINKBalance_Call{Call: _e.mock.On("LINKBalance", ctx, accountAddress, linkAddress)} -} - -func (_c *RPCClient_LINKBalance_Call) Run(run func(ctx context.Context, accountAddress common.Address, linkAddress common.Address)) *RPCClient_LINKBalance_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Address), args[2].(common.Address)) - }) - return _c -} - -func (_c *RPCClient_LINKBalance_Call) Return(_a0 *assets.Link, _a1 error) *RPCClient_LINKBalance_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_LINKBalance_Call) RunAndReturn(run func(context.Context, common.Address, common.Address) (*assets.Link, error)) *RPCClient_LINKBalance_Call { - _c.Call.Return(run) - return _c -} - -// LatestBlockHeight provides a mock function with given fields: _a0 -func (_m *RPCClient) LatestBlockHeight(_a0 context.Context) (*big.Int, error) { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for LatestBlockHeight") - } - - var r0 *big.Int - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (*big.Int, error)); ok { - return rf(_a0) - } - if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { - r0 = rf(_a0) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*big.Int) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(_a0) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_LatestBlockHeight_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LatestBlockHeight' -type RPCClient_LatestBlockHeight_Call struct { - *mock.Call -} - -// LatestBlockHeight is a helper method to define mock.On call -// - _a0 context.Context -func (_e *RPCClient_Expecter) LatestBlockHeight(_a0 interface{}) *RPCClient_LatestBlockHeight_Call { - return &RPCClient_LatestBlockHeight_Call{Call: _e.mock.On("LatestBlockHeight", _a0)} -} - -func (_c *RPCClient_LatestBlockHeight_Call) Run(run func(_a0 context.Context)) *RPCClient_LatestBlockHeight_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *RPCClient_LatestBlockHeight_Call) Return(_a0 *big.Int, _a1 error) *RPCClient_LatestBlockHeight_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_LatestBlockHeight_Call) RunAndReturn(run func(context.Context) (*big.Int, error)) *RPCClient_LatestBlockHeight_Call { - _c.Call.Return(run) - return _c -} - -// LatestFinalizedBlock provides a mock function with given fields: ctx -func (_m *RPCClient) LatestFinalizedBlock(ctx context.Context) (*types.Head, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for LatestFinalizedBlock") - } - - var r0 *types.Head - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (*types.Head, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) *types.Head); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*types.Head) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_LatestFinalizedBlock_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LatestFinalizedBlock' -type RPCClient_LatestFinalizedBlock_Call struct { - *mock.Call -} - -// LatestFinalizedBlock is a helper method to define mock.On call -// - ctx context.Context -func (_e *RPCClient_Expecter) LatestFinalizedBlock(ctx interface{}) *RPCClient_LatestFinalizedBlock_Call { - return &RPCClient_LatestFinalizedBlock_Call{Call: _e.mock.On("LatestFinalizedBlock", ctx)} -} - -func (_c *RPCClient_LatestFinalizedBlock_Call) Run(run func(ctx context.Context)) *RPCClient_LatestFinalizedBlock_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *RPCClient_LatestFinalizedBlock_Call) Return(_a0 *types.Head, _a1 error) *RPCClient_LatestFinalizedBlock_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_LatestFinalizedBlock_Call) RunAndReturn(run func(context.Context) (*types.Head, error)) *RPCClient_LatestFinalizedBlock_Call { - _c.Call.Return(run) - return _c -} - -// PendingCallContract provides a mock function with given fields: ctx, msg -func (_m *RPCClient) PendingCallContract(ctx context.Context, msg interface{}) ([]byte, error) { - ret := _m.Called(ctx, msg) - - if len(ret) == 0 { - panic("no return value specified for PendingCallContract") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, interface{}) ([]byte, error)); ok { - return rf(ctx, msg) - } - if rf, ok := ret.Get(0).(func(context.Context, interface{}) []byte); ok { - r0 = rf(ctx, msg) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { - r1 = rf(ctx, msg) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_PendingCallContract_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PendingCallContract' -type RPCClient_PendingCallContract_Call struct { - *mock.Call -} - -// PendingCallContract is a helper method to define mock.On call -// - ctx context.Context -// - msg interface{} -func (_e *RPCClient_Expecter) PendingCallContract(ctx interface{}, msg interface{}) *RPCClient_PendingCallContract_Call { - return &RPCClient_PendingCallContract_Call{Call: _e.mock.On("PendingCallContract", ctx, msg)} -} - -func (_c *RPCClient_PendingCallContract_Call) Run(run func(ctx context.Context, msg interface{})) *RPCClient_PendingCallContract_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(interface{})) - }) - return _c -} - -func (_c *RPCClient_PendingCallContract_Call) Return(rpcErr []byte, extractErr error) *RPCClient_PendingCallContract_Call { - _c.Call.Return(rpcErr, extractErr) - return _c -} - -func (_c *RPCClient_PendingCallContract_Call) RunAndReturn(run func(context.Context, interface{}) ([]byte, error)) *RPCClient_PendingCallContract_Call { - _c.Call.Return(run) - return _c -} - -// PendingCodeAt provides a mock function with given fields: ctx, account -func (_m *RPCClient) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) { - ret := _m.Called(ctx, account) - - if len(ret) == 0 { - panic("no return value specified for PendingCodeAt") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Address) ([]byte, error)); ok { - return rf(ctx, account) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Address) []byte); ok { - r0 = rf(ctx, account) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Address) error); ok { - r1 = rf(ctx, account) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_PendingCodeAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PendingCodeAt' -type RPCClient_PendingCodeAt_Call struct { - *mock.Call -} - -// PendingCodeAt is a helper method to define mock.On call -// - ctx context.Context -// - account common.Address -func (_e *RPCClient_Expecter) PendingCodeAt(ctx interface{}, account interface{}) *RPCClient_PendingCodeAt_Call { - return &RPCClient_PendingCodeAt_Call{Call: _e.mock.On("PendingCodeAt", ctx, account)} -} - -func (_c *RPCClient_PendingCodeAt_Call) Run(run func(ctx context.Context, account common.Address)) *RPCClient_PendingCodeAt_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Address)) - }) - return _c -} - -func (_c *RPCClient_PendingCodeAt_Call) Return(b []byte, err error) *RPCClient_PendingCodeAt_Call { - _c.Call.Return(b, err) - return _c -} - -func (_c *RPCClient_PendingCodeAt_Call) RunAndReturn(run func(context.Context, common.Address) ([]byte, error)) *RPCClient_PendingCodeAt_Call { - _c.Call.Return(run) - return _c -} - -// PendingSequenceAt provides a mock function with given fields: ctx, addr -func (_m *RPCClient) PendingSequenceAt(ctx context.Context, addr common.Address) (types.Nonce, error) { - ret := _m.Called(ctx, addr) - - if len(ret) == 0 { - panic("no return value specified for PendingSequenceAt") - } - - var r0 types.Nonce - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Address) (types.Nonce, error)); ok { - return rf(ctx, addr) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Address) types.Nonce); ok { - r0 = rf(ctx, addr) - } else { - r0 = ret.Get(0).(types.Nonce) - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Address) error); ok { - r1 = rf(ctx, addr) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_PendingSequenceAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PendingSequenceAt' -type RPCClient_PendingSequenceAt_Call struct { - *mock.Call -} - -// PendingSequenceAt is a helper method to define mock.On call -// - ctx context.Context -// - addr common.Address -func (_e *RPCClient_Expecter) PendingSequenceAt(ctx interface{}, addr interface{}) *RPCClient_PendingSequenceAt_Call { - return &RPCClient_PendingSequenceAt_Call{Call: _e.mock.On("PendingSequenceAt", ctx, addr)} -} - -func (_c *RPCClient_PendingSequenceAt_Call) Run(run func(ctx context.Context, addr common.Address)) *RPCClient_PendingSequenceAt_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Address)) - }) - return _c -} - -func (_c *RPCClient_PendingSequenceAt_Call) Return(_a0 types.Nonce, _a1 error) *RPCClient_PendingSequenceAt_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_PendingSequenceAt_Call) RunAndReturn(run func(context.Context, common.Address) (types.Nonce, error)) *RPCClient_PendingSequenceAt_Call { - _c.Call.Return(run) - return _c -} - -// SendEmptyTransaction provides a mock function with given fields: ctx, newTxAttempt, seq, gasLimit, fee, fromAddress -func (_m *RPCClient) SendEmptyTransaction(ctx context.Context, newTxAttempt func(types.Nonce, uint32, *evmassets.Wei, common.Address) (interface{}, error), seq types.Nonce, gasLimit uint32, fee *evmassets.Wei, fromAddress common.Address) (string, error) { - ret := _m.Called(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) - - if len(ret) == 0 { - panic("no return value specified for SendEmptyTransaction") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, func(types.Nonce, uint32, *evmassets.Wei, common.Address) (interface{}, error), types.Nonce, uint32, *evmassets.Wei, common.Address) (string, error)); ok { - return rf(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) - } - if rf, ok := ret.Get(0).(func(context.Context, func(types.Nonce, uint32, *evmassets.Wei, common.Address) (interface{}, error), types.Nonce, uint32, *evmassets.Wei, common.Address) string); ok { - r0 = rf(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func(context.Context, func(types.Nonce, uint32, *evmassets.Wei, common.Address) (interface{}, error), types.Nonce, uint32, *evmassets.Wei, common.Address) error); ok { - r1 = rf(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_SendEmptyTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendEmptyTransaction' -type RPCClient_SendEmptyTransaction_Call struct { - *mock.Call -} - -// SendEmptyTransaction is a helper method to define mock.On call -// - ctx context.Context -// - newTxAttempt func(types.Nonce , uint32 , *evmassets.Wei , common.Address)(interface{} , error) -// - seq types.Nonce -// - gasLimit uint32 -// - fee *evmassets.Wei -// - fromAddress common.Address -func (_e *RPCClient_Expecter) SendEmptyTransaction(ctx interface{}, newTxAttempt interface{}, seq interface{}, gasLimit interface{}, fee interface{}, fromAddress interface{}) *RPCClient_SendEmptyTransaction_Call { - return &RPCClient_SendEmptyTransaction_Call{Call: _e.mock.On("SendEmptyTransaction", ctx, newTxAttempt, seq, gasLimit, fee, fromAddress)} -} - -func (_c *RPCClient_SendEmptyTransaction_Call) Run(run func(ctx context.Context, newTxAttempt func(types.Nonce, uint32, *evmassets.Wei, common.Address) (interface{}, error), seq types.Nonce, gasLimit uint32, fee *evmassets.Wei, fromAddress common.Address)) *RPCClient_SendEmptyTransaction_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(func(types.Nonce, uint32, *evmassets.Wei, common.Address) (interface{}, error)), args[2].(types.Nonce), args[3].(uint32), args[4].(*evmassets.Wei), args[5].(common.Address)) - }) - return _c -} - -func (_c *RPCClient_SendEmptyTransaction_Call) Return(txhash string, err error) *RPCClient_SendEmptyTransaction_Call { - _c.Call.Return(txhash, err) - return _c -} - -func (_c *RPCClient_SendEmptyTransaction_Call) RunAndReturn(run func(context.Context, func(types.Nonce, uint32, *evmassets.Wei, common.Address) (interface{}, error), types.Nonce, uint32, *evmassets.Wei, common.Address) (string, error)) *RPCClient_SendEmptyTransaction_Call { - _c.Call.Return(run) - return _c -} - -// SendTransaction provides a mock function with given fields: ctx, tx -func (_m *RPCClient) SendTransaction(ctx context.Context, tx *coretypes.Transaction) error { - ret := _m.Called(ctx, tx) - - if len(ret) == 0 { - panic("no return value specified for SendTransaction") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *coretypes.Transaction) error); ok { - r0 = rf(ctx, tx) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// RPCClient_SendTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendTransaction' -type RPCClient_SendTransaction_Call struct { - *mock.Call -} - -// SendTransaction is a helper method to define mock.On call -// - ctx context.Context -// - tx *coretypes.Transaction -func (_e *RPCClient_Expecter) SendTransaction(ctx interface{}, tx interface{}) *RPCClient_SendTransaction_Call { - return &RPCClient_SendTransaction_Call{Call: _e.mock.On("SendTransaction", ctx, tx)} -} - -func (_c *RPCClient_SendTransaction_Call) Run(run func(ctx context.Context, tx *coretypes.Transaction)) *RPCClient_SendTransaction_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*coretypes.Transaction)) - }) - return _c -} - -func (_c *RPCClient_SendTransaction_Call) Return(_a0 error) *RPCClient_SendTransaction_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *RPCClient_SendTransaction_Call) RunAndReturn(run func(context.Context, *coretypes.Transaction) error) *RPCClient_SendTransaction_Call { - _c.Call.Return(run) - return _c -} - -// SequenceAt provides a mock function with given fields: ctx, accountAddress, blockNumber -func (_m *RPCClient) SequenceAt(ctx context.Context, accountAddress common.Address, blockNumber *big.Int) (types.Nonce, error) { - ret := _m.Called(ctx, accountAddress, blockNumber) - - if len(ret) == 0 { - panic("no return value specified for SequenceAt") - } - - var r0 types.Nonce - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) (types.Nonce, error)); ok { - return rf(ctx, accountAddress, blockNumber) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) types.Nonce); ok { - r0 = rf(ctx, accountAddress, blockNumber) - } else { - r0 = ret.Get(0).(types.Nonce) - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Address, *big.Int) error); ok { - r1 = rf(ctx, accountAddress, blockNumber) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_SequenceAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SequenceAt' -type RPCClient_SequenceAt_Call struct { - *mock.Call -} - -// SequenceAt is a helper method to define mock.On call -// - ctx context.Context -// - accountAddress common.Address -// - blockNumber *big.Int -func (_e *RPCClient_Expecter) SequenceAt(ctx interface{}, accountAddress interface{}, blockNumber interface{}) *RPCClient_SequenceAt_Call { - return &RPCClient_SequenceAt_Call{Call: _e.mock.On("SequenceAt", ctx, accountAddress, blockNumber)} -} - -func (_c *RPCClient_SequenceAt_Call) Run(run func(ctx context.Context, accountAddress common.Address, blockNumber *big.Int)) *RPCClient_SequenceAt_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Address), args[2].(*big.Int)) - }) - return _c -} - -func (_c *RPCClient_SequenceAt_Call) Return(_a0 types.Nonce, _a1 error) *RPCClient_SequenceAt_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_SequenceAt_Call) RunAndReturn(run func(context.Context, common.Address, *big.Int) (types.Nonce, error)) *RPCClient_SequenceAt_Call { - _c.Call.Return(run) - return _c -} - -// SetAliveLoopSub provides a mock function with given fields: _a0 -func (_m *RPCClient) SetAliveLoopSub(_a0 commontypes.Subscription) { - _m.Called(_a0) -} - -// RPCClient_SetAliveLoopSub_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetAliveLoopSub' -type RPCClient_SetAliveLoopSub_Call struct { - *mock.Call -} - -// SetAliveLoopSub is a helper method to define mock.On call -// - _a0 commontypes.Subscription -func (_e *RPCClient_Expecter) SetAliveLoopSub(_a0 interface{}) *RPCClient_SetAliveLoopSub_Call { - return &RPCClient_SetAliveLoopSub_Call{Call: _e.mock.On("SetAliveLoopSub", _a0)} -} - -func (_c *RPCClient_SetAliveLoopSub_Call) Run(run func(_a0 commontypes.Subscription)) *RPCClient_SetAliveLoopSub_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(commontypes.Subscription)) - }) - return _c -} - -func (_c *RPCClient_SetAliveLoopSub_Call) Return() *RPCClient_SetAliveLoopSub_Call { - _c.Call.Return() - return _c -} - -func (_c *RPCClient_SetAliveLoopSub_Call) RunAndReturn(run func(commontypes.Subscription)) *RPCClient_SetAliveLoopSub_Call { - _c.Call.Return(run) - return _c -} - -// SimulateTransaction provides a mock function with given fields: ctx, tx -func (_m *RPCClient) SimulateTransaction(ctx context.Context, tx *coretypes.Transaction) error { - ret := _m.Called(ctx, tx) - - if len(ret) == 0 { - panic("no return value specified for SimulateTransaction") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *coretypes.Transaction) error); ok { - r0 = rf(ctx, tx) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// RPCClient_SimulateTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SimulateTransaction' -type RPCClient_SimulateTransaction_Call struct { - *mock.Call -} - -// SimulateTransaction is a helper method to define mock.On call -// - ctx context.Context -// - tx *coretypes.Transaction -func (_e *RPCClient_Expecter) SimulateTransaction(ctx interface{}, tx interface{}) *RPCClient_SimulateTransaction_Call { - return &RPCClient_SimulateTransaction_Call{Call: _e.mock.On("SimulateTransaction", ctx, tx)} -} - -func (_c *RPCClient_SimulateTransaction_Call) Run(run func(ctx context.Context, tx *coretypes.Transaction)) *RPCClient_SimulateTransaction_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*coretypes.Transaction)) - }) - return _c -} - -func (_c *RPCClient_SimulateTransaction_Call) Return(_a0 error) *RPCClient_SimulateTransaction_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *RPCClient_SimulateTransaction_Call) RunAndReturn(run func(context.Context, *coretypes.Transaction) error) *RPCClient_SimulateTransaction_Call { - _c.Call.Return(run) - return _c -} - -// SubscribeFilterLogs provides a mock function with given fields: ctx, q, ch -func (_m *RPCClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- coretypes.Log) (ethereum.Subscription, error) { - ret := _m.Called(ctx, q, ch) - - if len(ret) == 0 { - panic("no return value specified for SubscribeFilterLogs") - } - - var r0 ethereum.Subscription - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) (ethereum.Subscription, error)); ok { - return rf(ctx, q, ch) - } - if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) ethereum.Subscription); ok { - r0 = rf(ctx, q, ch) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(ethereum.Subscription) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) error); ok { - r1 = rf(ctx, q, ch) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_SubscribeFilterLogs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeFilterLogs' -type RPCClient_SubscribeFilterLogs_Call struct { - *mock.Call -} - -// SubscribeFilterLogs is a helper method to define mock.On call -// - ctx context.Context -// - q ethereum.FilterQuery -// - ch chan<- coretypes.Log -func (_e *RPCClient_Expecter) SubscribeFilterLogs(ctx interface{}, q interface{}, ch interface{}) *RPCClient_SubscribeFilterLogs_Call { - return &RPCClient_SubscribeFilterLogs_Call{Call: _e.mock.On("SubscribeFilterLogs", ctx, q, ch)} -} - -func (_c *RPCClient_SubscribeFilterLogs_Call) Run(run func(ctx context.Context, q ethereum.FilterQuery, ch chan<- coretypes.Log)) *RPCClient_SubscribeFilterLogs_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(ethereum.FilterQuery), args[2].(chan<- coretypes.Log)) - }) - return _c -} - -func (_c *RPCClient_SubscribeFilterLogs_Call) Return(s ethereum.Subscription, err error) *RPCClient_SubscribeFilterLogs_Call { - _c.Call.Return(s, err) - return _c -} - -func (_c *RPCClient_SubscribeFilterLogs_Call) RunAndReturn(run func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) (ethereum.Subscription, error)) *RPCClient_SubscribeFilterLogs_Call { - _c.Call.Return(run) - return _c -} - -// SubscribeNewHead provides a mock function with given fields: ctx, channel -func (_m *RPCClient) SubscribeNewHead(ctx context.Context, channel chan<- *types.Head) (commontypes.Subscription, error) { - ret := _m.Called(ctx, channel) - - if len(ret) == 0 { - panic("no return value specified for SubscribeNewHead") - } - - var r0 commontypes.Subscription - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, chan<- *types.Head) (commontypes.Subscription, error)); ok { - return rf(ctx, channel) - } - if rf, ok := ret.Get(0).(func(context.Context, chan<- *types.Head) commontypes.Subscription); ok { - r0 = rf(ctx, channel) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(commontypes.Subscription) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, chan<- *types.Head) error); ok { - r1 = rf(ctx, channel) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_SubscribeNewHead_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeNewHead' -type RPCClient_SubscribeNewHead_Call struct { - *mock.Call -} - -// SubscribeNewHead is a helper method to define mock.On call -// - ctx context.Context -// - channel chan<- *types.Head -func (_e *RPCClient_Expecter) SubscribeNewHead(ctx interface{}, channel interface{}) *RPCClient_SubscribeNewHead_Call { - return &RPCClient_SubscribeNewHead_Call{Call: _e.mock.On("SubscribeNewHead", ctx, channel)} -} - -func (_c *RPCClient_SubscribeNewHead_Call) Run(run func(ctx context.Context, channel chan<- *types.Head)) *RPCClient_SubscribeNewHead_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(chan<- *types.Head)) - }) - return _c -} - -func (_c *RPCClient_SubscribeNewHead_Call) Return(s commontypes.Subscription, err error) *RPCClient_SubscribeNewHead_Call { - _c.Call.Return(s, err) - return _c -} - -func (_c *RPCClient_SubscribeNewHead_Call) RunAndReturn(run func(context.Context, chan<- *types.Head) (commontypes.Subscription, error)) *RPCClient_SubscribeNewHead_Call { - _c.Call.Return(run) - return _c -} - -// SubscribeToFinalizedHeads provides a mock function with given fields: _a0 -func (_m *RPCClient) SubscribeToFinalizedHeads(_a0 context.Context) (<-chan *types.Head, commontypes.Subscription, error) { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for SubscribeToFinalizedHeads") - } - - var r0 <-chan *types.Head - var r1 commontypes.Subscription - var r2 error - if rf, ok := ret.Get(0).(func(context.Context) (<-chan *types.Head, commontypes.Subscription, error)); ok { - return rf(_a0) - } - if rf, ok := ret.Get(0).(func(context.Context) <-chan *types.Head); ok { - r0 = rf(_a0) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan *types.Head) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) commontypes.Subscription); ok { - r1 = rf(_a0) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(commontypes.Subscription) - } - } - - if rf, ok := ret.Get(2).(func(context.Context) error); ok { - r2 = rf(_a0) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// RPCClient_SubscribeToFinalizedHeads_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeToFinalizedHeads' -type RPCClient_SubscribeToFinalizedHeads_Call struct { - *mock.Call -} - -// SubscribeToFinalizedHeads is a helper method to define mock.On call -// - _a0 context.Context -func (_e *RPCClient_Expecter) SubscribeToFinalizedHeads(_a0 interface{}) *RPCClient_SubscribeToFinalizedHeads_Call { - return &RPCClient_SubscribeToFinalizedHeads_Call{Call: _e.mock.On("SubscribeToFinalizedHeads", _a0)} -} - -func (_c *RPCClient_SubscribeToFinalizedHeads_Call) Run(run func(_a0 context.Context)) *RPCClient_SubscribeToFinalizedHeads_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *RPCClient_SubscribeToFinalizedHeads_Call) Return(_a0 <-chan *types.Head, _a1 commontypes.Subscription, _a2 error) *RPCClient_SubscribeToFinalizedHeads_Call { - _c.Call.Return(_a0, _a1, _a2) - return _c -} - -func (_c *RPCClient_SubscribeToFinalizedHeads_Call) RunAndReturn(run func(context.Context) (<-chan *types.Head, commontypes.Subscription, error)) *RPCClient_SubscribeToFinalizedHeads_Call { - _c.Call.Return(run) - return _c -} - -// SubscribeToHeads provides a mock function with given fields: ctx -func (_m *RPCClient) SubscribeToHeads(ctx context.Context) (<-chan *types.Head, commontypes.Subscription, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for SubscribeToHeads") - } - - var r0 <-chan *types.Head - var r1 commontypes.Subscription - var r2 error - if rf, ok := ret.Get(0).(func(context.Context) (<-chan *types.Head, commontypes.Subscription, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) <-chan *types.Head); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan *types.Head) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) commontypes.Subscription); ok { - r1 = rf(ctx) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(commontypes.Subscription) - } - } - - if rf, ok := ret.Get(2).(func(context.Context) error); ok { - r2 = rf(ctx) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// RPCClient_SubscribeToHeads_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeToHeads' -type RPCClient_SubscribeToHeads_Call struct { - *mock.Call -} - -// SubscribeToHeads is a helper method to define mock.On call -// - ctx context.Context -func (_e *RPCClient_Expecter) SubscribeToHeads(ctx interface{}) *RPCClient_SubscribeToHeads_Call { - return &RPCClient_SubscribeToHeads_Call{Call: _e.mock.On("SubscribeToHeads", ctx)} -} - -func (_c *RPCClient_SubscribeToHeads_Call) Run(run func(ctx context.Context)) *RPCClient_SubscribeToHeads_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *RPCClient_SubscribeToHeads_Call) Return(ch <-chan *types.Head, sub commontypes.Subscription, err error) *RPCClient_SubscribeToHeads_Call { - _c.Call.Return(ch, sub, err) - return _c -} - -func (_c *RPCClient_SubscribeToHeads_Call) RunAndReturn(run func(context.Context) (<-chan *types.Head, commontypes.Subscription, error)) *RPCClient_SubscribeToHeads_Call { - _c.Call.Return(run) - return _c -} - -// SubscribersCount provides a mock function with given fields: -func (_m *RPCClient) SubscribersCount() int32 { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for SubscribersCount") - } - - var r0 int32 - if rf, ok := ret.Get(0).(func() int32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int32) - } - - return r0 -} - -// RPCClient_SubscribersCount_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribersCount' -type RPCClient_SubscribersCount_Call struct { - *mock.Call -} - -// SubscribersCount is a helper method to define mock.On call -func (_e *RPCClient_Expecter) SubscribersCount() *RPCClient_SubscribersCount_Call { - return &RPCClient_SubscribersCount_Call{Call: _e.mock.On("SubscribersCount")} -} - -func (_c *RPCClient_SubscribersCount_Call) Run(run func()) *RPCClient_SubscribersCount_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *RPCClient_SubscribersCount_Call) Return(_a0 int32) *RPCClient_SubscribersCount_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *RPCClient_SubscribersCount_Call) RunAndReturn(run func() int32) *RPCClient_SubscribersCount_Call { - _c.Call.Return(run) - return _c -} - -// SuggestGasPrice provides a mock function with given fields: ctx -func (_m *RPCClient) SuggestGasPrice(ctx context.Context) (*big.Int, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for SuggestGasPrice") - } - - var r0 *big.Int - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (*big.Int, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*big.Int) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_SuggestGasPrice_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SuggestGasPrice' -type RPCClient_SuggestGasPrice_Call struct { - *mock.Call -} - -// SuggestGasPrice is a helper method to define mock.On call -// - ctx context.Context -func (_e *RPCClient_Expecter) SuggestGasPrice(ctx interface{}) *RPCClient_SuggestGasPrice_Call { - return &RPCClient_SuggestGasPrice_Call{Call: _e.mock.On("SuggestGasPrice", ctx)} -} - -func (_c *RPCClient_SuggestGasPrice_Call) Run(run func(ctx context.Context)) *RPCClient_SuggestGasPrice_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *RPCClient_SuggestGasPrice_Call) Return(p *big.Int, err error) *RPCClient_SuggestGasPrice_Call { - _c.Call.Return(p, err) - return _c -} - -func (_c *RPCClient_SuggestGasPrice_Call) RunAndReturn(run func(context.Context) (*big.Int, error)) *RPCClient_SuggestGasPrice_Call { - _c.Call.Return(run) - return _c -} - -// SuggestGasTipCap provides a mock function with given fields: ctx -func (_m *RPCClient) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for SuggestGasTipCap") - } - - var r0 *big.Int - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (*big.Int, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*big.Int) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_SuggestGasTipCap_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SuggestGasTipCap' -type RPCClient_SuggestGasTipCap_Call struct { - *mock.Call -} - -// SuggestGasTipCap is a helper method to define mock.On call -// - ctx context.Context -func (_e *RPCClient_Expecter) SuggestGasTipCap(ctx interface{}) *RPCClient_SuggestGasTipCap_Call { - return &RPCClient_SuggestGasTipCap_Call{Call: _e.mock.On("SuggestGasTipCap", ctx)} -} - -func (_c *RPCClient_SuggestGasTipCap_Call) Run(run func(ctx context.Context)) *RPCClient_SuggestGasTipCap_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *RPCClient_SuggestGasTipCap_Call) Return(t *big.Int, err error) *RPCClient_SuggestGasTipCap_Call { - _c.Call.Return(t, err) - return _c -} - -func (_c *RPCClient_SuggestGasTipCap_Call) RunAndReturn(run func(context.Context) (*big.Int, error)) *RPCClient_SuggestGasTipCap_Call { - _c.Call.Return(run) - return _c -} - -// TokenBalance provides a mock function with given fields: ctx, accountAddress, tokenAddress -func (_m *RPCClient) TokenBalance(ctx context.Context, accountAddress common.Address, tokenAddress common.Address) (*big.Int, error) { - ret := _m.Called(ctx, accountAddress, tokenAddress) - - if len(ret) == 0 { - panic("no return value specified for TokenBalance") - } - - var r0 *big.Int - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Address, common.Address) (*big.Int, error)); ok { - return rf(ctx, accountAddress, tokenAddress) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Address, common.Address) *big.Int); ok { - r0 = rf(ctx, accountAddress, tokenAddress) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*big.Int) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Address, common.Address) error); ok { - r1 = rf(ctx, accountAddress, tokenAddress) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_TokenBalance_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TokenBalance' -type RPCClient_TokenBalance_Call struct { - *mock.Call -} - -// TokenBalance is a helper method to define mock.On call -// - ctx context.Context -// - accountAddress common.Address -// - tokenAddress common.Address -func (_e *RPCClient_Expecter) TokenBalance(ctx interface{}, accountAddress interface{}, tokenAddress interface{}) *RPCClient_TokenBalance_Call { - return &RPCClient_TokenBalance_Call{Call: _e.mock.On("TokenBalance", ctx, accountAddress, tokenAddress)} -} - -func (_c *RPCClient_TokenBalance_Call) Run(run func(ctx context.Context, accountAddress common.Address, tokenAddress common.Address)) *RPCClient_TokenBalance_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Address), args[2].(common.Address)) - }) - return _c -} - -func (_c *RPCClient_TokenBalance_Call) Return(_a0 *big.Int, _a1 error) *RPCClient_TokenBalance_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_TokenBalance_Call) RunAndReturn(run func(context.Context, common.Address, common.Address) (*big.Int, error)) *RPCClient_TokenBalance_Call { - _c.Call.Return(run) - return _c -} - -// TransactionByHash provides a mock function with given fields: ctx, txHash -func (_m *RPCClient) TransactionByHash(ctx context.Context, txHash common.Hash) (*coretypes.Transaction, error) { - ret := _m.Called(ctx, txHash) - - if len(ret) == 0 { - panic("no return value specified for TransactionByHash") - } - - var r0 *coretypes.Transaction - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*coretypes.Transaction, error)); ok { - return rf(ctx, txHash) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *coretypes.Transaction); ok { - r0 = rf(ctx, txHash) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*coretypes.Transaction) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { - r1 = rf(ctx, txHash) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_TransactionByHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TransactionByHash' -type RPCClient_TransactionByHash_Call struct { - *mock.Call -} - -// TransactionByHash is a helper method to define mock.On call -// - ctx context.Context -// - txHash common.Hash -func (_e *RPCClient_Expecter) TransactionByHash(ctx interface{}, txHash interface{}) *RPCClient_TransactionByHash_Call { - return &RPCClient_TransactionByHash_Call{Call: _e.mock.On("TransactionByHash", ctx, txHash)} -} - -func (_c *RPCClient_TransactionByHash_Call) Run(run func(ctx context.Context, txHash common.Hash)) *RPCClient_TransactionByHash_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Hash)) - }) - return _c -} - -func (_c *RPCClient_TransactionByHash_Call) Return(_a0 *coretypes.Transaction, _a1 error) *RPCClient_TransactionByHash_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_TransactionByHash_Call) RunAndReturn(run func(context.Context, common.Hash) (*coretypes.Transaction, error)) *RPCClient_TransactionByHash_Call { - _c.Call.Return(run) - return _c -} - -// TransactionReceipt provides a mock function with given fields: ctx, txHash -func (_m *RPCClient) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { - ret := _m.Called(ctx, txHash) - - if len(ret) == 0 { - panic("no return value specified for TransactionReceipt") - } - - var r0 *types.Receipt - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*types.Receipt, error)); ok { - return rf(ctx, txHash) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *types.Receipt); ok { - r0 = rf(ctx, txHash) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*types.Receipt) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { - r1 = rf(ctx, txHash) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_TransactionReceipt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TransactionReceipt' -type RPCClient_TransactionReceipt_Call struct { - *mock.Call -} - -// TransactionReceipt is a helper method to define mock.On call -// - ctx context.Context -// - txHash common.Hash -func (_e *RPCClient_Expecter) TransactionReceipt(ctx interface{}, txHash interface{}) *RPCClient_TransactionReceipt_Call { - return &RPCClient_TransactionReceipt_Call{Call: _e.mock.On("TransactionReceipt", ctx, txHash)} -} - -func (_c *RPCClient_TransactionReceipt_Call) Run(run func(ctx context.Context, txHash common.Hash)) *RPCClient_TransactionReceipt_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Hash)) - }) - return _c -} - -func (_c *RPCClient_TransactionReceipt_Call) Return(_a0 *types.Receipt, _a1 error) *RPCClient_TransactionReceipt_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_TransactionReceipt_Call) RunAndReturn(run func(context.Context, common.Hash) (*types.Receipt, error)) *RPCClient_TransactionReceipt_Call { - _c.Call.Return(run) - return _c -} - -// TransactionReceiptGeth provides a mock function with given fields: ctx, txHash -func (_m *RPCClient) TransactionReceiptGeth(ctx context.Context, txHash common.Hash) (*coretypes.Receipt, error) { - ret := _m.Called(ctx, txHash) - - if len(ret) == 0 { - panic("no return value specified for TransactionReceiptGeth") - } - - var r0 *coretypes.Receipt - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*coretypes.Receipt, error)); ok { - return rf(ctx, txHash) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *coretypes.Receipt); ok { - r0 = rf(ctx, txHash) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*coretypes.Receipt) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { - r1 = rf(ctx, txHash) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_TransactionReceiptGeth_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TransactionReceiptGeth' -type RPCClient_TransactionReceiptGeth_Call struct { - *mock.Call -} - -// TransactionReceiptGeth is a helper method to define mock.On call -// - ctx context.Context -// - txHash common.Hash -func (_e *RPCClient_Expecter) TransactionReceiptGeth(ctx interface{}, txHash interface{}) *RPCClient_TransactionReceiptGeth_Call { - return &RPCClient_TransactionReceiptGeth_Call{Call: _e.mock.On("TransactionReceiptGeth", ctx, txHash)} -} - -func (_c *RPCClient_TransactionReceiptGeth_Call) Run(run func(ctx context.Context, txHash common.Hash)) *RPCClient_TransactionReceiptGeth_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Hash)) - }) - return _c -} - -func (_c *RPCClient_TransactionReceiptGeth_Call) Return(r *coretypes.Receipt, err error) *RPCClient_TransactionReceiptGeth_Call { - _c.Call.Return(r, err) - return _c -} - -func (_c *RPCClient_TransactionReceiptGeth_Call) RunAndReturn(run func(context.Context, common.Hash) (*coretypes.Receipt, error)) *RPCClient_TransactionReceiptGeth_Call { - _c.Call.Return(run) - return _c -} - -// UnsubscribeAllExceptAliveLoop provides a mock function with given fields: -func (_m *RPCClient) UnsubscribeAllExceptAliveLoop() { - _m.Called() -} - -// RPCClient_UnsubscribeAllExceptAliveLoop_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UnsubscribeAllExceptAliveLoop' -type RPCClient_UnsubscribeAllExceptAliveLoop_Call struct { - *mock.Call -} - -// UnsubscribeAllExceptAliveLoop is a helper method to define mock.On call -func (_e *RPCClient_Expecter) UnsubscribeAllExceptAliveLoop() *RPCClient_UnsubscribeAllExceptAliveLoop_Call { - return &RPCClient_UnsubscribeAllExceptAliveLoop_Call{Call: _e.mock.On("UnsubscribeAllExceptAliveLoop")} -} - -func (_c *RPCClient_UnsubscribeAllExceptAliveLoop_Call) Run(run func()) *RPCClient_UnsubscribeAllExceptAliveLoop_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *RPCClient_UnsubscribeAllExceptAliveLoop_Call) Return() *RPCClient_UnsubscribeAllExceptAliveLoop_Call { - _c.Call.Return() - return _c -} - -func (_c *RPCClient_UnsubscribeAllExceptAliveLoop_Call) RunAndReturn(run func()) *RPCClient_UnsubscribeAllExceptAliveLoop_Call { - _c.Call.Return(run) - return _c -} - -// NewRPCClient creates a new instance of RPCClient. 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 NewRPCClient(t interface { - mock.TestingT - Cleanup(func()) -}) *RPCClient { - mock := &RPCClient{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/chains/evm/client/null_client.go b/core/chains/evm/client/null_client.go index 5b1a4d7e1bb..c59861a7379 100644 --- a/core/chains/evm/client/null_client.go +++ b/core/chains/evm/client/null_client.go @@ -90,9 +90,9 @@ func (nc *NullClient) SubscribeFilterLogs(ctx context.Context, q ethereum.Filter return newNullSubscription(nc.lggr), nil } -func (nc *NullClient) SubscribeNewHead(ctx context.Context, ch chan<- *evmtypes.Head) (ethereum.Subscription, error) { - nc.lggr.Debug("SubscribeNewHead") - return newNullSubscription(nc.lggr), nil +func (nc *NullClient) SubscribeToHeads(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) { + nc.lggr.Debug("SubscribeToHeads") + return nil, newNullSubscription(nc.lggr), nil } // diff --git a/core/chains/evm/client/null_client_test.go b/core/chains/evm/client/null_client_test.go index bc6c166030f..6d61a3e808f 100644 --- a/core/chains/evm/client/null_client_test.go +++ b/core/chains/evm/client/null_client_test.go @@ -15,7 +15,6 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" - evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ) func TestNullClient(t *testing.T) { @@ -62,10 +61,9 @@ func TestNullClient(t *testing.T) { require.Nil(t, h) require.Equal(t, 1, logs.FilterMessage("HeadByNumber").Len()) - chHeads := make(chan *evmtypes.Head) - sub, err := nc.SubscribeNewHead(ctx, chHeads) + _, sub, err := nc.SubscribeToHeads(ctx) require.NoError(t, err) - require.Equal(t, 1, logs.FilterMessage("SubscribeNewHead").Len()) + require.Equal(t, 1, logs.FilterMessage("SubscribeToHeads").Len()) require.Nil(t, sub.Err()) require.Equal(t, 1, logs.FilterMessage("Err").Len()) sub.Unsubscribe() diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index f55c35980df..79d6bc9214f 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -29,6 +29,7 @@ import ( commonclient "github.com/smartcontractkit/chainlink/v2/common/client" commontypes "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" @@ -77,35 +78,6 @@ var ( }, []string{"evmChainID", "nodeName", "rpcHost", "isSendOnly", "success", "rpcCallName"}) ) -// RPCClient includes all the necessary generalized RPC methods along with any additional chain-specific methods. -type RPCClient interface { - commonclient.RPC[ - *big.Int, - evmtypes.Nonce, - common.Address, - common.Hash, - *types.Transaction, - common.Hash, - types.Log, - ethereum.FilterQuery, - *evmtypes.Receipt, - *assets.Wei, - *evmtypes.Head, - rpc.BatchElem, - ] - BlockByHashGeth(ctx context.Context, hash common.Hash) (b *types.Block, err error) - BlockByNumberGeth(ctx context.Context, number *big.Int) (b *types.Block, err error) - HeaderByHash(ctx context.Context, h common.Hash) (head *types.Header, err error) - HeaderByNumber(ctx context.Context, n *big.Int) (head *types.Header, err error) - PendingCodeAt(ctx context.Context, account common.Address) (b []byte, err error) - SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (s ethereum.Subscription, err error) - SuggestGasPrice(ctx context.Context) (p *big.Int, err error) - SuggestGasTipCap(ctx context.Context) (t *big.Int, err error) - TransactionReceiptGeth(ctx context.Context, txHash common.Hash) (r *types.Receipt, err error) - GetInterceptedChainInfo() (latest, highestUserObservations commonclient.ChainInfo) - FeeHistory(ctx context.Context, blockCount uint64, rewardPercentiles []float64) (feeHistory *ethereum.FeeHistory, err error) -} - const rpcSubscriptionMethodNewHeads = "newHeads" type rawclient struct { @@ -114,16 +86,17 @@ type rawclient struct { uri url.URL } -type rpcClient struct { +type RPCClient struct { + cfg config.NodePool rpcLog logger.SugaredLogger name string id int chainID *big.Int tier commonclient.NodeTier - largePayloadRpcTimeout time.Duration - rpcTimeout time.Duration + largePayloadRPCTimeout time.Duration finalizedBlockPollInterval time.Duration newHeadsPollInterval time.Duration + rpcTimeout time.Duration chainType chaintype.ChainType ws *rawclient @@ -134,25 +107,25 @@ type rpcClient struct { // Need to track subscriptions because closing the RPC does not (always?) // close the underlying subscription - subs []ethereum.Subscription - - // Need to track the aliveLoop subscription, so we do not cancel it when checking lease on the MultiNode - aliveLoopSub ethereum.Subscription + subs map[ethereum.Subscription]struct{} // chStopInFlight can be closed to immediately cancel all in-flight requests on - // this rpcClient. Closing and replacing should be serialized through - // stateMu since it can happen on state transitions as well as rpcClient Close. + // this RPCClient. Closing and replacing should be serialized through + // stateMu since it can happen on state transitions as well as RPCClient Close. chStopInFlight chan struct{} chainInfoLock sync.RWMutex - // intercepted values seen by callers of the rpcClient excluding health check calls. Need to ensure MultiNode provides repeatable read guarantee + // intercepted values seen by callers of the RPCClient excluding health check calls. Need to ensure MultiNode provides repeatable read guarantee highestUserObservations commonclient.ChainInfo // most recent chain info observed during current lifecycle (reseted on DisconnectAll) latestChainInfo commonclient.ChainInfo } -// NewRPCCLient returns a new *rpcClient as commonclient.RPC +var _ commonclient.RPCClient[*big.Int, *evmtypes.Head] = (*RPCClient)(nil) +var _ commonclient.SendTxRPCClient[*types.Transaction] = (*RPCClient)(nil) + func NewRPCClient( + cfg config.NodePool, lggr logger.Logger, wsuri *url.URL, httpuri *url.URL, @@ -160,23 +133,22 @@ func NewRPCClient( id int, chainID *big.Int, tier commonclient.NodeTier, - finalizedBlockPollInterval time.Duration, - newHeadsPollInterval time.Duration, - largePayloadRpcTimeout time.Duration, + largePayloadRPCTimeout time.Duration, rpcTimeout time.Duration, chainType chaintype.ChainType, -) RPCClient { - r := &rpcClient{ - largePayloadRpcTimeout: largePayloadRpcTimeout, +) *RPCClient { + r := &RPCClient{ + largePayloadRPCTimeout: largePayloadRPCTimeout, rpcTimeout: rpcTimeout, chainType: chainType, } + r.cfg = cfg r.name = name r.id = id r.chainID = chainID r.tier = tier - r.finalizedBlockPollInterval = finalizedBlockPollInterval - r.newHeadsPollInterval = newHeadsPollInterval + r.finalizedBlockPollInterval = cfg.FinalizedBlockPollInterval() + r.newHeadsPollInterval = cfg.NewHeadsPollInterval() if wsuri != nil { r.ws = &rawclient{uri: *wsuri} } @@ -192,12 +164,39 @@ func NewRPCClient( "evmChainID", chainID, ) r.rpcLog = logger.Sugared(lggr).Named("RPC") + r.subs = map[ethereum.Subscription]struct{}{} return r } +func (r *RPCClient) Ping(ctx context.Context) error { + version, err := r.ClientVersion(ctx) + if err != nil { + return fmt.Errorf("ping failed: %w", err) + } + r.rpcLog.Debugf("ping client version: %s", version) + return err +} + +func (r *RPCClient) UnsubscribeAllExcept(subs ...commontypes.Subscription) { + r.subsSliceMu.Lock() + defer r.subsSliceMu.Unlock() + + keepSubs := map[commontypes.Subscription]struct{}{} + for _, sub := range subs { + keepSubs[sub] = struct{}{} + } + + for sub := range r.subs { + if _, keep := keepSubs[sub]; !keep { + sub.Unsubscribe() + delete(r.subs, sub) + } + } +} + // Not thread-safe, pure dial. -func (r *rpcClient) Dial(callerCtx context.Context) error { +func (r *RPCClient) Dial(callerCtx context.Context) error { ctx, cancel := r.makeQueryCtx(callerCtx, r.rpcTimeout) defer cancel() @@ -234,7 +233,7 @@ func (r *rpcClient) Dial(callerCtx context.Context) error { // Not thread-safe, pure dial. // DialHTTP doesn't actually make any external HTTP calls // It can only return error if the URL is malformed. -func (r *rpcClient) DialHTTP() error { +func (r *RPCClient) DialHTTP() error { promEVMPoolRPCNodeDials.WithLabelValues(r.chainID.String(), r.name).Inc() lggr := r.rpcLog.With("httpuri", r.http.uri.Redacted()) lggr.Debugw("RPC dial: evmclient.Client#dial") @@ -254,27 +253,28 @@ func (r *rpcClient) DialHTTP() error { return nil } -func (r *rpcClient) Close() { +func (r *RPCClient) Close() { defer func() { if r.ws != nil && r.ws.rpc != nil { r.ws.rpc.Close() } }() - - r.stateMu.Lock() - defer r.stateMu.Unlock() r.cancelInflightRequests() + r.UnsubscribeAllExcept() + r.chainInfoLock.Lock() + r.latestChainInfo = commonclient.ChainInfo{} + r.chainInfoLock.Unlock() } // cancelInflightRequests closes and replaces the chStopInFlight -// WARNING: NOT THREAD-SAFE -// This must be called from within the r.stateMu lock -func (r *rpcClient) cancelInflightRequests() { +func (r *RPCClient) cancelInflightRequests() { + r.stateMu.Lock() + defer r.stateMu.Unlock() close(r.chStopInFlight) r.chStopInFlight = make(chan struct{}) } -func (r *rpcClient) String() string { +func (r *RPCClient) String() string { s := fmt.Sprintf("(%s)%s", r.tier.String(), r.name) if r.ws != nil { s = s + fmt.Sprintf(":%s", r.ws.uri.Redacted()) @@ -285,7 +285,7 @@ func (r *rpcClient) String() string { return s } -func (r *rpcClient) logResult( +func (r *RPCClient) logResult( lggr logger.Logger, err error, callDuration time.Duration, @@ -308,7 +308,7 @@ func (r *rpcClient) logResult( promEVMPoolRPCCallTiming. WithLabelValues( r.chainID.String(), // chain id - r.name, // rpcClient name + r.name, // RPCClient name rpcDomain, // rpc domain "false", // is send only strconv.FormatBool(err == nil), // is successful @@ -317,18 +317,18 @@ func (r *rpcClient) logResult( Observe(float64(callDuration)) } -func (r *rpcClient) getRPCDomain() string { +func (r *RPCClient) getRPCDomain() string { if r.http != nil { return r.http.uri.Host } return r.ws.uri.Host } -// registerSub adds the sub to the rpcClient list -func (r *rpcClient) registerSub(sub ethereum.Subscription, stopInFLightCh chan struct{}) error { +// registerSub adds the sub to the RPCClient list +func (r *RPCClient) registerSub(sub ethereum.Subscription, stopInFLightCh chan struct{}) error { r.subsSliceMu.Lock() defer r.subsSliceMu.Unlock() - // ensure that the `sub` belongs to current life cycle of the `rpcClient` and it should not be killed due to + // ensure that the `sub` belongs to current life cycle of the `RPCClient` and it should not be killed due to // previous `DisconnectAll` call. select { case <-stopInFLightCh: @@ -337,69 +337,15 @@ func (r *rpcClient) registerSub(sub ethereum.Subscription, stopInFLightCh chan s default: } // TODO: BCI-3358 - delete sub when caller unsubscribes. - r.subs = append(r.subs, sub) + r.subs[sub] = struct{}{} return nil } -// DisconnectAll disconnects all clients connected to the rpcClient -func (r *rpcClient) DisconnectAll() { - r.stateMu.Lock() - if r.ws != nil && r.ws.rpc != nil { - r.ws.rpc.Close() - } - r.cancelInflightRequests() - r.stateMu.Unlock() - - r.subsSliceMu.Lock() - r.unsubscribeAll() - r.subsSliceMu.Unlock() - - r.chainInfoLock.Lock() - r.latestChainInfo = commonclient.ChainInfo{} - r.chainInfoLock.Unlock() -} - -// unsubscribeAll unsubscribes all subscriptions -// WARNING: NOT THREAD-SAFE -// This must be called from within the r.stateMu lock -func (r *rpcClient) unsubscribeAll() { - for _, sub := range r.subs { - sub.Unsubscribe() - } - r.subs = nil -} -func (r *rpcClient) SetAliveLoopSub(sub commontypes.Subscription) { - r.stateMu.Lock() - defer r.stateMu.Unlock() - - r.aliveLoopSub = sub -} - -// SubscribersCount returns the number of client subscribed to the node -func (r *rpcClient) SubscribersCount() int32 { - r.stateMu.RLock() - defer r.stateMu.RUnlock() - return int32(len(r.subs)) -} - -// UnsubscribeAllExceptAliveLoop disconnects all subscriptions to the node except the alive loop subscription -// while holding the n.stateMu lock -func (r *rpcClient) UnsubscribeAllExceptAliveLoop() { - r.stateMu.Lock() - defer r.stateMu.Unlock() - - for _, s := range r.subs { - if s != r.aliveLoopSub { - s.Unsubscribe() - } - } -} - // RPC wrappers // CallContext implementation -func (r *rpcClient) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { - ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.largePayloadRpcTimeout) +func (r *RPCClient) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { + ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.largePayloadRPCTimeout) defer cancel() lggr := r.newRqLggr().With( "method", method, @@ -421,7 +367,7 @@ func (r *rpcClient) CallContext(ctx context.Context, result interface{}, method return err } -func (r *rpcClient) BatchCallContext(rootCtx context.Context, b []rpc.BatchElem) error { +func (r *RPCClient) BatchCallContext(rootCtx context.Context, b []rpc.BatchElem) error { // Astar's finality tags provide weaker finality guarantees than we require. // Fetch latest finalized block using Astar's custom requests and populate it after batch request completes var astarRawLatestFinalizedBlock json.RawMessage @@ -442,7 +388,7 @@ func (r *rpcClient) BatchCallContext(rootCtx context.Context, b []rpc.BatchElem) } } - ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(rootCtx, r.largePayloadRpcTimeout) + ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(rootCtx, r.largePayloadRPCTimeout) defer cancel() lggr := r.newRqLggr().With("nBatchElems", len(b), "batchElems", b) @@ -499,73 +445,7 @@ func isRequestingFinalizedBlock(el rpc.BatchElem) bool { } } -// TODO: Full transition from SubscribeNewHead to SubscribeToHeads is done in BCI-2875 -func (r *rpcClient) SubscribeNewHead(ctx context.Context, channel chan<- *evmtypes.Head) (_ commontypes.Subscription, err error) { - ctx, cancel, chStopInFlight, ws, _ := r.acquireQueryCtx(ctx, r.rpcTimeout) - defer cancel() - args := []interface{}{"newHeads"} - lggr := r.newRqLggr().With("args", args) - if r.newHeadsPollInterval > 0 { - interval := r.newHeadsPollInterval - timeout := interval - poller, pollerCh := commonclient.NewPoller[*evmtypes.Head](interval, r.latestBlock, timeout, r.rpcLog) - if err = poller.Start(ctx); err != nil { - return nil, err - } - - // NOTE this is a temporary special treatment for SubscribeNewHead before we refactor head tracker to use SubscribeToHeads - // as we need to forward new head from the poller channel to the channel passed from caller. - go func() { - for head := range pollerCh { - select { - case channel <- head: - // forwarding new head to the channel passed from caller - case <-poller.Err(): - // return as poller returns error - return - } - } - }() - - err = r.registerSub(&poller, chStopInFlight) - if err != nil { - return nil, err - } - - lggr.Debugf("Polling new heads over http") - return &poller, nil - } - - if ws == nil { - return nil, errors.New("SubscribeNewHead is not allowed without ws url") - } - - lggr.Debug("RPC call: evmclient.Client#EthSubscribe") - start := time.Now() - defer func() { - duration := time.Since(start) - r.logResult(lggr, err, duration, r.getRPCDomain(), "EthSubscribe") - err = r.wrapWS(err) - }() - subForwarder := newSubForwarder(channel, func(head *evmtypes.Head) *evmtypes.Head { - head.EVMChainID = ubig.New(r.chainID) - r.onNewHead(ctx, chStopInFlight, head) - return head - }, r.wrapRPCClientError) - err = subForwarder.start(ws.rpc.EthSubscribe(ctx, subForwarder.srcCh, args...)) - if err != nil { - return - } - - err = r.registerSub(subForwarder, chStopInFlight) - if err != nil { - return - } - - return subForwarder, nil -} - -func (r *rpcClient) SubscribeToHeads(ctx context.Context) (ch <-chan *evmtypes.Head, sub commontypes.Subscription, err error) { +func (r *RPCClient) SubscribeToHeads(ctx context.Context) (ch <-chan *evmtypes.Head, sub commontypes.Subscription, err error) { ctx, cancel, chStopInFlight, ws, _ := r.acquireQueryCtx(ctx, r.rpcTimeout) defer cancel() args := []interface{}{rpcSubscriptionMethodNewHeads} @@ -621,10 +501,11 @@ func (r *rpcClient) SubscribeToHeads(ctx context.Context) (ch <-chan *evmtypes.H return channel, forwarder, err } -func (r *rpcClient) SubscribeToFinalizedHeads(ctx context.Context) (<-chan *evmtypes.Head, commontypes.Subscription, error) { +func (r *RPCClient) SubscribeToFinalizedHeads(ctx context.Context) (<-chan *evmtypes.Head, commontypes.Subscription, error) { ctx, cancel, chStopInFlight, _, _ := r.acquireQueryCtx(ctx, r.rpcTimeout) defer cancel() - interval := r.finalizedBlockPollInterval + + interval := r.cfg.FinalizedBlockPollInterval() if interval == 0 { return nil, nil, errors.New("FinalizedBlockPollInterval is 0") } @@ -644,7 +525,7 @@ func (r *rpcClient) SubscribeToFinalizedHeads(ctx context.Context) (<-chan *evmt // GethClient wrappers -func (r *rpcClient) TransactionReceipt(ctx context.Context, txHash common.Hash) (receipt *evmtypes.Receipt, err error) { +func (r *RPCClient) TransactionReceipt(ctx context.Context, txHash common.Hash) (receipt *evmtypes.Receipt, err error) { err = r.CallContext(ctx, &receipt, "eth_getTransactionReceipt", txHash, false) if err != nil { return nil, err @@ -656,7 +537,7 @@ func (r *rpcClient) TransactionReceipt(ctx context.Context, txHash common.Hash) return } -func (r *rpcClient) TransactionReceiptGeth(ctx context.Context, txHash common.Hash) (receipt *types.Receipt, err error) { +func (r *RPCClient) TransactionReceiptGeth(ctx context.Context, txHash common.Hash) (receipt *types.Receipt, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("txHash", txHash) @@ -679,8 +560,7 @@ func (r *rpcClient) TransactionReceiptGeth(ctx context.Context, txHash common.Ha return } - -func (r *rpcClient) TransactionByHash(ctx context.Context, txHash common.Hash) (tx *types.Transaction, err error) { +func (r *RPCClient) TransactionByHash(ctx context.Context, txHash common.Hash) (tx *types.Transaction, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("txHash", txHash) @@ -704,7 +584,7 @@ func (r *rpcClient) TransactionByHash(ctx context.Context, txHash common.Hash) ( return } -func (r *rpcClient) HeaderByNumber(ctx context.Context, number *big.Int) (header *types.Header, err error) { +func (r *RPCClient) HeaderByNumber(ctx context.Context, number *big.Int) (header *types.Header, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("number", number) @@ -725,7 +605,7 @@ func (r *rpcClient) HeaderByNumber(ctx context.Context, number *big.Int) (header return } -func (r *rpcClient) HeaderByHash(ctx context.Context, hash common.Hash) (header *types.Header, err error) { +func (r *RPCClient) HeaderByHash(ctx context.Context, hash common.Hash) (header *types.Header, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("hash", hash) @@ -748,7 +628,7 @@ func (r *rpcClient) HeaderByHash(ctx context.Context, hash common.Hash) (header return } -func (r *rpcClient) LatestFinalizedBlock(ctx context.Context) (head *evmtypes.Head, err error) { +func (r *RPCClient) LatestFinalizedBlock(ctx context.Context) (head *evmtypes.Head, err error) { // capture chStopInFlight to ensure we are not updating chainInfo with observations related to previous life cycle ctx, cancel, chStopInFlight, _, _ := r.acquireQueryCtx(ctx, r.rpcTimeout) defer cancel() @@ -774,11 +654,11 @@ func (r *rpcClient) LatestFinalizedBlock(ctx context.Context) (head *evmtypes.He return } -func (r *rpcClient) latestBlock(ctx context.Context) (head *evmtypes.Head, err error) { +func (r *RPCClient) latestBlock(ctx context.Context) (head *evmtypes.Head, err error) { return r.BlockByNumber(ctx, nil) } -func (r *rpcClient) astarLatestFinalizedBlock(ctx context.Context, result interface{}) (err error) { +func (r *RPCClient) astarLatestFinalizedBlock(ctx context.Context, result interface{}) (err error) { var hashResult string err = r.CallContext(ctx, &hashResult, "chain_getFinalizedHead") if err != nil { @@ -805,7 +685,7 @@ func (r *rpcClient) astarLatestFinalizedBlock(ctx context.Context, result interf return nil } -func (r *rpcClient) BlockByNumber(ctx context.Context, number *big.Int) (head *evmtypes.Head, err error) { +func (r *RPCClient) BlockByNumber(ctx context.Context, number *big.Int) (head *evmtypes.Head, err error) { ctx, cancel, chStopInFlight, _, _ := r.acquireQueryCtx(ctx, r.rpcTimeout) defer cancel() hexNumber := ToBlockNumArg(number) @@ -828,7 +708,7 @@ func (r *rpcClient) BlockByNumber(ctx context.Context, number *big.Int) (head *e return } -func (r *rpcClient) ethGetBlockByNumber(ctx context.Context, number string, result interface{}) (err error) { +func (r *RPCClient) ethGetBlockByNumber(ctx context.Context, number string, result interface{}) (err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() const method = "eth_getBlockByNumber" @@ -851,7 +731,7 @@ func (r *rpcClient) ethGetBlockByNumber(ctx context.Context, number string, resu return err } -func (r *rpcClient) BlockByHash(ctx context.Context, hash common.Hash) (head *evmtypes.Head, err error) { +func (r *RPCClient) BlockByHash(ctx context.Context, hash common.Hash) (head *evmtypes.Head, err error) { err = r.CallContext(ctx, &head, "eth_getBlockByHash", hash.Hex(), false) if err != nil { return nil, err @@ -864,7 +744,7 @@ func (r *rpcClient) BlockByHash(ctx context.Context, hash common.Hash) (head *ev return } -func (r *rpcClient) BlockByHashGeth(ctx context.Context, hash common.Hash) (block *types.Block, err error) { +func (r *RPCClient) BlockByHashGeth(ctx context.Context, hash common.Hash) (block *types.Block, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("hash", hash) @@ -887,7 +767,7 @@ func (r *rpcClient) BlockByHashGeth(ctx context.Context, hash common.Hash) (bloc return } -func (r *rpcClient) BlockByNumberGeth(ctx context.Context, number *big.Int) (block *types.Block, err error) { +func (r *RPCClient) BlockByNumberGeth(ctx context.Context, number *big.Int) (block *types.Block, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("number", number) @@ -910,8 +790,8 @@ func (r *rpcClient) BlockByNumberGeth(ctx context.Context, number *big.Int) (blo return } -func (r *rpcClient) SendTransaction(ctx context.Context, tx *types.Transaction) error { - ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.largePayloadRpcTimeout) +func (r *RPCClient) SendTransaction(ctx context.Context, tx *types.Transaction) error { + ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.largePayloadRPCTimeout) defer cancel() lggr := r.newRqLggr().With("tx", tx) @@ -930,12 +810,12 @@ func (r *rpcClient) SendTransaction(ctx context.Context, tx *types.Transaction) return err } -func (r *rpcClient) SimulateTransaction(ctx context.Context, tx *types.Transaction) error { +func (r *RPCClient) SimulateTransaction(ctx context.Context, tx *types.Transaction) error { // Not Implemented return pkgerrors.New("SimulateTransaction not implemented") } -func (r *rpcClient) SendEmptyTransaction( +func (r *RPCClient) SendEmptyTransaction( ctx context.Context, newTxAttempt func(nonce evmtypes.Nonce, feeLimit uint32, fee *assets.Wei, fromAddress common.Address) (attempt any, err error), nonce evmtypes.Nonce, @@ -948,7 +828,7 @@ func (r *rpcClient) SendEmptyTransaction( } // PendingSequenceAt returns one higher than the highest nonce from both mempool and mined transactions -func (r *rpcClient) PendingSequenceAt(ctx context.Context, account common.Address) (nonce evmtypes.Nonce, err error) { +func (r *RPCClient) PendingSequenceAt(ctx context.Context, account common.Address) (nonce evmtypes.Nonce, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("account", account) @@ -977,7 +857,7 @@ func (r *rpcClient) PendingSequenceAt(ctx context.Context, account common.Addres // SequenceAt is a bit of a misnomer. You might expect it to return the highest // mined nonce at the given block number, but it actually returns the total // transaction count which is the highest mined nonce + 1 -func (r *rpcClient) SequenceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (nonce evmtypes.Nonce, err error) { +func (r *RPCClient) SequenceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (nonce evmtypes.Nonce, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("account", account, "blockNumber", blockNumber) @@ -1003,7 +883,7 @@ func (r *rpcClient) SequenceAt(ctx context.Context, account common.Address, bloc return } -func (r *rpcClient) PendingCodeAt(ctx context.Context, account common.Address) (code []byte, err error) { +func (r *RPCClient) PendingCodeAt(ctx context.Context, account common.Address) (code []byte, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("account", account) @@ -1026,7 +906,7 @@ func (r *rpcClient) PendingCodeAt(ctx context.Context, account common.Address) ( return } -func (r *rpcClient) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) (code []byte, err error) { +func (r *RPCClient) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) (code []byte, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("account", account, "blockNumber", blockNumber) @@ -1049,8 +929,8 @@ func (r *rpcClient) CodeAt(ctx context.Context, account common.Address, blockNum return } -func (r *rpcClient) EstimateGas(ctx context.Context, c interface{}) (gas uint64, err error) { - ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.largePayloadRpcTimeout) +func (r *RPCClient) EstimateGas(ctx context.Context, c interface{}) (gas uint64, err error) { + ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.largePayloadRPCTimeout) defer cancel() call := c.(ethereum.CallMsg) lggr := r.newRqLggr().With("call", call) @@ -1073,7 +953,7 @@ func (r *rpcClient) EstimateGas(ctx context.Context, c interface{}) (gas uint64, return } -func (r *rpcClient) SuggestGasPrice(ctx context.Context) (price *big.Int, err error) { +func (r *RPCClient) SuggestGasPrice(ctx context.Context) (price *big.Int, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr() @@ -1096,8 +976,8 @@ func (r *rpcClient) SuggestGasPrice(ctx context.Context) (price *big.Int, err er return } -func (r *rpcClient) CallContract(ctx context.Context, msg interface{}, blockNumber *big.Int) (val []byte, err error) { - ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.largePayloadRpcTimeout) +func (r *RPCClient) CallContract(ctx context.Context, msg interface{}, blockNumber *big.Int) (val []byte, err error) { + ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.largePayloadRPCTimeout) defer cancel() lggr := r.newRqLggr().With("callMsg", msg, "blockNumber", blockNumber) message := msg.(ethereum.CallMsg) @@ -1124,8 +1004,8 @@ func (r *rpcClient) CallContract(ctx context.Context, msg interface{}, blockNumb return } -func (r *rpcClient) PendingCallContract(ctx context.Context, msg interface{}) (val []byte, err error) { - ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.largePayloadRpcTimeout) +func (r *RPCClient) PendingCallContract(ctx context.Context, msg interface{}) (val []byte, err error) { + ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.largePayloadRPCTimeout) defer cancel() lggr := r.newRqLggr().With("callMsg", msg) message := msg.(ethereum.CallMsg) @@ -1152,13 +1032,13 @@ func (r *rpcClient) PendingCallContract(ctx context.Context, msg interface{}) (v return } -func (r *rpcClient) LatestBlockHeight(ctx context.Context) (*big.Int, error) { +func (r *RPCClient) LatestBlockHeight(ctx context.Context) (*big.Int, error) { var height big.Int h, err := r.BlockNumber(ctx) return height.SetUint64(h), err } -func (r *rpcClient) BlockNumber(ctx context.Context) (height uint64, err error) { +func (r *RPCClient) BlockNumber(ctx context.Context) (height uint64, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr() @@ -1181,7 +1061,7 @@ func (r *rpcClient) BlockNumber(ctx context.Context) (height uint64, err error) return } -func (r *rpcClient) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (balance *big.Int, err error) { +func (r *RPCClient) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (balance *big.Int, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("account", account.Hex(), "blockNumber", blockNumber) @@ -1204,7 +1084,7 @@ func (r *rpcClient) BalanceAt(ctx context.Context, account common.Address, block return } -func (r *rpcClient) FeeHistory(ctx context.Context, blockCount uint64, rewardPercentiles []float64) (feeHistory *ethereum.FeeHistory, err error) { +func (r *RPCClient) FeeHistory(ctx context.Context, blockCount uint64, rewardPercentiles []float64) (feeHistory *ethereum.FeeHistory, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("blockCount", blockCount, "rewardPercentiles", rewardPercentiles) @@ -1237,7 +1117,7 @@ type CallArgs struct { } // TokenBalance returns the balance of the given address for the token contract address. -func (r *rpcClient) TokenBalance(ctx context.Context, address common.Address, contractAddress common.Address) (*big.Int, error) { +func (r *RPCClient) TokenBalance(ctx context.Context, address common.Address, contractAddress common.Address) (*big.Int, error) { result := "" numLinkBigInt := new(big.Int) functionSelector := evmtypes.HexToFunctionSelector(BALANCE_OF_ADDRESS_FUNCTION_SELECTOR) // balanceOf(address) @@ -1257,7 +1137,7 @@ func (r *rpcClient) TokenBalance(ctx context.Context, address common.Address, co } // LINKBalance returns the balance of LINK at the given address -func (r *rpcClient) LINKBalance(ctx context.Context, address common.Address, linkAddress common.Address) (*commonassets.Link, error) { +func (r *RPCClient) LINKBalance(ctx context.Context, address common.Address, linkAddress common.Address) (*commonassets.Link, error) { balance, err := r.TokenBalance(ctx, address, linkAddress) if err != nil { return commonassets.NewLinkFromJuels(0), err @@ -1265,11 +1145,11 @@ func (r *rpcClient) LINKBalance(ctx context.Context, address common.Address, lin return (*commonassets.Link)(balance), nil } -func (r *rpcClient) FilterEvents(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { +func (r *RPCClient) FilterEvents(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { return r.FilterLogs(ctx, q) } -func (r *rpcClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) (l []types.Log, err error) { +func (r *RPCClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) (l []types.Log, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("q", q) @@ -1292,12 +1172,12 @@ func (r *rpcClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) (l [ return } -func (r *rpcClient) ClientVersion(ctx context.Context) (version string, err error) { +func (r *RPCClient) ClientVersion(ctx context.Context) (version string, err error) { err = r.CallContext(ctx, &version, "web3_clientVersion") return } -func (r *rpcClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (_ ethereum.Subscription, err error) { +func (r *RPCClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (_ ethereum.Subscription, err error) { ctx, cancel, chStopInFlight, ws, _ := r.acquireQueryCtx(ctx, r.rpcTimeout) defer cancel() if ws == nil { @@ -1326,7 +1206,7 @@ func (r *rpcClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQu return sub, nil } -func (r *rpcClient) SuggestGasTipCap(ctx context.Context) (tipCap *big.Int, err error) { +func (r *RPCClient) SuggestGasTipCap(ctx context.Context) (tipCap *big.Int, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr() @@ -1351,7 +1231,7 @@ func (r *rpcClient) SuggestGasTipCap(ctx context.Context) (tipCap *big.Int, err // Returns the ChainID according to the geth client. This is useful for functions like verify() // the common node. -func (r *rpcClient) ChainID(ctx context.Context) (chainID *big.Int, err error) { +func (r *RPCClient) ChainID(ctx context.Context) (chainID *big.Int, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() @@ -1367,16 +1247,16 @@ func (r *rpcClient) ChainID(ctx context.Context) (chainID *big.Int, err error) { } // newRqLggr generates a new logger with a unique request ID -func (r *rpcClient) newRqLggr() logger.SugaredLogger { +func (r *RPCClient) newRqLggr() logger.SugaredLogger { return r.rpcLog.With("requestID", uuid.New()) } -func (r *rpcClient) wrapRPCClientError(err error) error { +func (r *RPCClient) wrapRPCClientError(err error) error { // simple add msg to the error without adding new stack trace return pkgerrors.WithMessage(err, r.rpcClientErrorPrefix()) } -func (r *rpcClient) rpcClientErrorPrefix() string { +func (r *RPCClient) rpcClientErrorPrefix() string { return fmt.Sprintf("RPCClient returned error (%s)", r.name) } @@ -1390,12 +1270,12 @@ func wrapCallError(err error, tp string) error { return pkgerrors.Wrapf(err, "%s call failed", tp) } -func (r *rpcClient) wrapWS(err error) error { +func (r *RPCClient) wrapWS(err error) error { err = wrapCallError(err, fmt.Sprintf("%s websocket (%s)", r.tier.String(), r.ws.uri.Redacted())) return r.wrapRPCClientError(err) } -func (r *rpcClient) wrapHTTP(err error) error { +func (r *RPCClient) wrapHTTP(err error) error { err = wrapCallError(err, fmt.Sprintf("%s http (%s)", r.tier.String(), r.http.uri.Redacted())) err = r.wrapRPCClientError(err) if err != nil { @@ -1407,12 +1287,12 @@ func (r *rpcClient) wrapHTTP(err error) error { } // makeLiveQueryCtxAndSafeGetClients wraps makeQueryCtx -func (r *rpcClient) makeLiveQueryCtxAndSafeGetClients(parentCtx context.Context, timeout time.Duration) (ctx context.Context, cancel context.CancelFunc, ws *rawclient, http *rawclient) { +func (r *RPCClient) makeLiveQueryCtxAndSafeGetClients(parentCtx context.Context, timeout time.Duration) (ctx context.Context, cancel context.CancelFunc, ws *rawclient, http *rawclient) { ctx, cancel, _, ws, http = r.acquireQueryCtx(parentCtx, timeout) return } -func (r *rpcClient) acquireQueryCtx(parentCtx context.Context, timeout time.Duration) (ctx context.Context, cancel context.CancelFunc, +func (r *RPCClient) acquireQueryCtx(parentCtx context.Context, timeout time.Duration) (ctx context.Context, cancel context.CancelFunc, chStopInFlight chan struct{}, ws *rawclient, http *rawclient) { // Need to wrap in mutex because state transition can cancel and replace the // context @@ -1446,11 +1326,11 @@ func makeQueryCtx(ctx context.Context, ch services.StopChan, timeout time.Durati return ctx, cancel } -func (r *rpcClient) makeQueryCtx(ctx context.Context, timeout time.Duration) (context.Context, context.CancelFunc) { +func (r *RPCClient) makeQueryCtx(ctx context.Context, timeout time.Duration) (context.Context, context.CancelFunc) { return makeQueryCtx(ctx, r.getChStopInflight(), timeout) } -func (r *rpcClient) IsSyncing(ctx context.Context) (bool, error) { +func (r *RPCClient) IsSyncing(ctx context.Context) (bool, error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr() @@ -1477,21 +1357,17 @@ func (r *rpcClient) IsSyncing(ctx context.Context) (bool, error) { // getChStopInflight provides a convenience helper that mutex wraps a // read to the chStopInFlight -func (r *rpcClient) getChStopInflight() chan struct{} { +func (r *RPCClient) getChStopInflight() chan struct{} { r.stateMu.RLock() defer r.stateMu.RUnlock() return r.chStopInFlight } -func (r *rpcClient) Name() string { - return r.name -} - -func Name(r *rpcClient) string { +func (r *RPCClient) Name() string { return r.name } -func (r *rpcClient) onNewHead(ctx context.Context, requestCh <-chan struct{}, head *evmtypes.Head) { +func (r *RPCClient) onNewHead(ctx context.Context, requestCh <-chan struct{}, head *evmtypes.Head) { if head == nil { return } @@ -1503,7 +1379,7 @@ func (r *rpcClient) onNewHead(ctx context.Context, requestCh <-chan struct{}, he r.highestUserObservations.TotalDifficulty = commonclient.MaxTotalDifficulty(r.highestUserObservations.TotalDifficulty, head.TotalDifficulty) } select { - case <-requestCh: // no need to update latestChainInfo, as rpcClient already started new life cycle + case <-requestCh: // no need to update latestChainInfo, as RPCClient already started new life cycle return default: r.latestChainInfo.BlockNumber = head.Number @@ -1511,7 +1387,7 @@ func (r *rpcClient) onNewHead(ctx context.Context, requestCh <-chan struct{}, he } } -func (r *rpcClient) onNewFinalizedHead(ctx context.Context, requestCh <-chan struct{}, head *evmtypes.Head) { +func (r *RPCClient) onNewFinalizedHead(ctx context.Context, requestCh <-chan struct{}, head *evmtypes.Head) { if head == nil { return } @@ -1521,16 +1397,16 @@ func (r *rpcClient) onNewFinalizedHead(ctx context.Context, requestCh <-chan str r.highestUserObservations.FinalizedBlockNumber = max(r.highestUserObservations.FinalizedBlockNumber, head.Number) } select { - case <-requestCh: // no need to update latestChainInfo, as rpcClient already started new life cycle + case <-requestCh: // no need to update latestChainInfo, as RPCClient already started new life cycle return default: r.latestChainInfo.FinalizedBlockNumber = head.Number } } -func (r *rpcClient) GetInterceptedChainInfo() (latest, highestUserObservations commonclient.ChainInfo) { - r.chainInfoLock.RLock() - defer r.chainInfoLock.RUnlock() +func (r *RPCClient) GetInterceptedChainInfo() (latest, highestUserObservations commonclient.ChainInfo) { + r.chainInfoLock.Lock() + defer r.chainInfoLock.Unlock() return r.latestChainInfo, r.highestUserObservations } diff --git a/core/chains/evm/client/rpc_client_test.go b/core/chains/evm/client/rpc_client_test.go index 662c757ffb3..edbb10cc36f 100644 --- a/core/chains/evm/client/rpc_client_test.go +++ b/core/chains/evm/client/rpc_client_test.go @@ -19,13 +19,10 @@ import ( "github.com/tidwall/gjson" "go.uber.org/zap" - commontypes "github.com/smartcontractkit/chainlink/v2/common/types" - - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - "github.com/smartcontractkit/chainlink-common/pkg/logger" - + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" + commontypes "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/testutils" @@ -40,7 +37,7 @@ func makeNewHeadWSMessage(head *evmtypes.Head) string { return fmt.Sprintf(`{"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x00","result":%s}}`, string(asJSON)) } -func TestRPCClient_SubscribeNewHead(t *testing.T) { +func TestRPCClient_SubscribeToHeads(t *testing.T) { t.Parallel() ctx, cancel := context.WithTimeout(tests.Context(t), tests.WaitTimeout(t)) defer cancel() @@ -48,24 +45,53 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { chainId := big.NewInt(123456) lggr := logger.Test(t) + nodePoolCfgHeadPolling := client.TestNodePoolConfig{ + NodeNewHeadsPollInterval: 1 * time.Second, + NodeFinalizedBlockPollInterval: 1 * time.Second, + } + + nodePoolCfgNoPolling := client.TestNodePoolConfig{ + NodeFinalizedBlockPollInterval: 1 * time.Second, + } + + var rpcHeads []*evmtypes.Head + previousHead := &evmtypes.Head{Number: 0} + SetNextRPCHead := func(head *evmtypes.Head) { + rpcHeads = append(rpcHeads, head) + } + serverCallBack := func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { if method == "eth_unsubscribe" { resp.Result = "true" return + } else if method == "eth_subscribe" { + assert.Equal(t, "eth_subscribe", method) + if assert.True(t, params.IsArray()) && assert.Equal(t, "newHeads", params.Array()[0].String()) { + resp.Result = `"0x00"` + } + return } - assert.Equal(t, "eth_subscribe", method) - if assert.True(t, params.IsArray()) && assert.Equal(t, "newHeads", params.Array()[0].String()) { - resp.Result = `"0x00"` + assert.Equal(t, "eth_getBlockByNumber", method) + if assert.True(t, params.IsArray()) && assert.Equal(t, "latest", params.Array()[0].String()) { + if len(rpcHeads) == 0 { + SetNextRPCHead(previousHead) + } + head := rpcHeads[0] + previousHead = head + rpcHeads = rpcHeads[1:] + jsonHead, err := json.Marshal(head) + if err != nil { + panic(fmt.Errorf("failed to marshal head: %w", err)) + } + resp.Result = string(jsonHead) } return } - checkClosedRPCClientShouldRemoveExistingSub := func(t tests.TestingT, ctx context.Context, sub commontypes.Subscription, rpcClient client.RPCClient) { + checkClosedRPCClientShouldRemoveExistingSub := func(t tests.TestingT, ctx context.Context, sub commontypes.Subscription, rpcClient *client.RPCClient) { errCh := sub.Err() - // ensure sub exists - require.Equal(t, int32(1), rpcClient.SubscribersCount()) - rpcClient.DisconnectAll() + rpcClient.UnsubscribeAllExcept() // ensure sub is closed select { @@ -75,13 +101,12 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { } require.NoError(t, rpcClient.Dial(ctx)) - require.Equal(t, int32(0), rpcClient.SubscribersCount()) } t.Run("WS and HTTP URL cannot be both empty", func(t *testing.T) { // ws is optional when LogBroadcaster is disabled, however SubscribeFilterLogs will return error if ws is missing observedLggr, _ := logger.TestObserved(t, zap.DebugLevel) - rpcClient := client.NewRPCClient(observedLggr, nil, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpcClient := client.NewRPCClient(nodePoolCfgHeadPolling, observedLggr, nil, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") require.Equal(t, errors.New("cannot dial rpc client when both ws and http info are missing"), rpcClient.Dial(ctx)) }) @@ -89,7 +114,7 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) // set to default values @@ -101,14 +126,14 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { assert.Equal(t, int64(0), highestUserObservations.FinalizedBlockNumber) assert.Nil(t, highestUserObservations.TotalDifficulty) - ch := make(chan *evmtypes.Head) - sub, err := rpc.SubscribeNewHead(tests.Context(t), ch) + SetNextRPCHead(&evmtypes.Head{Number: 256, TotalDifficulty: big.NewInt(1000)}) + SetNextRPCHead(&evmtypes.Head{Number: 128, TotalDifficulty: big.NewInt(500)}) + + ch, sub, err := rpc.SubscribeToHeads(tests.Context(t)) require.NoError(t, err) defer sub.Unsubscribe() - go server.MustWriteBinaryMessageSync(t, makeNewHeadWSMessage(&evmtypes.Head{Number: 256, TotalDifficulty: big.NewInt(1000)})) // received 256 head <-ch - go server.MustWriteBinaryMessageSync(t, makeNewHeadWSMessage(&evmtypes.Head{Number: 128, TotalDifficulty: big.NewInt(500)})) // received 128 head <-ch @@ -125,8 +150,8 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { assertHighestUserObservations(highestUserObservations) - // DisconnectAll resets latest - rpc.DisconnectAll() + // Close resets latest + rpc.Close() latest, highestUserObservations = rpc.GetInterceptedChainInfo() assert.Equal(t, int64(0), latest.BlockNumber) @@ -139,14 +164,15 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) - ch := make(chan *evmtypes.Head) - sub, err := rpc.SubscribeNewHead(commonclient.CtxAddHealthCheckFlag(tests.Context(t)), ch) + + SetNextRPCHead(&evmtypes.Head{Number: 256, TotalDifficulty: big.NewInt(1000)}) + + ch, sub, err := rpc.SubscribeToHeads(commonclient.CtxAddHealthCheckFlag(tests.Context(t))) require.NoError(t, err) defer sub.Unsubscribe() - go server.MustWriteBinaryMessageSync(t, makeNewHeadWSMessage(&evmtypes.Head{Number: 256, TotalDifficulty: big.NewInt(1000)})) // received 256 head <-ch @@ -155,66 +181,46 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { assert.Equal(t, int64(0), latest.FinalizedBlockNumber) assert.Equal(t, big.NewInt(1000), latest.TotalDifficulty) - assert.Equal(t, int64(0), highestUserObservations.BlockNumber) + assert.Equal(t, int64(256), highestUserObservations.BlockNumber) assert.Equal(t, int64(0), highestUserObservations.FinalizedBlockNumber) - assert.Equal(t, (*big.Int)(nil), highestUserObservations.TotalDifficulty) + assert.Equal(t, big.NewInt(1000), highestUserObservations.TotalDifficulty) }) t.Run("SubscribeToHeads with http polling enabled will update new heads", func(t *testing.T) { - type rpcServer struct { - Head *evmtypes.Head - URL *url.URL - } - createRPCServer := func() *rpcServer { - server := &rpcServer{} - server.Head = &evmtypes.Head{Number: 127} - server.URL = testutils.NewWSServer(t, chainId, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { - assert.Equal(t, "eth_getBlockByNumber", method) - if assert.True(t, params.IsArray()) && assert.Equal(t, "latest", params.Array()[0].String()) { - head := server.Head - jsonHead, err := json.Marshal(head) - if err != nil { - panic(fmt.Errorf("failed to marshal head: %w", err)) - } - resp.Result = string(jsonHead) - } - - return - }).WSURL() - return server - } + server := testutils.NewWSServer(t, chainId, serverCallBack) + wsURL := server.WSURL() - server := createRPCServer() - rpc := client.NewRPCClient(lggr, server.URL, nil, "rpc", 1, chainId, commonclient.Primary, 0, tests.TestInterval, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) + latest, highestUserObservations := rpc.GetInterceptedChainInfo() // latest chain info hasn't been initialized assert.Equal(t, int64(0), latest.BlockNumber) assert.Equal(t, int64(0), highestUserObservations.BlockNumber) + SetNextRPCHead(&evmtypes.Head{Number: 127, TotalDifficulty: big.NewInt(1000)}) + headCh, sub, err := rpc.SubscribeToHeads(commonclient.CtxAddHealthCheckFlag(tests.Context(t))) require.NoError(t, err) defer sub.Unsubscribe() head := <-headCh - assert.Equal(t, server.Head.Number, head.BlockNumber()) + assert.Equal(t, int64(127), head.BlockNumber()) // the http polling subscription should update the head block latest, highestUserObservations = rpc.GetInterceptedChainInfo() - assert.Equal(t, server.Head.Number, latest.BlockNumber) - assert.Equal(t, server.Head.Number, highestUserObservations.BlockNumber) + assert.Equal(t, int64(127), latest.BlockNumber) }) t.Run("Concurrent Unsubscribe and onNewHead calls do not lead to a deadlock", func(t *testing.T) { const numberOfAttempts = 1000 // need a large number to increase the odds of reproducing the issue server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) var wg sync.WaitGroup for i := 0; i < numberOfAttempts; i++ { - ch := make(chan *evmtypes.Head) - sub, err := rpc.SubscribeNewHead(tests.Context(t), ch) + _, sub, err := rpc.SubscribeToHeads(tests.Context(t)) require.NoError(t, err) wg.Add(2) go func() { @@ -222,7 +228,7 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { wg.Done() }() go func() { - rpc.UnsubscribeAllExceptAliveLoop() + rpc.UnsubscribeAllExcept(sub) sub.Unsubscribe() wg.Done() }() @@ -232,53 +238,49 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { t.Run("Block's chain ID matched configured", func(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) - ch := make(chan *evmtypes.Head) - sub, err := rpc.SubscribeNewHead(tests.Context(t), ch) + ch, sub, err := rpc.SubscribeToHeads(tests.Context(t)) require.NoError(t, err) defer sub.Unsubscribe() go server.MustWriteBinaryMessageSync(t, makeNewHeadWSMessage(&evmtypes.Head{Number: 256})) head := <-ch require.Equal(t, chainId, head.ChainID()) }) - t.Run("Failed SubscribeNewHead returns and logs proper error", func(t *testing.T) { + t.Run("Failed SubscribeToHeads returns and logs proper error", func(t *testing.T) { server := testutils.NewWSServer(t, chainId, func(reqMethod string, reqParams gjson.Result) (resp testutils.JSONRPCResponse) { return resp }) wsURL := server.WSURL() observedLggr, observed := logger.TestObserved(t, zap.DebugLevel) - rpc := client.NewRPCClient(observedLggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgNoPolling, observedLggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") require.NoError(t, rpc.Dial(ctx)) server.Close() - _, err := rpc.SubscribeNewHead(ctx, make(chan *evmtypes.Head)) + _, _, err := rpc.SubscribeToHeads(ctx) require.ErrorContains(t, err, "RPCClient returned error (rpc)") tests.AssertLogEventually(t, observed, "evmclient.Client#EthSubscribe RPC call failure") }) - t.Run("Closed rpc client should remove existing SubscribeNewHead subscription with WS", func(t *testing.T) { + t.Run("Closed rpc client should remove existing SubscribeToHeads subscription with WS", func(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - - rpc := client.NewRPCClient(lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgNoPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) - ch := make(chan *evmtypes.Head) - sub, err := rpc.SubscribeNewHead(tests.Context(t), ch) + _, sub, err := rpc.SubscribeToHeads(tests.Context(t)) require.NoError(t, err) checkClosedRPCClientShouldRemoveExistingSub(t, ctx, sub, rpc) }) - t.Run("Closed rpc client should remove existing SubscribeNewHead subscription with HTTP polling", func(t *testing.T) { + t.Run("Closed rpc client should remove existing SubscribeToHeads subscription with HTTP polling", func(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(lggr, wsURL, &url.URL{}, "rpc", 1, chainId, commonclient.Primary, 0, 1, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) - ch := make(chan *evmtypes.Head) - sub, err := rpc.SubscribeNewHead(tests.Context(t), ch) + _, sub, err := rpc.SubscribeToHeads(tests.Context(t)) require.NoError(t, err) checkClosedRPCClientShouldRemoveExistingSub(t, ctx, sub, rpc) }) @@ -286,7 +288,7 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgNoPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) @@ -298,7 +300,7 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(lggr, wsURL, &url.URL{}, "rpc", 1, chainId, commonclient.Primary, 0, 1, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) @@ -310,7 +312,7 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(lggr, wsURL, &url.URL{}, "rpc", 1, chainId, commonclient.Primary, 1, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) @@ -321,12 +323,14 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { t.Run("Subscription error is properly wrapper", func(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgNoPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) - sub, err := rpc.SubscribeNewHead(ctx, make(chan *evmtypes.Head)) + SetNextRPCHead(nil) + _, sub, err := rpc.SubscribeToHeads(ctx) require.NoError(t, err) go server.MustWriteBinaryMessageSync(t, "invalid msg") + select { case err = <-sub.Err(): require.ErrorContains(t, err, "RPCClient returned error (rpc): invalid character") @@ -339,6 +343,11 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { func TestRPCClient_SubscribeFilterLogs(t *testing.T) { t.Parallel() + nodePoolCfg := client.TestNodePoolConfig{ + NodeNewHeadsPollInterval: 1 * time.Second, + NodeFinalizedBlockPollInterval: 1 * time.Second, + } + chainId := big.NewInt(123456) lggr := logger.Test(t) ctx, cancel := context.WithTimeout(tests.Context(t), tests.WaitTimeout(t)) @@ -346,7 +355,7 @@ func TestRPCClient_SubscribeFilterLogs(t *testing.T) { t.Run("Failed SubscribeFilterLogs when WSURL is empty", func(t *testing.T) { // ws is optional when LogBroadcaster is disabled, however SubscribeFilterLogs will return error if ws is missing observedLggr, _ := logger.TestObserved(t, zap.DebugLevel) - rpcClient := client.NewRPCClient(observedLggr, nil, &url.URL{}, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpcClient := client.NewRPCClient(nodePoolCfg, observedLggr, nil, &url.URL{}, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") require.Nil(t, rpcClient.Dial(ctx)) _, err := rpcClient.SubscribeFilterLogs(ctx, ethereum.FilterQuery{}, make(chan types.Log)) @@ -358,7 +367,7 @@ func TestRPCClient_SubscribeFilterLogs(t *testing.T) { }) wsURL := server.WSURL() observedLggr, observed := logger.TestObserved(t, zap.DebugLevel) - rpc := client.NewRPCClient(observedLggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfg, observedLggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") require.NoError(t, rpc.Dial(ctx)) server.Close() _, err := rpc.SubscribeFilterLogs(ctx, ethereum.FilterQuery{}, make(chan types.Log)) @@ -375,7 +384,7 @@ func TestRPCClient_SubscribeFilterLogs(t *testing.T) { return resp }) wsURL := server.WSURL() - rpc := client.NewRPCClient(lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfg, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) sub, err := rpc.SubscribeFilterLogs(ctx, ethereum.FilterQuery{}, make(chan types.Log)) @@ -397,6 +406,11 @@ func TestRPCClient_LatestFinalizedBlock(t *testing.T) { ctx, cancel := context.WithTimeout(tests.Context(t), tests.WaitTimeout(t)) defer cancel() + nodePoolCfg := client.TestNodePoolConfig{ + NodeNewHeadsPollInterval: 1 * time.Second, + NodeFinalizedBlockPollInterval: 1 * time.Second, + } + chainId := big.NewInt(123456) lggr := logger.Test(t) @@ -424,7 +438,7 @@ func TestRPCClient_LatestFinalizedBlock(t *testing.T) { } server := createRPCServer() - rpc := client.NewRPCClient(lggr, server.URL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfg, lggr, server.URL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") require.NoError(t, rpc.Dial(ctx)) defer rpc.Close() server.Head = &evmtypes.Head{Number: 128} @@ -463,8 +477,8 @@ func TestRPCClient_LatestFinalizedBlock(t *testing.T) { assert.Equal(t, int64(0), latest.BlockNumber) assert.Equal(t, int64(256), latest.FinalizedBlockNumber) - // DisconnectAll resets latest ChainInfo - rpc.DisconnectAll() + // Close resets latest ChainInfo + rpc.Close() latest, highestUserObservations = rpc.GetInterceptedChainInfo() assert.Equal(t, int64(0), highestUserObservations.BlockNumber) assert.Equal(t, int64(128), highestUserObservations.FinalizedBlockNumber) @@ -476,40 +490,45 @@ func TestRPCClient_LatestFinalizedBlock(t *testing.T) { func TestRpcClientLargePayloadTimeout(t *testing.T) { t.Parallel() + nodePoolCfg := client.TestNodePoolConfig{ + NodeNewHeadsPollInterval: 1 * time.Second, + NodeFinalizedBlockPollInterval: 1 * time.Second, + } + testCases := []struct { Name string - Fn func(ctx context.Context, rpc client.RPCClient) error + Fn func(ctx context.Context, rpc *client.RPCClient) error }{ { Name: "SendTransaction", - Fn: func(ctx context.Context, rpc client.RPCClient) error { + Fn: func(ctx context.Context, rpc *client.RPCClient) error { return rpc.SendTransaction(ctx, types.NewTx(&types.LegacyTx{})) }, }, { Name: "EstimateGas", - Fn: func(ctx context.Context, rpc client.RPCClient) error { + Fn: func(ctx context.Context, rpc *client.RPCClient) error { _, err := rpc.EstimateGas(ctx, ethereum.CallMsg{}) return err }, }, { Name: "CallContract", - Fn: func(ctx context.Context, rpc client.RPCClient) error { + Fn: func(ctx context.Context, rpc *client.RPCClient) error { _, err := rpc.CallContract(ctx, ethereum.CallMsg{}, nil) return err }, }, { Name: "CallContext", - Fn: func(ctx context.Context, rpc client.RPCClient) error { + Fn: func(ctx context.Context, rpc *client.RPCClient) error { err := rpc.CallContext(ctx, nil, "rpc_call", nil) return err }, }, { Name: "BatchCallContext", - Fn: func(ctx context.Context, rpc client.RPCClient) error { + Fn: func(ctx context.Context, rpc *client.RPCClient) error { err := rpc.BatchCallContext(ctx, nil) return err }, @@ -534,7 +553,7 @@ func TestRpcClientLargePayloadTimeout(t *testing.T) { // use something unreasonably large for RPC timeout to ensure that we use largePayloadRPCTimeout const rpcTimeout = time.Hour const largePayloadRPCTimeout = tests.TestInterval - rpc := client.NewRPCClient(logger.Test(t), rpcURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, largePayloadRPCTimeout, rpcTimeout, "") + rpc := client.NewRPCClient(nodePoolCfg, logger.Test(t), rpcURL, nil, "rpc", 1, chainId, commonclient.Primary, largePayloadRPCTimeout, rpcTimeout, "") require.NoError(t, rpc.Dial(ctx)) defer rpc.Close() err := testCase.Fn(ctx, rpc) @@ -546,6 +565,11 @@ func TestRpcClientLargePayloadTimeout(t *testing.T) { func TestAstarCustomFinality(t *testing.T) { t.Parallel() + nodePoolCfg := client.TestNodePoolConfig{ + NodeNewHeadsPollInterval: 1 * time.Second, + NodeFinalizedBlockPollInterval: 1 * time.Second, + } + chainId := big.NewInt(123456) // create new server that returns 4 block for Astar custom finality and 8 block for finality tag. wsURL := testutils.NewWSServer(t, chainId, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { @@ -574,7 +598,7 @@ func TestAstarCustomFinality(t *testing.T) { const expectedFinalizedBlockNumber = int64(4) const expectedFinalizedBlockHash = "0x7441e97acf83f555e0deefef86db636bc8a37eb84747603412884e4df4d22804" - rpcClient := client.NewRPCClient(logger.Test(t), wsURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, chaintype.ChainAstar) + rpcClient := client.NewRPCClient(nodePoolCfg, logger.Test(t), wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, chaintype.ChainAstar) defer rpcClient.Close() err := rpcClient.Dial(tests.Context(t)) require.NoError(t, err) diff --git a/core/chains/evm/client/simulated_backend_client.go b/core/chains/evm/client/simulated_backend_client.go index 11828e58710..3753a6704a5 100644 --- a/core/chains/evm/client/simulated_backend_client.go +++ b/core/chains/evm/client/simulated_backend_client.go @@ -297,20 +297,20 @@ func (h *headSubscription) Unsubscribe() { // Err returns err channel func (h *headSubscription) Err() <-chan error { return h.subscription.Err() } -// SubscribeNewHead registers a subscription for push notifications of new blocks. +// SubscribeToHeads registers a subscription for push notifications of new blocks. // Note the sim's API only accepts types.Head so we have this goroutine // to convert those into evmtypes.Head. -func (c *SimulatedBackendClient) SubscribeNewHead( +func (c *SimulatedBackendClient) SubscribeToHeads( ctx context.Context, - channel chan<- *evmtypes.Head, -) (ethereum.Subscription, error) { +) (<-chan *evmtypes.Head, ethereum.Subscription, error) { subscription := &headSubscription{unSub: make(chan chan struct{})} ch := make(chan *types.Header) + channel := make(chan *evmtypes.Head) var err error subscription.subscription, err = c.b.SubscribeNewHead(ctx, ch) if err != nil { - return nil, fmt.Errorf("%w: could not subscribe to new heads on "+ + return nil, nil, fmt.Errorf("%w: could not subscribe to new heads on "+ "simulated backend", err) } go func() { @@ -346,7 +346,7 @@ func (c *SimulatedBackendClient) SubscribeNewHead( } } }() - return subscription, err + return channel, subscription, err } // HeaderByNumber returns the geth header type. diff --git a/core/chains/evm/config/toml/config.go b/core/chains/evm/config/toml/config.go index 27a279f49fc..dff73cacb47 100644 --- a/core/chains/evm/config/toml/config.go +++ b/core/chains/evm/config/toml/config.go @@ -338,6 +338,7 @@ func (c *EVMConfig) ValidateConfig() (err error) { } err = multierr.Append(err, c.Chain.ValidateConfig()) + err = multierr.Append(err, c.NodePool.ValidateConfig(c.Chain.FinalityTagEnabled)) return } @@ -968,6 +969,20 @@ func (p *NodePool) setFrom(f *NodePool) { p.Errors.setFrom(&f.Errors) } +func (p *NodePool) ValidateConfig(finalityTagEnabled *bool) (err error) { + if finalityTagEnabled != nil && *finalityTagEnabled { + if p.FinalizedBlockPollInterval == nil { + err = multierr.Append(err, commonconfig.ErrMissing{Name: "FinalizedBlockPollInterval", Msg: "required when FinalityTagEnabled is true"}) + return + } + if p.FinalizedBlockPollInterval.Duration() <= 0 { + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "FinalizedBlockPollInterval", Value: p.FinalizedBlockPollInterval, + Msg: "must be greater than 0"}) + } + } + return +} + type OCR struct { ContractConfirmations *uint16 ContractTransmitterTransmitTimeout *commonconfig.Duration diff --git a/core/chains/evm/headtracker/head_broadcaster_test.go b/core/chains/evm/headtracker/head_broadcaster_test.go index 7ac61ab34b0..3cd02b3bf05 100644 --- a/core/chains/evm/headtracker/head_broadcaster_test.go +++ b/core/chains/evm/headtracker/head_broadcaster_test.go @@ -55,11 +55,12 @@ func TestHeadBroadcaster_Subscribe(t *testing.T) { ethClient := testutils.NewEthClientMockWithDefaultChain(t) chchHeaders := make(chan chan<- *evmtypes.Head, 1) - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + chHead := make(chan *evmtypes.Head) + ethClient.On("SubscribeToHeads", mock.Anything). Run(func(args mock.Arguments) { - chchHeaders <- args.Get(1).(chan<- *evmtypes.Head) + chchHeaders <- chHead }). - Return(sub, nil) + Return((<-chan *evmtypes.Head)(chHead), sub, nil) ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(testutils.Head(1), nil) sub.On("Unsubscribe").Return() diff --git a/core/chains/evm/headtracker/head_listener_test.go b/core/chains/evm/headtracker/head_listener_test.go index 2e459af2a2b..25902fa6b1a 100644 --- a/core/chains/evm/headtracker/head_listener_test.go +++ b/core/chains/evm/headtracker/head_listener_test.go @@ -27,7 +27,7 @@ func Test_HeadListener_HappyPath(t *testing.T) { t.Parallel() // Logic: // - spawn a listener instance - // - mock SubscribeNewHead/Err/Unsubscribe to track these calls + // - mock SubscribeToHeads/Err/Unsubscribe to track these calls // - send 3 heads // - ask listener to stop // Asserts: @@ -43,13 +43,12 @@ func Test_HeadListener_HappyPath(t *testing.T) { var headCount atomic.Int32 unsubscribeAwaiter := testutils.NewAwaiter() + chHeads := make(chan *evmtypes.Head) subscribeAwaiter := testutils.NewAwaiter() - var chHeads chan<- *evmtypes.Head var chErr = make(chan error) var chSubErr <-chan error = chErr sub := commonmocks.NewSubscription(t) - ethClient.On("SubscribeNewHead", mock.Anything, mock.AnythingOfType("chan<- *types.Head")).Return(sub, nil).Once().Run(func(args mock.Arguments) { - chHeads = args.Get(1).(chan<- *evmtypes.Head) + ethClient.On("SubscribeToHeads", mock.Anything).Return((<-chan *evmtypes.Head)(chHeads), sub, nil).Once().Run(func(args mock.Arguments) { subscribeAwaiter.ItHappened() }) sub.On("Err").Return(chSubErr) @@ -97,13 +96,12 @@ func Test_HeadListener_NotReceivingHeads(t *testing.T) { firstHeadAwaiter := testutils.NewAwaiter() + chHeads := make(chan *evmtypes.Head) subscribeAwaiter := testutils.NewAwaiter() - var chHeads chan<- *evmtypes.Head var chErr = make(chan error) var chSubErr <-chan error = chErr sub := commonmocks.NewSubscription(t) - ethClient.On("SubscribeNewHead", mock.Anything, mock.AnythingOfType("chan<- *types.Head")).Return(sub, nil).Once().Run(func(args mock.Arguments) { - chHeads = args.Get(1).(chan<- *evmtypes.Head) + ethClient.On("SubscribeToHeads", mock.Anything).Return((<-chan *evmtypes.Head)(chHeads), sub, nil).Once().Run(func(args mock.Arguments) { subscribeAwaiter.ItHappened() }) sub.On("Err").Return(chSubErr) @@ -161,11 +159,10 @@ func Test_HeadListener_SubscriptionErr(t *testing.T) { // initially and once again after exactly one head has been received sub.On("Err").Return(chSubErr).Twice() + headsCh := make(chan *evmtypes.Head) subscribeAwaiter := testutils.NewAwaiter() - var headsCh chan<- *evmtypes.Head // Initial subscribe - ethClient.On("SubscribeNewHead", mock.Anything, mock.AnythingOfType("chan<- *types.Head")).Return(sub, nil).Once().Run(func(args mock.Arguments) { - headsCh = args.Get(1).(chan<- *evmtypes.Head) + ethClient.On("SubscribeToHeads", mock.Anything).Return((<-chan *evmtypes.Head)(headsCh), sub, nil).Once().Run(func(args mock.Arguments) { subscribeAwaiter.ItHappened() }) func() { @@ -199,9 +196,8 @@ func Test_HeadListener_SubscriptionErr(t *testing.T) { sub2.On("Err").Return(chSubErr2) subscribeAwaiter2 := testutils.NewAwaiter() - var headsCh2 chan<- *evmtypes.Head - ethClient.On("SubscribeNewHead", mock.Anything, mock.AnythingOfType("chan<- *types.Head")).Return(sub2, nil).Once().Run(func(args mock.Arguments) { - headsCh2 = args.Get(1).(chan<- *evmtypes.Head) + headsCh2 := make(chan *evmtypes.Head) + ethClient.On("SubscribeToHeads", mock.Anything).Return((<-chan *evmtypes.Head)(headsCh2), sub2, nil).Once().Run(func(args mock.Arguments) { subscribeAwaiter2.ItHappened() }) diff --git a/core/chains/evm/headtracker/head_tracker_test.go b/core/chains/evm/headtracker/head_tracker_test.go index 5534a6aa10c..e357ff3a98f 100644 --- a/core/chains/evm/headtracker/head_tracker_test.go +++ b/core/chains/evm/headtracker/head_tracker_test.go @@ -63,9 +63,9 @@ func TestHeadTracker_New(t *testing.T) { mockEth := &testutils.MockEth{ EthClient: ethClient, } - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + ethClient.On("SubscribeToHeads", mock.Anything, mock.Anything). Maybe(). - Return(mockEth.NewSub(t), nil) + Return(nil, mockEth.NewSub(t), nil) orm := headtracker.NewORM(*testutils.FixtureChainID, db) assert.Nil(t, orm.IdempotentInsertHead(tests.Context(t), testutils.Head(1))) @@ -145,14 +145,13 @@ func TestHeadTracker_Get(t *testing.T) { mockEth := &testutils.MockEth{ EthClient: ethClient, } - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + ethClient.On("SubscribeToHeads", mock.Anything). Maybe(). Return( - func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { + func(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) { defer close(chStarted) - return mockEth.NewSub(t) + return make(<-chan *evmtypes.Head), mockEth.NewSub(t), nil }, - func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, ) ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(testutils.Head(0), nil).Maybe() @@ -198,11 +197,12 @@ func TestHeadTracker_Start_NewHeads(t *testing.T) { ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(testutils.Head(0), nil).Once() // for backfill ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(testutils.Head(0), nil).Maybe() - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + ch := make(chan *evmtypes.Head) + ethClient.On("SubscribeToHeads", mock.Anything). Run(func(mock.Arguments) { close(chStarted) }). - Return(sub, nil) + Return((<-chan *evmtypes.Head)(ch), sub, nil) ht := createHeadTracker(t, ethClient, config.EVM(), config.EVM().HeadTracker(), orm) ht.Start(t) @@ -243,7 +243,7 @@ func TestHeadTracker_Start(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) mockEth := &testutils.MockEth{EthClient: ethClient} sub := mockEth.NewSub(t) - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(sub, nil).Maybe() + ethClient.On("SubscribeToHeads", mock.Anything, mock.Anything).Return(nil, sub, nil).Maybe() return createHeadTracker(t, ethClient, config.EVM(), config.EVM().HeadTracker(), opts.ORM) } t.Run("Starts even if failed to get initialHead", func(t *testing.T) { @@ -271,7 +271,7 @@ func TestHeadTracker_Start(t *testing.T) { head := testutils.Head(1000) ht.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(head, nil).Once() ht.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(nil, nil).Once() - ht.ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(nil, errors.New("failed to connect")).Maybe() + ht.ethClient.On("SubscribeToHeads", mock.Anything, mock.Anything).Return(nil, nil, errors.New("failed to connect")).Maybe() ht.Start(t) tests.AssertLogEventually(t, ht.observer, "Error handling initial head") }) @@ -286,7 +286,7 @@ func TestHeadTracker_Start(t *testing.T) { ht.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(finalizedHead, nil).Once() // on backfill ht.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(nil, errors.New("backfill call to finalized failed")).Maybe() - ht.ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(nil, errors.New("failed to connect")).Maybe() + ht.ethClient.On("SubscribeToHeads", mock.Anything, mock.Anything).Return(nil, nil, errors.New("failed to connect")).Maybe() ht.Start(t) tests.AssertLogEventually(t, ht.observer, "Received new head") tests.AssertEventually(t, func() bool { @@ -304,7 +304,7 @@ func TestHeadTracker_Start(t *testing.T) { require.NoError(t, ht.orm.IdempotentInsertHead(ctx, testutils.Head(finalizedHead.Number-1))) // on backfill ht.ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(nil, errors.New("backfill call to finalized failed")).Maybe() - ht.ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(nil, errors.New("failed to connect")).Maybe() + ht.ethClient.On("SubscribeToHeads", mock.Anything, mock.Anything).Return(nil, nil, errors.New("failed to connect")).Maybe() ht.Start(t) tests.AssertLogEventually(t, ht.observer, "Received new head") tests.AssertEventually(t, func() bool { @@ -361,14 +361,14 @@ func TestHeadTracker_CallsHeadTrackableCallbacks(t *testing.T) { chchHeaders := make(chan testutils.RawSub[*evmtypes.Head], 1) mockEth := &testutils.MockEth{EthClient: ethClient} - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + chHead := make(chan *evmtypes.Head) + ethClient.On("SubscribeToHeads", mock.Anything). Return( - func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { + func(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) { sub := mockEth.NewSub(t) - chchHeaders <- testutils.NewRawSub(ch, sub.Err()) - return sub + chchHeaders <- testutils.NewRawSub(chHead, sub.Err()) + return chHead, sub, nil }, - func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, ) ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(testutils.Head(0), nil) ethClient.On("HeadByHash", mock.Anything, mock.Anything).Return(testutils.Head(0), nil).Maybe() @@ -397,16 +397,19 @@ func TestHeadTracker_ReconnectOnError(t *testing.T) { ethClient := testutils.NewEthClientMockWithDefaultChain(t) mockEth := &testutils.MockEth{EthClient: ethClient} - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + chHead := make(chan *evmtypes.Head) + ethClient.On("SubscribeToHeads", mock.Anything). Return( - func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { return mockEth.NewSub(t) }, - func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, + func(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) { + return chHead, mockEth.NewSub(t), nil + }, ) - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(nil, errors.New("cannot reconnect")) - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + ethClient.On("SubscribeToHeads", mock.Anything).Return((<-chan *evmtypes.Head)(chHead), nil, errors.New("cannot reconnect")) + ethClient.On("SubscribeToHeads", mock.Anything). Return( - func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { return mockEth.NewSub(t) }, - func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, + func(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) { + return chHead, mockEth.NewSub(t), nil + }, ) ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(testutils.Head(0), nil) checker := &mocks.MockHeadTrackable{} @@ -431,16 +434,16 @@ func TestHeadTracker_ResubscribeOnSubscriptionError(t *testing.T) { ethClient := testutils.NewEthClientMockWithDefaultChain(t) + ch := make(chan *evmtypes.Head) chchHeaders := make(chan testutils.RawSub[*evmtypes.Head], 1) mockEth := &testutils.MockEth{EthClient: ethClient} - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + ethClient.On("SubscribeToHeads", mock.Anything). Return( - func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { + func(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) { sub := mockEth.NewSub(t) chchHeaders <- testutils.NewRawSub(ch, sub.Err()) - return sub + return ch, sub, nil }, - func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, ) ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(testutils.Head(0), nil) ethClient.On("HeadByHash", mock.Anything, mock.Anything).Return(testutils.Head(0), nil).Maybe() @@ -496,14 +499,14 @@ func TestHeadTracker_Start_LoadsLatestChain(t *testing.T) { chchHeaders := make(chan testutils.RawSub[*evmtypes.Head], 1) mockEth := &testutils.MockEth{EthClient: ethClient} - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + ch := make(chan *evmtypes.Head) + ethClient.On("SubscribeToHeads", mock.Anything). Return( - func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { + func(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) { sub := mockEth.NewSub(t) chchHeaders <- testutils.NewRawSub(ch, sub.Err()) - return sub + return ch, sub, nil }, - func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, ) orm := headtracker.NewORM(*testutils.FixtureChainID, db) @@ -553,14 +556,14 @@ func TestHeadTracker_SwitchesToLongestChainWithHeadSamplingEnabled(t *testing.T) chchHeaders := make(chan testutils.RawSub[*evmtypes.Head], 1) mockEth := &testutils.MockEth{EthClient: ethClient} - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + chHead := make(chan *evmtypes.Head) + ethClient.On("SubscribeToHeads", mock.Anything). Return( - func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { + func(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) { sub := mockEth.NewSub(t) - chchHeaders <- testutils.NewRawSub(ch, sub.Err()) - return sub + chchHeaders <- testutils.NewRawSub(chHead, sub.Err()) + return chHead, sub, nil }, - func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, ) // --------------------- @@ -674,14 +677,14 @@ func TestHeadTracker_SwitchesToLongestChainWithHeadSamplingDisabled(t *testing.T chchHeaders := make(chan testutils.RawSub[*evmtypes.Head], 1) mockEth := &testutils.MockEth{EthClient: ethClient} - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + chHead := make(chan *evmtypes.Head) + ethClient.On("SubscribeToHeads", mock.Anything). Return( - func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { + func(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) { sub := mockEth.NewSub(t) - chchHeaders <- testutils.NewRawSub(ch, sub.Err()) - return sub + chchHeaders <- testutils.NewRawSub(chHead, sub.Err()) + return chHead, sub, nil }, - func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, ) // --------------------- diff --git a/core/chains/evm/testutils/client.go b/core/chains/evm/testutils/client.go index 1e5523fbff9..dfa4653de1c 100644 --- a/core/chains/evm/testutils/client.go +++ b/core/chains/evm/testutils/client.go @@ -6,6 +6,7 @@ import ( "net/http" "net/http/httptest" "net/url" + "strings" "sync" "sync/atomic" "testing" @@ -146,14 +147,75 @@ func (ts *testWSServer) newWSHandler(chainID *big.Int, callback JSONRPCHandler) return } ts.t.Log("Received message", string(data)) + req := gjson.ParseBytes(data) - if !req.IsObject() { - if isSingleObjectArray := req.IsArray() && len(req.Array()) == 1; !isSingleObjectArray { - ts.t.Logf("Request must be object: %v", req.Type) + + if req.IsArray() { // Handle batch request + ts.t.Log("Received batch request") + responses := []string{} + for _, reqElem := range req.Array() { + m := reqElem.Get("method") + if m.Type != gjson.String { + ts.t.Logf("Method must be string: %v", m.Type) + continue + } + + var resp JSONRPCResponse + if chainID != nil && m.String() == "eth_chainId" { + resp.Result = `"0x` + chainID.Text(16) + `"` + } else if m.String() == "eth_syncing" { + resp.Result = "false" + } else { + resp = callback(m.String(), reqElem.Get("params")) + } + id := reqElem.Get("id") + var msg string + if resp.Error.Message != "" { + msg = fmt.Sprintf(`{"jsonrpc":"2.0","id":%s,"error":{"code":%d,"message":"%s"}}`, id, resp.Error.Code, resp.Error.Message) + } else { + msg = fmt.Sprintf(`{"jsonrpc":"2.0","id":%s,"result":%s}`, id, resp.Result) + } + responses = append(responses, msg) + } + responseBatch := fmt.Sprintf("[%s]", strings.Join(responses, ",")) + ts.t.Logf("Sending batch response: %v", responseBatch) + ts.mu.Lock() + err = conn.WriteMessage(websocket.BinaryMessage, []byte(responseBatch)) + ts.mu.Unlock() + if err != nil { + ts.t.Logf("Failed to write message: %v", err) + return + } + } else { // Handle single request + m := req.Get("method") + if m.Type != gjson.String { + ts.t.Logf("Method must be string: %v", m.Type) return } - req = req.Array()[0] + var resp JSONRPCResponse + if chainID != nil && m.String() == "eth_chainId" { + resp.Result = `"0x` + chainID.Text(16) + `"` + } else if m.String() == "eth_syncing" { + resp.Result = "false" + } else { + resp = callback(m.String(), req.Get("params")) + } + id := req.Get("id") + var msg string + if resp.Error.Message != "" { + msg = fmt.Sprintf(`{"jsonrpc":"2.0","id":%s,"error":{"code":%d,"message":"%s"}}`, id, resp.Error.Code, resp.Error.Message) + } else { + msg = fmt.Sprintf(`{"jsonrpc":"2.0","id":%s,"result":%s}`, id, resp.Result) + } + ts.t.Logf("Sending message: %v", msg) + ts.mu.Lock() + err = conn.WriteMessage(websocket.BinaryMessage, []byte(msg)) + ts.mu.Unlock() + if err != nil { + ts.t.Logf("Failed to write message: %v", err) + return + } } if e := req.Get("error"); e.Exists() { ts.t.Logf("Received jsonrpc error: %v", e) diff --git a/core/chains/legacyevm/chain.go b/core/chains/legacyevm/chain.go index d296c6e8551..277d6283690 100644 --- a/core/chains/legacyevm/chain.go +++ b/core/chains/legacyevm/chain.go @@ -195,7 +195,11 @@ func newChain(ctx context.Context, cfg *evmconfig.ChainScoped, nodes []*toml.Nod if !opts.AppConfig.EVMRPCEnabled() { client = evmclient.NewNullClient(chainID, l) } else if opts.GenEthClient == nil { - client = evmclient.NewEvmClient(cfg.EVM().NodePool(), cfg.EVM(), cfg.EVM().NodePool().Errors(), l, chainID, nodes, cfg.EVM().ChainType()) + var err error + client, err = evmclient.NewEvmClient(cfg.EVM().NodePool(), cfg.EVM(), cfg.EVM().NodePool().Errors(), l, chainID, nodes, cfg.EVM().ChainType()) + if err != nil { + return nil, err + } } else { client = opts.GenEthClient(chainID) } @@ -425,7 +429,6 @@ func (c *chain) listNodeStatuses(start, end int) ([]types.NodeStatus, int, error for _, n := range nodes[start:end] { var ( nodeState string - exists bool ) toml, err := gotoml.Marshal(n) if err != nil { @@ -434,10 +437,11 @@ func (c *chain) listNodeStatuses(start, end int) ([]types.NodeStatus, int, error if states == nil { nodeState = "Unknown" } else { - nodeState, exists = states[*n.Name] - if !exists { - // The node is in the DB and the chain is enabled but it's not running - nodeState = "NotLoaded" + // The node is in the DB and the chain is enabled but it's not running + nodeState = "NotLoaded" + s, exists := states[*n.Name] + if exists { + nodeState = s } } stats = append(stats, types.NodeStatus{ diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index b60dd8d73ce..d3af33d85bf 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -514,8 +514,9 @@ func NewEthMocks(t testing.TB) *evmclimocks.Client { func NewEthMocksWithStartupAssertions(t testing.TB) *evmclimocks.Client { testutils.SkipShort(t, "long test") c := NewEthMocks(t) + chHead := make(<-chan *evmtypes.Head) c.On("Dial", mock.Anything).Maybe().Return(nil) - c.On("SubscribeNewHead", mock.Anything, mock.Anything).Maybe().Return(EmptyMockSubscription(t), nil) + c.On("SubscribeToHeads", mock.Anything).Maybe().Return(chHead, EmptyMockSubscription(t), nil) c.On("SendTransaction", mock.Anything, mock.Anything).Maybe().Return(nil) c.On("HeadByNumber", mock.Anything, mock.Anything).Maybe().Return(Head(0), nil) c.On("ConfiguredChainID").Maybe().Return(&FixtureChainID) @@ -536,8 +537,9 @@ func NewEthMocksWithStartupAssertions(t testing.TB) *evmclimocks.Client { func NewEthMocksWithTransactionsOnBlocksAssertions(t testing.TB) *evmclimocks.Client { testutils.SkipShort(t, "long test") c := NewEthMocks(t) + chHead := make(<-chan *evmtypes.Head) c.On("Dial", mock.Anything).Maybe().Return(nil) - c.On("SubscribeNewHead", mock.Anything, mock.Anything).Maybe().Return(EmptyMockSubscription(t), nil) + c.On("SubscribeToHeads", mock.Anything).Maybe().Return(chHead, EmptyMockSubscription(t), nil) c.On("SendTransaction", mock.Anything, mock.Anything).Maybe().Return(nil) c.On("SendTransactionReturnCode", mock.Anything, mock.Anything, mock.Anything).Maybe().Return(client.Successful, nil) // Construct chain @@ -1297,7 +1299,7 @@ func MockApplicationEthCalls(t *testing.T, app *TestApplication, ethClient *evmc // Start ethClient.On("Dial", mock.Anything).Return(nil) - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(sub, nil).Maybe() + ethClient.On("SubscribeToHeads", mock.Anything).Return(make(<-chan *evmtypes.Head), sub, nil).Maybe() ethClient.On("ConfiguredChainID", mock.Anything).Return(evmtest.MustGetDefaultChainID(t, app.GetConfig().EVMConfigs()), nil) ethClient.On("PendingNonceAt", mock.Anything, mock.Anything).Return(uint64(0), nil).Maybe() ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(nil, nil).Maybe() diff --git a/core/internal/features/features_test.go b/core/internal/features/features_test.go index edd5db410ff..79af91796dd 100644 --- a/core/internal/features/features_test.go +++ b/core/internal/features/features_test.go @@ -1294,14 +1294,14 @@ func TestIntegration_BlockHistoryEstimator(t *testing.T) { h42 := evmtypes.Head{Hash: b42.Hash, ParentHash: h41.Hash, Number: 42, EVMChainID: evmChainID} mockEth := &evmtestutils.MockEth{EthClient: ethClient} - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + ethClient.On("SubscribeToHeads", mock.Anything). Return( - func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { + func(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) { + ch := make(chan *evmtypes.Head) sub := mockEth.NewSub(t) chchNewHeads <- evmtestutils.NewRawSub(ch, sub.Err()) - return sub + return ch, sub, nil }, - func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, ) // Nonce syncer ethClient.On("PendingNonceAt", mock.Anything, mock.Anything).Maybe().Return(uint64(0), nil) From a2b8566e406fda91225f9b36a73c3c0a9ad3225e Mon Sep 17 00:00:00 2001 From: Ryan Tinianov Date: Tue, 15 Oct 2024 12:14:17 -0400 Subject: [PATCH 2/2] Dedupe webapi trigger structs (#14733) * Dedupe webapi trigger structs * Re-generate event trigger to get comments --- core/capabilities/webapi/trigger.go | 10 ++--- core/capabilities/webapi/trigger_test.go | 10 +++-- .../webapicap/event_trigger-schema.json | 16 ++++--- .../webapicap/event_trigger_generated.go | 11 +++-- .../handlers/webapicapabilities/handler.go | 3 +- .../webapicapabilities/handler_test.go | 7 ++-- .../handlers/webapicapabilities/webapi.go | 42 ------------------- 7 files changed, 34 insertions(+), 65 deletions(-) diff --git a/core/capabilities/webapi/trigger.go b/core/capabilities/webapi/trigger.go index 674bfbad81c..8eb971f9a83 100644 --- a/core/capabilities/webapi/trigger.go +++ b/core/capabilities/webapi/trigger.go @@ -44,7 +44,7 @@ type triggerConnectorHandler struct { services.StateMachine capabilities.CapabilityInfo - capabilities.Validator[webapicap.TriggerConfig, struct{}, capabilities.TriggerResponse] + capabilities.Validator[webapicap.TriggerConfig, struct{}, webapicap.TriggerRequestPayload] connector connector.GatewayConnector lggr logger.Logger mu sync.Mutex @@ -61,7 +61,7 @@ func NewTrigger(config string, registry core.CapabilitiesRegistry, connector con } handler := &triggerConnectorHandler{ CapabilityInfo: webapiTriggerInfo, - Validator: capabilities.NewValidator[webapicap.TriggerConfig, struct{}, capabilities.TriggerResponse](capabilities.ValidatorArgs{Info: webapiTriggerInfo}), + Validator: capabilities.NewValidator[webapicap.TriggerConfig, struct{}, webapicap.TriggerRequestPayload](capabilities.ValidatorArgs{Info: webapiTriggerInfo}), connector: connector, registeredWorkflows: map[string]webapiTrigger{}, registry: registry, @@ -72,7 +72,7 @@ func NewTrigger(config string, registry core.CapabilitiesRegistry, connector con } // processTrigger iterates over each topic, checking against senders and rateLimits, then starting event processing and responding -func (h *triggerConnectorHandler) processTrigger(ctx context.Context, gatewayID string, body *api.MessageBody, sender ethCommon.Address, payload webapicapabilities.TriggerRequestPayload) error { +func (h *triggerConnectorHandler) processTrigger(ctx context.Context, gatewayID string, body *api.MessageBody, sender ethCommon.Address, payload webapicap.TriggerRequestPayload) error { // Pass on the payload with the expectation that it's in an acceptable format for the executor wrappedPayload, err := values.WrapMap(payload) if err != nil { @@ -103,7 +103,7 @@ func (h *triggerConnectorHandler) processTrigger(ctx context.Context, gatewayID continue } fullyMatchedWorkflows++ - TriggerEventID := body.Sender + payload.TriggerEventID + TriggerEventID := body.Sender + payload.TriggerEventId tr := capabilities.TriggerResponse{ Event: capabilities.TriggerEvent{ TriggerType: TriggerType, @@ -135,7 +135,7 @@ func (h *triggerConnectorHandler) HandleGatewayMessage(ctx context.Context, gate // TODO: Validate Signature body := &msg.Body sender := ethCommon.HexToAddress(body.Sender) - var payload webapicapabilities.TriggerRequestPayload + var payload webapicap.TriggerRequestPayload err := json.Unmarshal(body.Payload, &payload) if err != nil { h.lggr.Errorw("error decoding payload", "err", err) diff --git a/core/capabilities/webapi/trigger_test.go b/core/capabilities/webapi/trigger_test.go index c848e9502ac..f5e12a48758 100644 --- a/core/capabilities/webapi/trigger_test.go +++ b/core/capabilities/webapi/trigger_test.go @@ -16,6 +16,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" registrymock "github.com/smartcontractkit/chainlink-common/pkg/types/core/mocks" "github.com/smartcontractkit/chainlink-common/pkg/values" + + "github.com/smartcontractkit/chainlink/v2/core/capabilities/webapi/webapicap" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" corelogger "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/api" @@ -191,7 +193,7 @@ func TestTriggerExecute(t *testing.T) { requireNoChanMsg(t, channel2) data := received.Event.Outputs - var payload webapicapabilities.TriggerRequestPayload + var payload webapicap.TriggerRequestPayload unwrapErr := data.UnwrapTo(&payload) require.NoError(t, unwrapErr) require.Equal(t, payload.Topics, []string{"daily_price_update"}) @@ -210,7 +212,7 @@ func TestTriggerExecute(t *testing.T) { sent := <-channel require.Equal(t, sent.Event.TriggerType, TriggerType) data := sent.Event.Outputs - var payload webapicapabilities.TriggerRequestPayload + var payload webapicap.TriggerRequestPayload unwrapErr := data.UnwrapTo(&payload) require.NoError(t, unwrapErr) require.Equal(t, payload.Topics, []string{"ad_hoc_price_update"}) @@ -218,7 +220,7 @@ func TestTriggerExecute(t *testing.T) { sent2 := <-channel2 require.Equal(t, sent2.Event.TriggerType, TriggerType) data2 := sent2.Event.Outputs - var payload2 webapicapabilities.TriggerRequestPayload + var payload2 webapicap.TriggerRequestPayload err2 := data2.UnwrapTo(&payload2) require.NoError(t, err2) require.Equal(t, payload2.Topics, []string{"ad_hoc_price_update"}) @@ -346,7 +348,7 @@ func TestTriggerExecute2WorkflowsSameTopicDifferentAllowLists(t *testing.T) { require.Equal(t, received.Event.TriggerType, TriggerType) require.NoError(t, chanErr) data := received.Event.Outputs - var payload webapicapabilities.TriggerRequestPayload + var payload webapicap.TriggerRequestPayload unwrapErr := data.UnwrapTo(&payload) require.NoError(t, unwrapErr) require.Equal(t, payload.Topics, []string{"daily_price_update"}) diff --git a/core/capabilities/webapi/webapicap/event_trigger-schema.json b/core/capabilities/webapi/webapicap/event_trigger-schema.json index eb9b5400060..f7b5997399e 100644 --- a/core/capabilities/webapi/webapicap/event_trigger-schema.json +++ b/core/capabilities/webapi/webapicap/event_trigger-schema.json @@ -3,6 +3,7 @@ "$id": "https://github.com/smartcontractkit/chainlink/v2/core/capabilities/webapi/webapicap/web-api-trigger@1.0.0", "$defs": { "TriggerConfig": { + "description": "See https://gateway-us-1.chain.link/web-api-trigger", "type": "object", "properties": { "allowedSenders": { @@ -53,24 +54,29 @@ "type": "object", "properties": { "trigger_id": { - "type": "string" + "type": "string", + "description": "ID of the trigger corresponding to the capability ID." }, "trigger_event_id": { - "type": "string" + "type": "string", + "description": "Uniquely identifies generated event (scoped to trigger_id and sender)." }, "timestamp": { "type": "integer", - "format": "int64" + "format": "int64", + "description": "Timestamp of the event (unix time), needs to be within certain freshness to be processed." }, "topics": { "type": "array", "items": { - "type": "string" + "type": "string", + "description" : "An array of a single topic (string) to be started by this event." } }, "params": { "type": "object", - "additionalProperties": true + "additionalProperties": true, + "description": "Key-value pairs for the workflow engine, untranslated." } }, "required": ["trigger_id", "trigger_event_id", "timestamp", "topics", "params"], diff --git a/core/capabilities/webapi/webapicap/event_trigger_generated.go b/core/capabilities/webapi/webapicap/event_trigger_generated.go index 4cc34acb11a..b67a7875ac4 100644 --- a/core/capabilities/webapi/webapicap/event_trigger_generated.go +++ b/core/capabilities/webapi/webapicap/event_trigger_generated.go @@ -56,6 +56,7 @@ type Trigger struct { Outputs *TriggerRequestPayload `json:"Outputs,omitempty" yaml:"Outputs,omitempty" mapstructure:"Outputs,omitempty"` } +// See https://gateway-us-1.chain.link/web-api-trigger type TriggerConfig struct { // AllowedSenders corresponds to the JSON schema field "allowedSenders". AllowedSenders []string `json:"allowedSenders" yaml:"allowedSenders" mapstructure:"allowedSenders"` @@ -98,22 +99,24 @@ func (j *TriggerConfig) UnmarshalJSON(b []byte) error { } type TriggerRequestPayload struct { - // Params corresponds to the JSON schema field "params". + // Key-value pairs for the workflow engine, untranslated. Params TriggerRequestPayloadParams `json:"params" yaml:"params" mapstructure:"params"` - // Timestamp corresponds to the JSON schema field "timestamp". + // Timestamp of the event (unix time), needs to be within certain freshness to be + // processed. Timestamp int64 `json:"timestamp" yaml:"timestamp" mapstructure:"timestamp"` // Topics corresponds to the JSON schema field "topics". Topics []string `json:"topics" yaml:"topics" mapstructure:"topics"` - // TriggerEventId corresponds to the JSON schema field "trigger_event_id". + // Uniquely identifies generated event (scoped to trigger_id and sender). TriggerEventId string `json:"trigger_event_id" yaml:"trigger_event_id" mapstructure:"trigger_event_id"` - // TriggerId corresponds to the JSON schema field "trigger_id". + // ID of the trigger corresponding to the capability ID. TriggerId string `json:"trigger_id" yaml:"trigger_id" mapstructure:"trigger_id"` } +// Key-value pairs for the workflow engine, untranslated. type TriggerRequestPayloadParams map[string]interface{} // UnmarshalJSON implements json.Unmarshaler. diff --git a/core/services/gateway/handlers/webapicapabilities/handler.go b/core/services/gateway/handlers/webapicapabilities/handler.go index 744bdc17406..f6222cb68f2 100644 --- a/core/services/gateway/handlers/webapicapabilities/handler.go +++ b/core/services/gateway/handlers/webapicapabilities/handler.go @@ -9,6 +9,7 @@ import ( "go.uber.org/multierr" + "github.com/smartcontractkit/chainlink/v2/core/capabilities/webapi/webapicap" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/api" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/config" @@ -202,7 +203,7 @@ func (h *handler) HandleUserMessage(ctx context.Context, msg *api.Message, callb don := h.don h.mu.Unlock() body := msg.Body - var payload TriggerRequestPayload + var payload webapicap.TriggerRequestPayload err := json.Unmarshal(body.Payload, &payload) if err != nil { h.lggr.Errorw("error decoding payload", "err", err) diff --git a/core/services/gateway/handlers/webapicapabilities/handler_test.go b/core/services/gateway/handlers/webapicapabilities/handler_test.go index 35b8d65973a..8d0308c0e71 100644 --- a/core/services/gateway/handlers/webapicapabilities/handler_test.go +++ b/core/services/gateway/handlers/webapicapabilities/handler_test.go @@ -3,14 +3,13 @@ package webapicapabilities import ( "encoding/json" "fmt" + "strconv" "testing" "time" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "strconv" - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/ethereum/go-ethereum/crypto" @@ -294,7 +293,7 @@ func TestHandlerReceiveHTTPMessageFromClient(t *testing.T) { err := handler.HandleUserMessage(ctx, invalidMsg, ch) require.NoError(t, err) resp := <-ch - require.Equal(t, handlers.UserCallbackPayload{Msg: invalidMsg, ErrCode: api.UserMessageParseError, ErrMsg: "error decoding payload"}, resp) + require.Equal(t, handlers.UserCallbackPayload{Msg: invalidMsg, ErrCode: api.UserMessageParseError, ErrMsg: "error decoding payload field params in TriggerRequestPayload: required"}, resp) _, open := <-ch require.Equal(t, open, false) }) @@ -305,7 +304,7 @@ func TestHandlerReceiveHTTPMessageFromClient(t *testing.T) { err := handler.HandleUserMessage(ctx, invalidMsg, ch) require.NoError(t, err) resp := <-ch - require.Equal(t, handlers.UserCallbackPayload{Msg: invalidMsg, ErrCode: api.UserMessageParseError, ErrMsg: "error decoding payload"}, resp) + require.Equal(t, handlers.UserCallbackPayload{Msg: invalidMsg, ErrCode: api.UserMessageParseError, ErrMsg: "error decoding payload field params in TriggerRequestPayload: required"}, resp) _, open := <-ch require.Equal(t, open, false) }) diff --git a/core/services/gateway/handlers/webapicapabilities/webapi.go b/core/services/gateway/handlers/webapicapabilities/webapi.go index ce14eddc27e..f6e2f6679a1 100644 --- a/core/services/gateway/handlers/webapicapabilities/webapi.go +++ b/core/services/gateway/handlers/webapicapabilities/webapi.go @@ -1,9 +1,5 @@ package webapicapabilities -import ( - "github.com/smartcontractkit/chainlink-common/pkg/values" -) - type TargetRequestPayload struct { URL string `json:"url"` // URL to query, only http and https protocols are supported. Method string `json:"method,omitempty"` // HTTP verb, defaults to GET. @@ -20,44 +16,6 @@ type TargetResponsePayload struct { Body []byte `json:"body,omitempty"` // HTTP response body } -// https://gateway-us-1.chain.link/web-api-trigger -// -// { -// jsonrpc: "2.0", -// id: "...", -// method: "web-api-trigger", -// params: { -// signature: "...", -// body: { -// don_id: "workflow_123", -// payload: { -// trigger_id: "web-api-trigger@1.0.0", -// trigger_event_id: "action_1234567890", -// timestamp: 1234567890, -// topics: ["daily_price_update"], -// params: { -// bid: "101", -// ask: "102" -// } -// } -// } -// } -// } -// -// from Web API Trigger Doc, with modifications. -// trigger_id - ID of the trigger corresponding to the capability ID -// trigger_event_id - uniquely identifies generated event (scoped to trigger_id and sender) -// timestamp - timestamp of the event (unix time), needs to be within certain freshness to be processed -// topics - an array of a single topic (string) to be started by this event -// params - key-value pairs for the workflow engine, untranslated. -type TriggerRequestPayload struct { - TriggerID string `json:"trigger_id"` - TriggerEventID string `json:"trigger_event_id"` - Timestamp int64 `json:"timestamp"` - Topics []string `json:"topics"` - Params values.Map `json:"params"` -} - type TriggerResponsePayload struct { ErrorMessage string `json:"error_message,omitempty"` // ERROR, ACCEPTED, PENDING, COMPLETED