From 800e8d8f6d7fb7848dd6ae0ce51284079c6c6c12 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Mon, 29 Apr 2024 19:16:09 +0200 Subject: [PATCH 01/53] implement RPCClient changes --- common/client/mock_node_client_test.go | 28 +-- common/client/mock_rpc_test.go | 28 +-- common/client/multi_node.go | 4 +- common/client/node.go | 19 +- common/client/node_fsm.go | 8 +- common/client/node_fsm_test.go | 10 +- common/client/node_lifecycle.go | 6 +- common/client/node_lifecycle_test.go | 62 +++---- common/client/types.go | 3 +- core/chains/evm/client/chain_client.go | 7 +- core/chains/evm/client/chain_client_test.go | 2 +- core/chains/evm/client/client.go | 6 +- core/chains/evm/client/mocks/rpc_client.go | 71 ++++--- core/chains/evm/client/rpc_client.go | 101 ++++++++-- core/chains/evm/client/rpc_client_test.go | 175 ++++++++++++++++++ .../{chain_id_sub.go => sub_forwarder.go} | 53 ++++-- ...n_id_sub_test.go => sub_forwarder_test.go} | 0 17 files changed, 422 insertions(+), 161 deletions(-) create mode 100644 core/chains/evm/client/rpc_client_test.go rename core/chains/evm/client/{chain_id_sub.go => sub_forwarder.go} (52%) rename core/chains/evm/client/{chain_id_sub_test.go => sub_forwarder_test.go} (100%) diff --git a/common/client/mock_node_client_test.go b/common/client/mock_node_client_test.go index ec83158a5ff..bba8554ae3c 100644 --- a/common/client/mock_node_client_test.go +++ b/common/client/mock_node_client_test.go @@ -111,11 +111,6 @@ func (_m *mockNodeClient[CHAIN_ID, HEAD]) DialHTTP() error { return r0 } -// DisconnectAll provides a mock function with given fields: -func (_m *mockNodeClient[CHAIN_ID, HEAD]) DisconnectAll() { - _m.Called() -} - // 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) @@ -177,32 +172,29 @@ func (_m *mockNodeClient[CHAIN_ID, HEAD]) SetAliveLoopSub(_a0 types.Subscription _m.Called(_a0) } -// Subscribe provides a mock function with given fields: ctx, channel, args -func (_m *mockNodeClient[CHAIN_ID, HEAD]) Subscribe(ctx context.Context, channel chan<- HEAD, args ...interface{}) (types.Subscription, error) { - var _ca []interface{} - _ca = append(_ca, ctx, channel) - _ca = append(_ca, args...) - ret := _m.Called(_ca...) +// 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 Subscribe") + 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, ...interface{}) (types.Subscription, error)); ok { - return rf(ctx, channel, args...) + 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, ...interface{}) types.Subscription); ok { - r0 = rf(ctx, channel, args...) + 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, ...interface{}) error); ok { - r1 = rf(ctx, channel, args...) + if rf, ok := ret.Get(1).(func(context.Context, chan<- HEAD) error); ok { + r1 = rf(ctx, channel) } else { r1 = ret.Error(1) } diff --git a/common/client/mock_rpc_test.go b/common/client/mock_rpc_test.go index 54f57e81f65..156146634de 100644 --- a/common/client/mock_rpc_test.go +++ b/common/client/mock_rpc_test.go @@ -303,11 +303,6 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS return r0 } -// 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() -} - // 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) @@ -637,32 +632,29 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS return r0 } -// Subscribe provides a mock function with given fields: ctx, channel, args -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Subscribe(ctx context.Context, channel chan<- HEAD, args ...interface{}) (types.Subscription, error) { - var _ca []interface{} - _ca = append(_ca, ctx, channel) - _ca = append(_ca, args...) - ret := _m.Called(_ca...) +// 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 Subscribe") + 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, ...interface{}) (types.Subscription, error)); ok { - return rf(ctx, channel, args...) + 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, ...interface{}) types.Subscription); ok { - r0 = rf(ctx, channel, args...) + 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, ...interface{}) error); ok { - r1 = rf(ctx, channel, args...) + if rf, ok := ret.Get(1).(func(context.Context, chan<- HEAD) error); ok { + r1 = rf(ctx, channel) } else { r1 = ret.Error(1) } diff --git a/common/client/multi_node.go b/common/client/multi_node.go index cc8daed599c..5372fc0d304 100644 --- a/common/client/multi_node.go +++ b/common/client/multi_node.go @@ -788,12 +788,12 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP 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]) Subscribe(ctx context.Context, channel chan<- HEAD, args ...interface{}) (s types.Subscription, err error) { +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().Subscribe(ctx, channel, args...) + 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]) TokenBalance(ctx context.Context, account ADDR, tokenAddr ADDR) (b *big.Int, err error) { diff --git a/common/client/node.go b/common/client/node.go index 6450b086f10..1c47e2c07b8 100644 --- a/common/client/node.go +++ b/common/client/node.go @@ -57,6 +57,18 @@ type ChainConfig interface { ChainType() commonconfig.ChainType } +type RPCChainInfo struct { + // MostRecentBlockNumber - latest block number + MostRecentBlockNumber int64 + // HighestBlockNumber - maximum block number ever observed + HighestBlockNumber int64 + // MostRecentlyFinalizedBlockNum - latest finalized block number + MostRecentlyFinalizedBlockNum int64 + // HighestFinalizedBlockNum - maximum block number ever observed for finalized block + HighestFinalizedBlockNum int64 + TotalDifficulty *big.Int +} + //go:generate mockery --quiet --name Node --structname mockNode --filename "mock_node_test.go" --inpackage --case=underscore type Node[ CHAIN_ID types.ID, @@ -321,13 +333,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 e9105dcc060..0e4cc6cbfa7 100644 --- a/common/client/node_fsm.go +++ b/common/client/node_fsm.go @@ -209,7 +209,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)) @@ -234,7 +234,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)) @@ -277,7 +277,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)) @@ -302,7 +302,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 36cee65e09e..f1ba37205e1 100644 --- a/common/client/node_fsm_test.go +++ b/common/client/node_fsm_test.go @@ -53,33 +53,33 @@ func TestUnit_Node_StateTransitions(t *testing.T) { const destinationState = nodeStateOutOfSync allowedStates := []nodeState{nodeStateAlive} rpc := newMockNodeClient[types.ID, Head](t) - rpc.On("DisconnectAll").Once() + rpc.On("Close").Once() 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.On("Close").Times(len(allowedStates)) 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.On("Close").Times(len(allowedStates)) 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.On("Close").Times(len(allowedStates)) 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.On("Close").Once() node := newTestNode(t, testNodeOpts{rpc: rpc}) node.setState(nodeStateDialed) fn := new(fnMock) diff --git a/common/client/node_lifecycle.go b/common/client/node_lifecycle.go index 4707a60426f..29c7249127f 100644 --- a/common/client/node_lifecycle.go +++ b/common/client/node_lifecycle.go @@ -68,8 +68,6 @@ const ( msgDegradedState = "Chainlink is now operating in a degraded state and urgent action is required to resolve the issue" ) -const rpcSubscriptionMethodNewHeads = "newHeads" - // Node is a FSM // Each state has a loop that goes with it, which monitors the node and moves it into another state as necessary. // Only one loop must run at a time. @@ -100,7 +98,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { lggr.Tracew("Alive loop starting", "nodeState", n.State()) headsC := make(chan HEAD) - sub, err := n.rpc.Subscribe(n.nodeCtx, headsC, rpcSubscriptionMethodNewHeads) + sub, err := n.rpc.SubscribeNewHead(n.nodeCtx, headsC) if err != nil { lggr.Errorw("Initial subscribe for heads failed", "nodeState", n.State()) n.declareUnreachable() @@ -329,7 +327,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(isOutOfSync func(num int64, td lggr.Tracew("Successfully subscribed to heads feed on out-of-sync RPC node", "nodeState", n.State()) ch := make(chan HEAD) - sub, err := n.rpc.Subscribe(n.nodeCtx, ch, rpcSubscriptionMethodNewHeads) + sub, err := n.rpc.SubscribeNewHead(n.nodeCtx, ch) if err != nil { lggr.Errorw("Failed to subscribe heads on out-of-sync RPC node", "nodeState", n.State(), "err", err) n.declareUnreachable() diff --git a/common/client/node_lifecycle_test.go b/common/client/node_lifecycle_test.go index b3c09b35000..2fdd14916dd 100644 --- a/common/client/node_lifecycle_test.go +++ b/common/client/node_lifecycle_test.go @@ -50,8 +50,8 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() expectedError := errors.New("failed to subscribe to rpc") - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(nil, expectedError).Once() - rpc.On("DisconnectAll").Once() + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(nil, expectedError).Once() + rpc.On("Close").Once() // might be called in unreachable loop rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() @@ -76,10 +76,10 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { close(errChan) sub.On("Err").Return((<-chan error)(errChan)).Once() sub.On("Unsubscribe").Once() - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(sub, nil).Once() + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(sub, nil).Once() rpc.On("SetAliveLoopSub", sub).Once() // disconnects all on transfer to unreachable - rpc.On("DisconnectAll").Once() + rpc.On("Close").Once() // might be called in unreachable loop rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() @@ -91,7 +91,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { sub := mocks.NewSubscription(t) sub.On("Err").Return((<-chan error)(nil)) sub.On("Unsubscribe").Once() - opts.rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(sub, nil).Once() + opts.rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(sub, nil).Once() opts.rpc.On("SetAliveLoopSub", sub).Once() return newDialedNode(t, opts) } @@ -169,7 +169,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { pollError := errors.New("failed to get ClientVersion") rpc.On("ClientVersion", mock.Anything).Return("", pollError) // disconnects all on transfer to unreachable - rpc.On("DisconnectAll").Once() + rpc.On("Close").Once() // might be called in unreachable loop rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() @@ -226,7 +226,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { assert.Equal(t, nodeStateOutOfSync, node.State()) }).Once() // disconnects all on transfer to unreachable or outOfSync - rpc.On("DisconnectAll").Maybe() + rpc.On("Close").Maybe() // might be called in unreachable loop rpc.On("Dial", mock.Anything).Run(func(_ mock.Arguments) { require.Equal(t, nodeStateOutOfSync, node.State()) @@ -297,7 +297,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { assert.Equal(t, nodeStateOutOfSync, node.State()) }).Once() // disconnects all on transfer to unreachable or outOfSync - rpc.On("DisconnectAll").Maybe() + rpc.On("Close").Maybe() // might be called in unreachable loop rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() @@ -334,7 +334,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { sub := mocks.NewSubscription(t) sub.On("Err").Return((<-chan error)(nil)) sub.On("Unsubscribe").Once() - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Run(func(args mock.Arguments) { + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { ch := args.Get(1).(chan<- Head) close(ch) }).Return(sub, nil).Once() @@ -350,7 +350,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) defer func() { assert.NoError(t, node.close()) }() // disconnects all on transfer to unreachable or outOfSync - rpc.On("DisconnectAll").Once() + rpc.On("Close").Once() // might be called in unreachable loop rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() @@ -366,7 +366,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { sub.On("Unsubscribe").Once() expectedBlockNumber := rand.Int64() expectedDiff := big.NewInt(rand.Int64()) - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Run(func(args mock.Arguments) { + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { ch := args.Get(1).(chan<- Head) go writeHeads(t, ch, head{BlockNumber: expectedBlockNumber, BlockDifficulty: expectedDiff}) }).Return(sub, nil).Once() @@ -391,7 +391,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { const blockNumber = 1000 const finalityDepth = 10 const expectedBlock = 990 - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Run(func(args mock.Arguments) { + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { ch := args.Get(1).(chan<- Head) go writeHeads(t, ch, head{BlockNumber: blockNumber - 1}, head{BlockNumber: blockNumber}, head{BlockNumber: blockNumber - 1}) }).Return(sub, nil).Once() @@ -421,7 +421,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { sub := mocks.NewSubscription(t) sub.On("Err").Return((<-chan error)(nil)) sub.On("Unsubscribe").Once() - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(sub, nil).Once() + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(sub, nil).Once() rpc.On("SetAliveLoopSub", sub).Once() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newDialedNode(t, testNodeOpts{ @@ -447,7 +447,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { sub := mocks.NewSubscription(t) sub.On("Err").Return((<-chan error)(nil)) sub.On("Unsubscribe").Once() - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(sub, nil).Once() + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(sub, nil).Once() rpc.On("SetAliveLoopSub", sub).Once() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newDialedNode(t, testNodeOpts{ @@ -474,7 +474,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { sub := mocks.NewSubscription(t) sub.On("Err").Return((<-chan error)(nil)) sub.On("Unsubscribe").Once() - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Run(func(args mock.Arguments) { + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { ch := args.Get(1).(chan<- Head) // ensure that "calculated" finalized head is larger than actual, to ensure we are correctly setting // the metric @@ -535,7 +535,7 @@ func setupRPCForAliveLoop(t *testing.T, rpc *mockNodeClient[types.ID, Head]) { aliveSubscription := mocks.NewSubscription(t) aliveSubscription.On("Err").Return((<-chan error)(nil)).Maybe() aliveSubscription.On("Unsubscribe").Maybe() - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(aliveSubscription, nil).Maybe() + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(aliveSubscription, nil).Maybe() rpc.On("SetAliveLoopSub", mock.Anything).Maybe() } @@ -546,7 +546,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { 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") node.setState(nodeStateAlive) return node } @@ -581,7 +581,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { outOfSyncSubscription.On("Err").Return((<-chan error)(nil)) outOfSyncSubscription.On("Unsubscribe").Once() heads := []head{{BlockNumber: 7}, {BlockNumber: 11}, {BlockNumber: 13}} - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Run(func(args mock.Arguments) { + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { ch := args.Get(1).(chan<- Head) go writeHeads(t, ch, heads...) }).Return(outOfSyncSubscription, nil).Once() @@ -705,7 +705,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() expectedError := errors.New("failed to subscribe") - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(nil, expectedError) + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(nil, expectedError) rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe() node.declareOutOfSync(stubIsOutOfSync) tests.AssertEventually(t, func() bool { @@ -732,7 +732,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { errChan <- errors.New("subscription was terminate") sub.On("Err").Return((<-chan error)(errChan)) sub.On("Unsubscribe").Once() - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(sub, nil).Once() + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(sub, nil).Once() rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe() node.declareOutOfSync(stubIsOutOfSync) tests.AssertLogEventually(t, observedLogs, "Subscription was terminated") @@ -758,7 +758,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { sub := mocks.NewSubscription(t) sub.On("Err").Return((<-chan error)(nil)) sub.On("Unsubscribe").Once() - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Run(func(args mock.Arguments) { + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { ch := args.Get(1).(chan<- Head) close(ch) }).Return(sub, nil).Once() @@ -789,7 +789,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { outOfSyncSubscription.On("Err").Return((<-chan error)(nil)) outOfSyncSubscription.On("Unsubscribe").Once() const highestBlock = 1000 - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Run(func(args mock.Arguments) { + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { ch := args.Get(1).(chan<- Head) go writeHeads(t, ch, head{BlockNumber: highestBlock - 1}, head{BlockNumber: highestBlock}) }).Return(outOfSyncSubscription, nil).Once() @@ -829,7 +829,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { outOfSyncSubscription := mocks.NewSubscription(t) outOfSyncSubscription.On("Err").Return((<-chan error)(nil)) outOfSyncSubscription.On("Unsubscribe").Once() - rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(outOfSyncSubscription, nil).Once() + rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(outOfSyncSubscription, nil).Once() setupRPCForAliveLoop(t, rpc) @@ -848,7 +848,7 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { 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") node.setState(nodeStateAlive) return node @@ -1006,7 +1006,7 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { 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") node.setState(nodeStateDialed) return node @@ -1149,7 +1149,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")) // disconnects all on transfer to unreachable - rpc.On("DisconnectAll") + rpc.On("Close") err := node.Start(tests.Context(t)) assert.NoError(t, err) tests.AssertLogEventually(t, observedLogs, "Dial failed: Node is unreachable") @@ -1174,7 +1174,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { assert.Equal(t, nodeStateDialed, node.State()) }).Return(nodeChainID, errors.New("failed to get chain id")) // disconnects all on transfer to unreachable - rpc.On("DisconnectAll") + rpc.On("Close") err := node.Start(tests.Context(t)) assert.NoError(t, err) tests.AssertLogEventually(t, observedLogs, "Failed to verify chain ID for node") @@ -1196,7 +1196,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { rpc.On("Dial", mock.Anything).Return(nil) rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil) // disconnects all on transfer to unreachable - rpc.On("DisconnectAll") + rpc.On("Close") err := node.Start(tests.Context(t)) assert.NoError(t, err) tests.AssertEventually(t, func() bool { @@ -1222,7 +1222,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { }).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") + rpc.On("Close") // 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)) @@ -1247,7 +1247,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { 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") + rpc.On("Close") err := node.Start(tests.Context(t)) assert.NoError(t, err) tests.AssertEventually(t, func() bool { @@ -1442,7 +1442,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { opts.config.nodeIsSyncingEnabled = true node := newTestNode(t, opts) opts.rpc.On("Close").Return(nil).Once() - opts.rpc.On("DisconnectAll") + opts.rpc.On("Close") node.setState(nodeStateDialed) return node diff --git a/common/client/types.go b/common/client/types.go index a27e6a50b73..6634c48d191 100644 --- a/common/client/types.go +++ b/common/client/types.go @@ -66,7 +66,6 @@ type NodeClient[ connection[CHAIN_ID, HEAD] DialHTTP() error - DisconnectAll() Close() ClientVersion(context.Context) (string, error) SubscribersCount() int32 @@ -145,5 +144,5 @@ type connection[ ] interface { ChainID(ctx context.Context) (CHAIN_ID, error) Dial(ctx context.Context) error - Subscribe(ctx context.Context, channel chan<- HEAD, args ...interface{}) (types.Subscription, error) + SubscribeNewHead(ctx context.Context, channel chan<- HEAD) (types.Subscription, error) } diff --git a/core/chains/evm/client/chain_client.go b/core/chains/evm/client/chain_client.go index 3ee10a600da..070e3486175 100644 --- a/core/chains/evm/client/chain_client.go +++ b/core/chains/evm/client/chain_client.go @@ -230,12 +230,7 @@ func (c *chainClient) SubscribeFilterLogs(ctx context.Context, q ethereum.Filter } func (c *chainClient) SubscribeNewHead(ctx context.Context, ch chan<- *evmtypes.Head) (ethereum.Subscription, error) { - csf := newChainIDSubForwarder(c.ConfiguredChainID(), ch) - err := csf.start(c.multiNode.Subscribe(ctx, csf.srcCh, "newHeads")) - if err != nil { - return nil, err - } - return csf, nil + return c.multiNode.SubscribeNewHead(ctx, ch) } func (c *chainClient) SuggestGasPrice(ctx context.Context) (p *big.Int, err error) { diff --git a/core/chains/evm/client/chain_client_test.go b/core/chains/evm/client/chain_client_test.go index 13fddf67622..51693fecf3f 100644 --- a/core/chains/evm/client/chain_client_test.go +++ b/core/chains/evm/client/chain_client_test.go @@ -24,7 +24,7 @@ func newMockRpc(t *testing.T) *mocks.RPCClient { 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("Subscribe", mock.Anything, mock.Anything, mock.Anything).Return(client.NewMockSubscription(), nil).Maybe() + mockRpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(client.NewMockSubscription(), nil).Maybe() mockRpc.On("SetAliveLoopSub", mock.Anything).Return().Maybe() return mockRpc } diff --git a/core/chains/evm/client/client.go b/core/chains/evm/client/client.go index 9628c74b9ab..84a00390981 100644 --- a/core/chains/evm/client/client.go +++ b/core/chains/evm/client/client.go @@ -333,7 +333,11 @@ func (client *client) SubscribeFilterLogs(ctx context.Context, q ethereum.Filter } func (client *client) SubscribeNewHead(ctx context.Context, ch chan<- *evmtypes.Head) (ethereum.Subscription, error) { - csf := newChainIDSubForwarder(client.ConfiguredChainID(), ch) + chainID := client.ConfiguredChainID() + csf := newSubForwarder(ch, func(head *evmtypes.Head) *evmtypes.Head { + head.EVMChainID = ubig.New(chainID) + return head + }, nil) err := csf.start(client.pool.EthSubscribe(ctx, csf.srcCh, "newHeads")) if err != nil { return nil, err diff --git a/core/chains/evm/client/mocks/rpc_client.go b/core/chains/evm/client/mocks/rpc_client.go index 980a215ccfe..c0e569bc810 100644 --- a/core/chains/evm/client/mocks/rpc_client.go +++ b/core/chains/evm/client/mocks/rpc_client.go @@ -9,6 +9,8 @@ import ( 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" @@ -442,6 +444,24 @@ func (_m *RPCClient) FilterEvents(ctx context.Context, query ethereum.FilterQuer return r0, r1 } +// GetInterceptedChainInfo provides a mock function with given fields: +func (_m *RPCClient) GetInterceptedChainInfo() commonclient.RPCChainInfo { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetInterceptedChainInfo") + } + + var r0 commonclient.RPCChainInfo + if rf, ok := ret.Get(0).(func() commonclient.RPCChainInfo); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(commonclient.RPCChainInfo) + } + + return r0 +} + // 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) @@ -805,32 +825,29 @@ func (_m *RPCClient) SimulateTransaction(ctx context.Context, tx *coretypes.Tran return r0 } -// Subscribe provides a mock function with given fields: ctx, channel, args -func (_m *RPCClient) Subscribe(ctx context.Context, channel chan<- *types.Head, args ...interface{}) (commontypes.Subscription, error) { - var _ca []interface{} - _ca = append(_ca, ctx, channel) - _ca = append(_ca, args...) - ret := _m.Called(_ca...) +// 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 Subscribe") + panic("no return value specified for SubscribeFilterLogs") } - var r0 commontypes.Subscription + var r0 ethereum.Subscription var r1 error - if rf, ok := ret.Get(0).(func(context.Context, chan<- *types.Head, ...interface{}) (commontypes.Subscription, error)); ok { - return rf(ctx, channel, args...) + 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, chan<- *types.Head, ...interface{}) commontypes.Subscription); ok { - r0 = rf(ctx, channel, args...) + 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).(commontypes.Subscription) + r0 = ret.Get(0).(ethereum.Subscription) } } - if rf, ok := ret.Get(1).(func(context.Context, chan<- *types.Head, ...interface{}) error); ok { - r1 = rf(ctx, channel, args...) + 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) } @@ -838,29 +855,29 @@ func (_m *RPCClient) Subscribe(ctx context.Context, channel chan<- *types.Head, return r0, r1 } -// 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) +// 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 SubscribeFilterLogs") + panic("no return value specified for SubscribeNewHead") } - var r0 ethereum.Subscription + var r0 commontypes.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, chan<- *types.Head) (commontypes.Subscription, error)); ok { + return rf(ctx, channel) } - if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) ethereum.Subscription); ok { - r0 = rf(ctx, q, ch) + 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).(ethereum.Subscription) + r0 = ret.Get(0).(commontypes.Subscription) } } - if rf, ok := ret.Get(1).(func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) error); ok { - r1 = rf(ctx, q, ch) + if rf, ok := ret.Get(1).(func(context.Context, chan<- *types.Head) error); ok { + r1 = rf(ctx, channel) } else { r1 = ret.Error(1) } diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index 255b038037a..ae9db24e6a6 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -56,6 +56,7 @@ type RPCClient interface { 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() commonclient.RPCChainInfo } type rpcClient struct { @@ -81,6 +82,9 @@ type rpcClient struct { // 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{} + + // intercepted values seen by callers of the rpcClient. Need to ensure MultiNode provides repeatable read guarantee + chainInfo commonclient.RPCChainInfo } // NewRPCCLient returns a new *rpcClient as commonclient.RPC @@ -111,6 +115,7 @@ func NewRPCClient( "evmChainID", chainID, ) r.rpcLog = logger.Sugared(lggr).Named("RPC") + r.chainInfo.TotalDifficulty = big.NewInt(0) return r } @@ -180,6 +185,9 @@ func (r *rpcClient) Close() { r.stateMu.Lock() defer r.stateMu.Unlock() r.cancelInflightRequests() + r.unsubscribeAll() + r.chainInfo.MostRecentlyFinalizedBlockNum = 0 + r.chainInfo.MostRecentBlockNumber = 0 } // cancelInflightRequests closes and replaces the chStopInFlight @@ -244,17 +252,6 @@ func (r *rpcClient) registerSub(sub ethereum.Subscription) { r.subs = append(r.subs, sub) } -// disconnectAll disconnects all clients connected to the rpcClient -// WARNING: NOT THREAD-SAFE -// This must be called from within the r.stateMu lock -func (r *rpcClient) DisconnectAll() { - if r.ws.rpc != nil { - r.ws.rpc.Close() - } - r.cancelInflightRequests() - r.unsubscribeAll() -} - // unsubscribeAll unsubscribes all subscriptions // WARNING: NOT THREAD-SAFE // This must be called from within the r.stateMu lock @@ -341,25 +338,32 @@ func (r *rpcClient) BatchCallContext(ctx context.Context, b []rpc.BatchElem) err return err } -func (r *rpcClient) Subscribe(ctx context.Context, channel chan<- *evmtypes.Head, args ...interface{}) (commontypes.Subscription, error) { +func (r *rpcClient) SubscribeNewHead(ctx context.Context, channel chan<- *evmtypes.Head) (commontypes.Subscription, error) { ctx, cancel, ws, _, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) if err != nil { return nil, err } defer cancel() + args := []interface{}{"newHeads"} lggr := r.newRqLggr().With("args", args) lggr.Debug("RPC call: evmclient.Client#EthSubscribe") start := time.Now() - sub, err := ws.rpc.EthSubscribe(ctx, channel, args...) + requestCh := r.getChStopInflight() + subForwarder := newSubForwarder(channel, func(head *evmtypes.Head) *evmtypes.Head { + head.EVMChainID = ubig.New(r.chainID) + r.oneNewHead(head, requestCh) + return head + }, nil) + err = subForwarder.start(ws.rpc.EthSubscribe(ctx, subForwarder.srcCh, args...)) if err == nil { - r.registerSub(sub) + r.registerSub(subForwarder) } duration := time.Since(start) r.logResult(lggr, err, duration, r.getRPCDomain(), "EthSubscribe") - return sub, err + return subForwarder, err } // GethClient wrappers @@ -480,7 +484,14 @@ func (r *rpcClient) HeaderByHash(ctx context.Context, hash common.Hash) (header } func (r *rpcClient) LatestFinalizedBlock(ctx context.Context) (head *evmtypes.Head, err error) { - return r.blockByNumber(ctx, rpc.FinalizedBlockNumber.String()) + requestCh := r.getChStopInflight() + block, err := r.blockByNumber(ctx, rpc.FinalizedBlockNumber.String()) + if err != nil { + return nil, err + } + + r.oneNewFinalizedHead(block, requestCh) + return block, nil } func (r *rpcClient) BlockByNumber(ctx context.Context, number *big.Int) (head *evmtypes.Head, err error) { @@ -1113,3 +1124,61 @@ func (r *rpcClient) Name() string { func Name(r *rpcClient) string { return r.name } + +// skipChainInfoUpdate - returns true, if data, received within requestChan lifetime, no longer accurately represents +// RPC's view on chain. This check helps us avoid following race condition: +// 1. rpcClient instance received request data, but have not processed it yet +// 2. RPC goes down and Node transitions to an unhealthy state +// 3. rpcClient processes data received on step 1 +// 4. New RPC starts with outdated state +// 5. rpcClient becomes alive, but chainInfo represents state of a different RPC instance +func skipChainInfoUpdate(requestChan chan struct{}) bool { + select { + case <-requestChan: + // channel is closed + return true + default: + return false + } +} + +func (r *rpcClient) oneNewHead(head *evmtypes.Head, requestChan chan struct{}) { + if head == nil { + return + } + + r.stateMu.Lock() + defer r.stateMu.Unlock() + if !skipChainInfoUpdate(requestChan) { + r.chainInfo.MostRecentBlockNumber = head.Number + } + r.chainInfo.HighestBlockNumber = max(r.chainInfo.HighestBlockNumber, head.Number) + if head.TotalDifficulty != nil { + r.chainInfo.TotalDifficulty = big.NewInt(0).Set(head.TotalDifficulty) + } +} + +func (r *rpcClient) oneNewFinalizedHead(head *evmtypes.Head, requestChan chan struct{}) { + if head == nil { + return + } + r.stateMu.Lock() + defer r.stateMu.Unlock() + if !skipChainInfoUpdate(requestChan) { + r.chainInfo.MostRecentlyFinalizedBlockNum = head.Number + } + r.chainInfo.HighestFinalizedBlockNum = max(r.chainInfo.HighestFinalizedBlockNum, head.Number) +} + +func (r *rpcClient) GetInterceptedChainInfo() commonclient.RPCChainInfo { + r.stateMu.RLock() + result := commonclient.RPCChainInfo{ + MostRecentBlockNumber: r.chainInfo.MostRecentBlockNumber, + HighestBlockNumber: r.chainInfo.HighestBlockNumber, + MostRecentlyFinalizedBlockNum: r.chainInfo.MostRecentlyFinalizedBlockNum, + HighestFinalizedBlockNum: r.chainInfo.HighestFinalizedBlockNum, + TotalDifficulty: big.NewInt(0).Set(r.chainInfo.TotalDifficulty), + } + r.stateMu.RUnlock() + return result +} diff --git a/core/chains/evm/client/rpc_client_test.go b/core/chains/evm/client/rpc_client_test.go new file mode 100644 index 00000000000..3932d115053 --- /dev/null +++ b/core/chains/evm/client/rpc_client_test.go @@ -0,0 +1,175 @@ +package client_test + +import ( + "context" + "encoding/json" + "fmt" + "math/big" + "net/url" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tidwall/gjson" + + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + + commonclient "github.com/smartcontractkit/chainlink/v2/common/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" +) + +func TestRPCClient_SubscribeNewHead(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithTimeout(testutils.Context(t), testutils.WaitTimeout(t)) + defer cancel() + + chainId := big.NewInt(123456) + lggr := logger.Test(t) + + type rpcServer struct { + Head *evmtypes.Head + URL *url.URL + } + createRPCServer := func() *rpcServer { + server := &rpcServer{} + server.URL = testutils.NewWSServer(t, chainId, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + if method == "eth_unsubscribe" { + resp.Result = "true" + return + } + assert.Equal(t, "eth_subscribe", method) + if assert.True(t, params.IsArray()) && assert.Equal(t, "newHeads", params.Array()[0].String()) { + resp.Result = `"0x00"` + head := server.Head + jsonHead, err := json.Marshal(head) + if err != nil { + panic(fmt.Errorf("failed to marshal head: %w", err)) + } + resp.Notify = string(jsonHead) + } + return + }).WSURL() + + return server + } + receiveNewHead := func(rpc client.RPCClient) *evmtypes.Head { + ch := make(chan *evmtypes.Head) + sub, err := rpc.SubscribeNewHead(tests.Context(t), ch) + require.NoError(t, err) + result := <-ch + sub.Unsubscribe() + return result + } + t.Run("Updates latest block info in InterceptedChainInfo", func(t *testing.T) { + server := createRPCServer() + rpc := client.NewRPCClient(lggr, *server.URL, nil, "rpc", 1, chainId, commonclient.Primary) + require.NoError(t, rpc.Dial(ctx)) + require.Equal(t, commonclient.RPCChainInfo{TotalDifficulty: big.NewInt(0)}, rpc.GetInterceptedChainInfo()) + server.Head = &evmtypes.Head{ + Number: 256, + TotalDifficulty: big.NewInt(1000), + } + _ = receiveNewHead(rpc) + server.Head = &evmtypes.Head{ + Number: 128, + TotalDifficulty: big.NewInt(1000), + } + _ = receiveNewHead(rpc) + chainInfo := rpc.GetInterceptedChainInfo() + assert.Equal(t, big.NewInt(1000), chainInfo.TotalDifficulty) + assert.Equal(t, int64(256), chainInfo.HighestBlockNumber) + assert.Equal(t, int64(128), chainInfo.MostRecentBlockNumber) + }) + t.Run("Block's chain ID matched configured", func(t *testing.T) { + server := createRPCServer() + rpc := client.NewRPCClient(lggr, *server.URL, nil, "rpc", 1, chainId, commonclient.Primary) + require.NoError(t, rpc.Dial(ctx)) + server.Head = &evmtypes.Head{ + Number: 256, + } + head := receiveNewHead(rpc) + require.Equal(t, chainId, head.ChainID()) + }) + t.Run("Close resets MostRecentBlockNumber", func(t *testing.T) { + server := createRPCServer() + rpc := client.NewRPCClient(lggr, *server.URL, nil, "rpc", 1, chainId, commonclient.Primary) + require.NoError(t, rpc.Dial(ctx)) + require.Equal(t, commonclient.RPCChainInfo{TotalDifficulty: big.NewInt(0)}, rpc.GetInterceptedChainInfo()) + // 1. received first head + server.Head = &evmtypes.Head{ + Number: 256, + } + _ = receiveNewHead(rpc) + chainInfo := rpc.GetInterceptedChainInfo() + assert.Equal(t, int64(256), chainInfo.HighestBlockNumber) + assert.Equal(t, int64(256), chainInfo.MostRecentBlockNumber) + rpc.Close() + chainInfo = rpc.GetInterceptedChainInfo() + // Highest remains as is to ensure we keep track of data observed by the callers + assert.Equal(t, int64(256), chainInfo.HighestBlockNumber) + // MostRecent was reset to correctly represent current state of the RPC + assert.Equal(t, int64(0), chainInfo.MostRecentBlockNumber) + + }) +} + +func TestRPCClient_LatestFinalizedBlock(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithTimeout(testutils.Context(t), testutils.WaitTimeout(t)) + defer cancel() + + chainId := big.NewInt(123456) + lggr := logger.Test(t) + + type rpcServer struct { + Head *evmtypes.Head + URL *url.URL + } + createRPCServer := func() *rpcServer { + server := &rpcServer{} + 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, "finalized", 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 := createRPCServer() + rpc := client.NewRPCClient(lggr, *server.URL, nil, "rpc", 1, chainId, commonclient.Primary) + require.NoError(t, rpc.Dial(ctx)) + server.Head = &evmtypes.Head{Number: 128} + // updates chain info + _, err := rpc.LatestFinalizedBlock(ctx) + require.NoError(t, err) + chainInfo := rpc.GetInterceptedChainInfo() + require.Equal(t, int64(128), chainInfo.HighestFinalizedBlockNum) + require.Equal(t, int64(128), chainInfo.MostRecentlyFinalizedBlockNum) + + // lower block number does not update Highest + server.Head = &evmtypes.Head{Number: 127} + _, err = rpc.LatestFinalizedBlock(ctx) + require.NoError(t, err) + chainInfo = rpc.GetInterceptedChainInfo() + require.Equal(t, int64(128), chainInfo.HighestFinalizedBlockNum) + require.Equal(t, int64(127), chainInfo.MostRecentlyFinalizedBlockNum) + + // Close resents chain info + rpc.Close() + chainInfo = rpc.GetInterceptedChainInfo() + require.Equal(t, int64(128), chainInfo.HighestFinalizedBlockNum) + require.Equal(t, int64(0), chainInfo.MostRecentlyFinalizedBlockNum) + +} diff --git a/core/chains/evm/client/chain_id_sub.go b/core/chains/evm/client/sub_forwarder.go similarity index 52% rename from core/chains/evm/client/chain_id_sub.go rename to core/chains/evm/client/sub_forwarder.go index c3162b300c7..7014ef29b50 100644 --- a/core/chains/evm/client/chain_id_sub.go +++ b/core/chains/evm/client/sub_forwarder.go @@ -9,34 +9,44 @@ import ( ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) -var _ ethereum.Subscription = &chainIDSubForwarder{} +var _ ethereum.Subscription = &subForwarder[any]{} -// chainIDSubForwarder wraps a head subscription in order to intercept and augment each head with chainID before forwarding. -type chainIDSubForwarder struct { - chainID *big.Int - destCh chan<- *evmtypes.Head +func newChainIDSubForwarder(chainID *big.Int, ch chan<- *evmtypes.Head) *subForwarder[*evmtypes.Head] { + return newSubForwarder(ch, func(head *evmtypes.Head) *evmtypes.Head { + head.EVMChainID = ubig.New(chainID) + return head + }, nil) +} + +// subForwarder wraps a subscription in order to intercept and augment each result before forwarding. +type subForwarder[T any] struct { + destCh chan<- T - srcCh chan *evmtypes.Head + srcCh chan T srcSub ethereum.Subscription + interceptResult func(T) T + interceptError func(error) error + done chan struct{} err chan error unSub chan struct{} } -func newChainIDSubForwarder(chainID *big.Int, ch chan<- *evmtypes.Head) *chainIDSubForwarder { - return &chainIDSubForwarder{ - chainID: chainID, - destCh: ch, - srcCh: make(chan *evmtypes.Head), - done: make(chan struct{}), - err: make(chan error), - unSub: make(chan struct{}, 1), +func newSubForwarder[T any](ch chan<- T, interceptResult func(T) T, interceptError func(error) error) *subForwarder[T] { + return &subForwarder[T]{ + interceptResult: interceptResult, + interceptError: interceptError, + destCh: ch, + srcCh: make(chan T), + done: make(chan struct{}), + err: make(chan error), + unSub: make(chan struct{}, 1), } } // start spawns the forwarding loop for sub. -func (c *chainIDSubForwarder) start(sub ethereum.Subscription, err error) error { +func (c *subForwarder[T]) start(sub ethereum.Subscription, err error) error { if err != nil { close(c.srcCh) return err @@ -48,7 +58,7 @@ func (c *chainIDSubForwarder) start(sub ethereum.Subscription, err error) error // forwardLoop receives from src, adds the chainID, and then sends to dest. // It also handles Unsubscribing, which may interrupt either forwarding operation. -func (c *chainIDSubForwarder) forwardLoop() { +func (c *subForwarder[T]) forwardLoop() { // the error channel must be closed when unsubscribing defer close(c.err) defer close(c.done) @@ -56,6 +66,9 @@ func (c *chainIDSubForwarder) forwardLoop() { for { select { case err := <-c.srcSub.Err(): + if c.interceptError != nil { + err = c.interceptError(err) + } select { case c.err <- err: case <-c.unSub: @@ -64,7 +77,9 @@ func (c *chainIDSubForwarder) forwardLoop() { return case h := <-c.srcCh: - h.EVMChainID = ubig.New(c.chainID) + if c.interceptResult != nil { + h = c.interceptResult(h) + } select { case c.destCh <- h: case <-c.unSub: @@ -79,7 +94,7 @@ func (c *chainIDSubForwarder) forwardLoop() { } } -func (c *chainIDSubForwarder) Unsubscribe() { +func (c *subForwarder[T]) Unsubscribe() { // tell forwardLoop to unsubscribe select { case c.unSub <- struct{}{}: @@ -90,6 +105,6 @@ func (c *chainIDSubForwarder) Unsubscribe() { <-c.done } -func (c *chainIDSubForwarder) Err() <-chan error { +func (c *subForwarder[T]) Err() <-chan error { return c.err } diff --git a/core/chains/evm/client/chain_id_sub_test.go b/core/chains/evm/client/sub_forwarder_test.go similarity index 100% rename from core/chains/evm/client/chain_id_sub_test.go rename to core/chains/evm/client/sub_forwarder_test.go From 1bc46f8c443a22a43682e55ba65a1bc6f40726cf Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Tue, 30 Apr 2024 15:37:05 +0200 Subject: [PATCH 02/53] simplify interception of RPC's view on chain --- common/client/node.go | 12 ----- common/client/types.go | 1 + core/chains/evm/client/mocks/rpc_client.go | 27 +++++----- core/chains/evm/client/rpc_client.go | 62 +++++----------------- core/chains/evm/client/rpc_client_test.go | 50 +++++------------ 5 files changed, 42 insertions(+), 110 deletions(-) diff --git a/common/client/node.go b/common/client/node.go index 1c47e2c07b8..9aafb579f7c 100644 --- a/common/client/node.go +++ b/common/client/node.go @@ -57,18 +57,6 @@ type ChainConfig interface { ChainType() commonconfig.ChainType } -type RPCChainInfo struct { - // MostRecentBlockNumber - latest block number - MostRecentBlockNumber int64 - // HighestBlockNumber - maximum block number ever observed - HighestBlockNumber int64 - // MostRecentlyFinalizedBlockNum - latest finalized block number - MostRecentlyFinalizedBlockNum int64 - // HighestFinalizedBlockNum - maximum block number ever observed for finalized block - HighestFinalizedBlockNum int64 - TotalDifficulty *big.Int -} - //go:generate mockery --quiet --name Node --structname mockNode --filename "mock_node_test.go" --inpackage --case=underscore type Node[ CHAIN_ID types.ID, diff --git a/common/client/types.go b/common/client/types.go index 6634c48d191..5759fcdb733 100644 --- a/common/client/types.go +++ b/common/client/types.go @@ -73,6 +73,7 @@ type NodeClient[ UnsubscribeAllExceptAliveLoop() IsSyncing(ctx context.Context) (bool, error) LatestFinalizedBlock(ctx context.Context) (HEAD, error) + GetInterceptedChainInfo() (highestBlockNumber, highestLatestFinalizedBlock int64) } // clientAPI includes all the direct RPC methods required by the generalized common client to implement its own. diff --git a/core/chains/evm/client/mocks/rpc_client.go b/core/chains/evm/client/mocks/rpc_client.go index c0e569bc810..17f7717ee72 100644 --- a/core/chains/evm/client/mocks/rpc_client.go +++ b/core/chains/evm/client/mocks/rpc_client.go @@ -9,8 +9,6 @@ import ( 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" @@ -381,11 +379,6 @@ func (_m *RPCClient) DialHTTP() error { return r0 } -// DisconnectAll provides a mock function with given fields: -func (_m *RPCClient) DisconnectAll() { - _m.Called() -} - // 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) @@ -445,21 +438,31 @@ func (_m *RPCClient) FilterEvents(ctx context.Context, query ethereum.FilterQuer } // GetInterceptedChainInfo provides a mock function with given fields: -func (_m *RPCClient) GetInterceptedChainInfo() commonclient.RPCChainInfo { +func (_m *RPCClient) GetInterceptedChainInfo() (int64, int64) { ret := _m.Called() if len(ret) == 0 { panic("no return value specified for GetInterceptedChainInfo") } - var r0 commonclient.RPCChainInfo - if rf, ok := ret.Get(0).(func() commonclient.RPCChainInfo); ok { + var r0 int64 + var r1 int64 + if rf, ok := ret.Get(0).(func() (int64, int64)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() int64); ok { r0 = rf() } else { - r0 = ret.Get(0).(commonclient.RPCChainInfo) + r0 = ret.Get(0).(int64) } - return r0 + if rf, ok := ret.Get(1).(func() int64); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(int64) + } + + return r0, r1 } // HeaderByHash provides a mock function with given fields: ctx, h diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index ae9db24e6a6..e45e8aa4fdf 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -56,7 +56,7 @@ type RPCClient interface { 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() commonclient.RPCChainInfo + GetInterceptedChainInfo() (highestBlockNumber, highestLatestFinalizedBlock int64) } type rpcClient struct { @@ -84,7 +84,10 @@ type rpcClient struct { chStopInFlight chan struct{} // intercepted values seen by callers of the rpcClient. Need to ensure MultiNode provides repeatable read guarantee - chainInfo commonclient.RPCChainInfo + // highestBlockNumber - maximum block number ever observed + highestBlockNumber int64 + // highestFinalizedBlockNum - maximum block number ever observed for finalized block + highestFinalizedBlockNum int64 } // NewRPCCLient returns a new *rpcClient as commonclient.RPC @@ -115,7 +118,6 @@ func NewRPCClient( "evmChainID", chainID, ) r.rpcLog = logger.Sugared(lggr).Named("RPC") - r.chainInfo.TotalDifficulty = big.NewInt(0) return r } @@ -186,8 +188,6 @@ func (r *rpcClient) Close() { defer r.stateMu.Unlock() r.cancelInflightRequests() r.unsubscribeAll() - r.chainInfo.MostRecentlyFinalizedBlockNum = 0 - r.chainInfo.MostRecentBlockNumber = 0 } // cancelInflightRequests closes and replaces the chStopInFlight @@ -349,10 +349,9 @@ func (r *rpcClient) SubscribeNewHead(ctx context.Context, channel chan<- *evmtyp lggr.Debug("RPC call: evmclient.Client#EthSubscribe") start := time.Now() - requestCh := r.getChStopInflight() subForwarder := newSubForwarder(channel, func(head *evmtypes.Head) *evmtypes.Head { head.EVMChainID = ubig.New(r.chainID) - r.oneNewHead(head, requestCh) + r.oneNewHead(head) return head }, nil) err = subForwarder.start(ws.rpc.EthSubscribe(ctx, subForwarder.srcCh, args...)) @@ -484,13 +483,12 @@ func (r *rpcClient) HeaderByHash(ctx context.Context, hash common.Hash) (header } func (r *rpcClient) LatestFinalizedBlock(ctx context.Context) (head *evmtypes.Head, err error) { - requestCh := r.getChStopInflight() block, err := r.blockByNumber(ctx, rpc.FinalizedBlockNumber.String()) if err != nil { return nil, err } - r.oneNewFinalizedHead(block, requestCh) + r.oneNewFinalizedHead(block) return block, nil } @@ -1125,60 +1123,28 @@ func Name(r *rpcClient) string { return r.name } -// skipChainInfoUpdate - returns true, if data, received within requestChan lifetime, no longer accurately represents -// RPC's view on chain. This check helps us avoid following race condition: -// 1. rpcClient instance received request data, but have not processed it yet -// 2. RPC goes down and Node transitions to an unhealthy state -// 3. rpcClient processes data received on step 1 -// 4. New RPC starts with outdated state -// 5. rpcClient becomes alive, but chainInfo represents state of a different RPC instance -func skipChainInfoUpdate(requestChan chan struct{}) bool { - select { - case <-requestChan: - // channel is closed - return true - default: - return false - } -} - -func (r *rpcClient) oneNewHead(head *evmtypes.Head, requestChan chan struct{}) { +func (r *rpcClient) oneNewHead(head *evmtypes.Head) { if head == nil { return } r.stateMu.Lock() defer r.stateMu.Unlock() - if !skipChainInfoUpdate(requestChan) { - r.chainInfo.MostRecentBlockNumber = head.Number - } - r.chainInfo.HighestBlockNumber = max(r.chainInfo.HighestBlockNumber, head.Number) - if head.TotalDifficulty != nil { - r.chainInfo.TotalDifficulty = big.NewInt(0).Set(head.TotalDifficulty) - } + r.highestBlockNumber = max(r.highestBlockNumber, head.Number) } -func (r *rpcClient) oneNewFinalizedHead(head *evmtypes.Head, requestChan chan struct{}) { +func (r *rpcClient) oneNewFinalizedHead(head *evmtypes.Head) { if head == nil { return } r.stateMu.Lock() defer r.stateMu.Unlock() - if !skipChainInfoUpdate(requestChan) { - r.chainInfo.MostRecentlyFinalizedBlockNum = head.Number - } - r.chainInfo.HighestFinalizedBlockNum = max(r.chainInfo.HighestFinalizedBlockNum, head.Number) + r.highestFinalizedBlockNum = max(r.highestFinalizedBlockNum, head.Number) } -func (r *rpcClient) GetInterceptedChainInfo() commonclient.RPCChainInfo { +func (r *rpcClient) GetInterceptedChainInfo() (highestBlockNumber, highestLatestFinalizedBlock int64) { r.stateMu.RLock() - result := commonclient.RPCChainInfo{ - MostRecentBlockNumber: r.chainInfo.MostRecentBlockNumber, - HighestBlockNumber: r.chainInfo.HighestBlockNumber, - MostRecentlyFinalizedBlockNum: r.chainInfo.MostRecentlyFinalizedBlockNum, - HighestFinalizedBlockNum: r.chainInfo.HighestFinalizedBlockNum, - TotalDifficulty: big.NewInt(0).Set(r.chainInfo.TotalDifficulty), - } + head, finalized := r.highestBlockNumber, r.highestFinalizedBlockNum r.stateMu.RUnlock() - return result + return head, finalized } diff --git a/core/chains/evm/client/rpc_client_test.go b/core/chains/evm/client/rpc_client_test.go index 3932d115053..80f96013cf6 100644 --- a/core/chains/evm/client/rpc_client_test.go +++ b/core/chains/evm/client/rpc_client_test.go @@ -68,7 +68,9 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { server := createRPCServer() rpc := client.NewRPCClient(lggr, *server.URL, nil, "rpc", 1, chainId, commonclient.Primary) require.NoError(t, rpc.Dial(ctx)) - require.Equal(t, commonclient.RPCChainInfo{TotalDifficulty: big.NewInt(0)}, rpc.GetInterceptedChainInfo()) + maxBlockNumber, maxFinalizedBlockNumber := rpc.GetInterceptedChainInfo() + require.Equal(t, int64(0), maxBlockNumber) + require.Equal(t, int64(0), maxFinalizedBlockNumber) server.Head = &evmtypes.Head{ Number: 256, TotalDifficulty: big.NewInt(1000), @@ -79,10 +81,9 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { TotalDifficulty: big.NewInt(1000), } _ = receiveNewHead(rpc) - chainInfo := rpc.GetInterceptedChainInfo() - assert.Equal(t, big.NewInt(1000), chainInfo.TotalDifficulty) - assert.Equal(t, int64(256), chainInfo.HighestBlockNumber) - assert.Equal(t, int64(128), chainInfo.MostRecentBlockNumber) + maxBlockNumber, maxFinalizedBlockNumber = rpc.GetInterceptedChainInfo() + assert.Equal(t, int64(256), maxBlockNumber) + assert.Equal(t, int64(0), maxFinalizedBlockNumber) }) t.Run("Block's chain ID matched configured", func(t *testing.T) { server := createRPCServer() @@ -94,27 +95,6 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { head := receiveNewHead(rpc) require.Equal(t, chainId, head.ChainID()) }) - t.Run("Close resets MostRecentBlockNumber", func(t *testing.T) { - server := createRPCServer() - rpc := client.NewRPCClient(lggr, *server.URL, nil, "rpc", 1, chainId, commonclient.Primary) - require.NoError(t, rpc.Dial(ctx)) - require.Equal(t, commonclient.RPCChainInfo{TotalDifficulty: big.NewInt(0)}, rpc.GetInterceptedChainInfo()) - // 1. received first head - server.Head = &evmtypes.Head{ - Number: 256, - } - _ = receiveNewHead(rpc) - chainInfo := rpc.GetInterceptedChainInfo() - assert.Equal(t, int64(256), chainInfo.HighestBlockNumber) - assert.Equal(t, int64(256), chainInfo.MostRecentBlockNumber) - rpc.Close() - chainInfo = rpc.GetInterceptedChainInfo() - // Highest remains as is to ensure we keep track of data observed by the callers - assert.Equal(t, int64(256), chainInfo.HighestBlockNumber) - // MostRecent was reset to correctly represent current state of the RPC - assert.Equal(t, int64(0), chainInfo.MostRecentBlockNumber) - - }) } func TestRPCClient_LatestFinalizedBlock(t *testing.T) { @@ -154,22 +134,16 @@ func TestRPCClient_LatestFinalizedBlock(t *testing.T) { // updates chain info _, err := rpc.LatestFinalizedBlock(ctx) require.NoError(t, err) - chainInfo := rpc.GetInterceptedChainInfo() - require.Equal(t, int64(128), chainInfo.HighestFinalizedBlockNum) - require.Equal(t, int64(128), chainInfo.MostRecentlyFinalizedBlockNum) + maxBlockNumber, maxFinalizedBlockNumber := rpc.GetInterceptedChainInfo() + assert.Equal(t, int64(0), maxBlockNumber) + assert.Equal(t, int64(128), maxFinalizedBlockNumber) // lower block number does not update Highest server.Head = &evmtypes.Head{Number: 127} _, err = rpc.LatestFinalizedBlock(ctx) require.NoError(t, err) - chainInfo = rpc.GetInterceptedChainInfo() - require.Equal(t, int64(128), chainInfo.HighestFinalizedBlockNum) - require.Equal(t, int64(127), chainInfo.MostRecentlyFinalizedBlockNum) - - // Close resents chain info - rpc.Close() - chainInfo = rpc.GetInterceptedChainInfo() - require.Equal(t, int64(128), chainInfo.HighestFinalizedBlockNum) - require.Equal(t, int64(0), chainInfo.MostRecentlyFinalizedBlockNum) + maxBlockNumber, maxFinalizedBlockNumber = rpc.GetInterceptedChainInfo() + assert.Equal(t, int64(0), maxBlockNumber) + assert.Equal(t, int64(128), maxFinalizedBlockNumber) } From 9d46a4107b3b28869ee72fab35fd68b8c56ea217 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Tue, 30 Apr 2024 15:48:13 +0200 Subject: [PATCH 03/53] Remove redundant changes --- common/client/mock_node_client_test.go | 33 ++++++++++++++++++++++ common/client/mock_rpc_test.go | 33 ++++++++++++++++++++++ common/client/node.go | 7 +++++ common/client/node_fsm.go | 8 +++--- common/client/node_fsm_test.go | 10 +++---- common/client/node_lifecycle_test.go | 30 ++++++++++---------- common/client/types.go | 1 + core/chains/evm/client/mocks/rpc_client.go | 5 ++++ core/chains/evm/client/rpc_client.go | 11 ++++++++ core/chains/evm/client/rpc_client_test.go | 3 ++ 10 files changed, 117 insertions(+), 24 deletions(-) diff --git a/common/client/mock_node_client_test.go b/common/client/mock_node_client_test.go index bba8554ae3c..824b4da11bb 100644 --- a/common/client/mock_node_client_test.go +++ b/common/client/mock_node_client_test.go @@ -111,6 +111,39 @@ func (_m *mockNodeClient[CHAIN_ID, HEAD]) DialHTTP() error { return r0 } +// DisconnectAll provides a mock function with given fields: +func (_m *mockNodeClient[CHAIN_ID, HEAD]) DisconnectAll() { + _m.Called() +} + +// GetInterceptedChainInfo provides a mock function with given fields: +func (_m *mockNodeClient[CHAIN_ID, HEAD]) GetInterceptedChainInfo() (int64, int64) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetInterceptedChainInfo") + } + + var r0 int64 + var r1 int64 + if rf, ok := ret.Get(0).(func() (int64, int64)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() int64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int64) + } + + if rf, ok := ret.Get(1).(func() int64); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(int64) + } + + return r0, r1 +} + // 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) diff --git a/common/client/mock_rpc_test.go b/common/client/mock_rpc_test.go index 156146634de..1840b391ab5 100644 --- a/common/client/mock_rpc_test.go +++ b/common/client/mock_rpc_test.go @@ -303,6 +303,11 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS return r0 } +// 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() +} + // 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) @@ -361,6 +366,34 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS return r0, r1 } +// 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() (int64, int64) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetInterceptedChainInfo") + } + + var r0 int64 + var r1 int64 + if rf, ok := ret.Get(0).(func() (int64, int64)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() int64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int64) + } + + if rf, ok := ret.Get(1).(func() int64); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(int64) + } + + return r0, r1 +} + // 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) diff --git a/common/client/node.go b/common/client/node.go index 9aafb579f7c..6450b086f10 100644 --- a/common/client/node.go +++ b/common/client/node.go @@ -321,6 +321,13 @@ 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 0e4cc6cbfa7..e9105dcc060 100644 --- a/common/client/node_fsm.go +++ b/common/client/node_fsm.go @@ -209,7 +209,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) transitionToOutOfSync(fn func()) { } switch n.state { case nodeStateAlive: - n.rpc.Close() + n.disconnectAll() n.state = nodeStateOutOfSync default: panic(transitionFail(n.state, nodeStateOutOfSync)) @@ -234,7 +234,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) transitionToUnreachable(fn func()) { } switch n.state { case nodeStateUndialed, nodeStateDialed, nodeStateAlive, nodeStateOutOfSync, nodeStateInvalidChainID, nodeStateSyncing: - n.rpc.Close() + n.disconnectAll() n.state = nodeStateUnreachable default: panic(transitionFail(n.state, nodeStateUnreachable)) @@ -277,7 +277,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) transitionToInvalidChainID(fn func()) { } switch n.state { case nodeStateDialed, nodeStateOutOfSync, nodeStateSyncing: - n.rpc.Close() + n.disconnectAll() n.state = nodeStateInvalidChainID default: panic(transitionFail(n.state, nodeStateInvalidChainID)) @@ -302,7 +302,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) transitionToSyncing(fn func()) { } switch n.state { case nodeStateDialed, nodeStateOutOfSync, nodeStateInvalidChainID: - n.rpc.Close() + n.disconnectAll() 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 f1ba37205e1..36cee65e09e 100644 --- a/common/client/node_fsm_test.go +++ b/common/client/node_fsm_test.go @@ -53,33 +53,33 @@ func TestUnit_Node_StateTransitions(t *testing.T) { const destinationState = nodeStateOutOfSync allowedStates := []nodeState{nodeStateAlive} rpc := newMockNodeClient[types.ID, Head](t) - rpc.On("Close").Once() + rpc.On("DisconnectAll").Once() 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("Close").Times(len(allowedStates)) + rpc.On("DisconnectAll").Times(len(allowedStates)) 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("Close").Times(len(allowedStates)) + rpc.On("DisconnectAll").Times(len(allowedStates)) 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("Close").Times(len(allowedStates)) + rpc.On("DisconnectAll").Times(len(allowedStates)) 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("Close").Once() + rpc.On("DisconnectAll").Once() node := newTestNode(t, testNodeOpts{rpc: rpc}) node.setState(nodeStateDialed) fn := new(fnMock) diff --git a/common/client/node_lifecycle_test.go b/common/client/node_lifecycle_test.go index 2fdd14916dd..30312575541 100644 --- a/common/client/node_lifecycle_test.go +++ b/common/client/node_lifecycle_test.go @@ -50,8 +50,8 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() expectedError := errors.New("failed to subscribe to rpc") + rpc.On("DisconnectAll").Once() rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(nil, expectedError).Once() - rpc.On("Close").Once() // might be called in unreachable loop rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() @@ -79,7 +79,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(sub, nil).Once() rpc.On("SetAliveLoopSub", sub).Once() // disconnects all on transfer to unreachable - rpc.On("Close").Once() + rpc.On("DisconnectAll").Once() // might be called in unreachable loop rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() @@ -169,7 +169,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { pollError := errors.New("failed to get ClientVersion") rpc.On("ClientVersion", mock.Anything).Return("", pollError) // disconnects all on transfer to unreachable - rpc.On("Close").Once() + rpc.On("DisconnectAll").Once() // might be called in unreachable loop rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() @@ -226,7 +226,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { assert.Equal(t, nodeStateOutOfSync, node.State()) }).Once() // disconnects all on transfer to unreachable or outOfSync - rpc.On("Close").Maybe() + rpc.On("DisconnectAll").Maybe() // might be called in unreachable loop rpc.On("Dial", mock.Anything).Run(func(_ mock.Arguments) { require.Equal(t, nodeStateOutOfSync, node.State()) @@ -297,7 +297,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { assert.Equal(t, nodeStateOutOfSync, node.State()) }).Once() // disconnects all on transfer to unreachable or outOfSync - rpc.On("Close").Maybe() + rpc.On("DisconnectAll").Maybe() // might be called in unreachable loop rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() @@ -350,7 +350,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) defer func() { assert.NoError(t, node.close()) }() // disconnects all on transfer to unreachable or outOfSync - rpc.On("Close").Once() + rpc.On("DisconnectAll").Once() // might be called in unreachable loop rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() @@ -546,7 +546,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { node := newTestNode(t, opts) opts.rpc.On("Close").Return(nil).Once() // disconnects all on transfer to unreachable or outOfSync - opts.rpc.On("Close") + opts.rpc.On("DisconnectAll") node.setState(nodeStateAlive) return node } @@ -848,7 +848,7 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { node := newTestNode(t, opts) opts.rpc.On("Close").Return(nil).Once() // disconnects all on transfer to unreachable - opts.rpc.On("Close") + opts.rpc.On("DisconnectAll") node.setState(nodeStateAlive) return node @@ -1006,7 +1006,7 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { newDialedNode := func(t *testing.T, opts testNodeOpts) testNode { node := newTestNode(t, opts) opts.rpc.On("Close").Return(nil).Once() - opts.rpc.On("Close") + opts.rpc.On("DisconnectAll") node.setState(nodeStateDialed) return node @@ -1149,7 +1149,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")) // disconnects all on transfer to unreachable - rpc.On("Close") + rpc.On("DisconnectAll") err := node.Start(tests.Context(t)) assert.NoError(t, err) tests.AssertLogEventually(t, observedLogs, "Dial failed: Node is unreachable") @@ -1174,7 +1174,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { assert.Equal(t, nodeStateDialed, node.State()) }).Return(nodeChainID, errors.New("failed to get chain id")) // disconnects all on transfer to unreachable - rpc.On("Close") + rpc.On("DisconnectAll") err := node.Start(tests.Context(t)) assert.NoError(t, err) tests.AssertLogEventually(t, observedLogs, "Failed to verify chain ID for node") @@ -1196,7 +1196,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { rpc.On("Dial", mock.Anything).Return(nil) rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil) // disconnects all on transfer to unreachable - rpc.On("Close") + rpc.On("DisconnectAll") err := node.Start(tests.Context(t)) assert.NoError(t, err) tests.AssertEventually(t, func() bool { @@ -1222,7 +1222,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { }).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("Close") + 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)) @@ -1247,7 +1247,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil) rpc.On("IsSyncing", mock.Anything).Return(true, nil) // disconnects all on transfer to unreachable - rpc.On("Close") + rpc.On("DisconnectAll") err := node.Start(tests.Context(t)) assert.NoError(t, err) tests.AssertEventually(t, func() bool { @@ -1442,7 +1442,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { opts.config.nodeIsSyncingEnabled = true node := newTestNode(t, opts) opts.rpc.On("Close").Return(nil).Once() - opts.rpc.On("Close") + opts.rpc.On("DisconnectAll") node.setState(nodeStateDialed) return node diff --git a/common/client/types.go b/common/client/types.go index 5759fcdb733..34a90259707 100644 --- a/common/client/types.go +++ b/common/client/types.go @@ -66,6 +66,7 @@ type NodeClient[ connection[CHAIN_ID, HEAD] DialHTTP() error + DisconnectAll() Close() ClientVersion(context.Context) (string, error) SubscribersCount() int32 diff --git a/core/chains/evm/client/mocks/rpc_client.go b/core/chains/evm/client/mocks/rpc_client.go index 17f7717ee72..42aa245a69a 100644 --- a/core/chains/evm/client/mocks/rpc_client.go +++ b/core/chains/evm/client/mocks/rpc_client.go @@ -379,6 +379,11 @@ func (_m *RPCClient) DialHTTP() error { return r0 } +// DisconnectAll provides a mock function with given fields: +func (_m *RPCClient) DisconnectAll() { + _m.Called() +} + // 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) diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index e45e8aa4fdf..05a065ca59d 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -252,6 +252,17 @@ func (r *rpcClient) registerSub(sub ethereum.Subscription) { r.subs = append(r.subs, sub) } +// disconnectAll disconnects all clients connected to the rpcClient +// WARNING: NOT THREAD-SAFE +// This must be called from within the r.stateMu lock +func (r *rpcClient) DisconnectAll() { + if r.ws.rpc != nil { + r.ws.rpc.Close() + } + r.cancelInflightRequests() + r.unsubscribeAll() +} + // unsubscribeAll unsubscribes all subscriptions // WARNING: NOT THREAD-SAFE // This must be called from within the r.stateMu lock diff --git a/core/chains/evm/client/rpc_client_test.go b/core/chains/evm/client/rpc_client_test.go index 80f96013cf6..795c015b27d 100644 --- a/core/chains/evm/client/rpc_client_test.go +++ b/core/chains/evm/client/rpc_client_test.go @@ -67,6 +67,7 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { t.Run("Updates latest block info in InterceptedChainInfo", func(t *testing.T) { server := createRPCServer() rpc := client.NewRPCClient(lggr, *server.URL, nil, "rpc", 1, chainId, commonclient.Primary) + defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) maxBlockNumber, maxFinalizedBlockNumber := rpc.GetInterceptedChainInfo() require.Equal(t, int64(0), maxBlockNumber) @@ -88,6 +89,7 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { t.Run("Block's chain ID matched configured", func(t *testing.T) { server := createRPCServer() rpc := client.NewRPCClient(lggr, *server.URL, nil, "rpc", 1, chainId, commonclient.Primary) + defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) server.Head = &evmtypes.Head{ Number: 256, @@ -130,6 +132,7 @@ func TestRPCClient_LatestFinalizedBlock(t *testing.T) { server := createRPCServer() rpc := client.NewRPCClient(lggr, *server.URL, nil, "rpc", 1, chainId, commonclient.Primary) require.NoError(t, rpc.Dial(ctx)) + defer rpc.Close() server.Head = &evmtypes.Head{Number: 128} // updates chain info _, err := rpc.LatestFinalizedBlock(ctx) From 8627c95bcf28286e66559d94be3ef6128d2054d4 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Tue, 30 Apr 2024 15:49:27 +0200 Subject: [PATCH 04/53] remove redundant changes --- core/chains/evm/client/rpc_client.go | 1 - 1 file changed, 1 deletion(-) diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index 05a065ca59d..3dce2a7c25a 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -187,7 +187,6 @@ func (r *rpcClient) Close() { r.stateMu.Lock() defer r.stateMu.Unlock() r.cancelInflightRequests() - r.unsubscribeAll() } // cancelInflightRequests closes and replaces the chStopInFlight From 4788f16d818a59bbec4ee2415f0483a8e5c42d9b Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Mon, 6 May 2024 19:50:34 +0200 Subject: [PATCH 05/53] do not pick RPC as active if it's lagging on finalized block --- common/client/mock_node_client_test.go | 20 +- common/client/mock_node_test.go | 52 +++-- .../mock_pool_chain_info_provider_test.go | 70 +++++++ common/client/mock_rpc_test.go | 20 +- common/client/mocks/config.go | 13 +- common/client/multi_node.go | 48 +++-- common/client/multi_node_test.go | 114 +++++++--- common/client/node.go | 48 ++--- common/client/node_fsm.go | 63 +++++- common/client/node_lifecycle.go | 148 +++++++------ common/client/node_lifecycle_test.go | 196 ++++++++++++++---- common/client/node_selector_highest_head.go | 3 +- .../client/node_selector_highest_head_test.go | 40 ++-- .../client/node_selector_total_difficulty.go | 3 +- .../node_selector_total_difficulty_test.go | 42 ++-- common/client/node_test.go | 5 + common/client/types.go | 30 ++- core/chains/evm/client/helpers_test.go | 5 + core/chains/evm/client/mocks/rpc_client.go | 22 +- core/chains/evm/client/rpc_client.go | 19 +- core/chains/evm/client/rpc_client_test.go | 24 +-- core/chains/evm/config/chain_scoped.go | 5 + .../evm/config/chain_scoped_node_pool.go | 5 + core/chains/evm/config/config.go | 2 + 24 files changed, 670 insertions(+), 327 deletions(-) create mode 100644 common/client/mock_pool_chain_info_provider_test.go diff --git a/common/client/mock_node_client_test.go b/common/client/mock_node_client_test.go index 824b4da11bb..6ecbaf25e7c 100644 --- a/common/client/mock_node_client_test.go +++ b/common/client/mock_node_client_test.go @@ -117,31 +117,21 @@ func (_m *mockNodeClient[CHAIN_ID, HEAD]) DisconnectAll() { } // GetInterceptedChainInfo provides a mock function with given fields: -func (_m *mockNodeClient[CHAIN_ID, HEAD]) GetInterceptedChainInfo() (int64, int64) { +func (_m *mockNodeClient[CHAIN_ID, HEAD]) GetInterceptedChainInfo() ChainInfo { ret := _m.Called() if len(ret) == 0 { panic("no return value specified for GetInterceptedChainInfo") } - var r0 int64 - var r1 int64 - if rf, ok := ret.Get(0).(func() (int64, int64)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() int64); ok { + var r0 ChainInfo + if rf, ok := ret.Get(0).(func() ChainInfo); ok { r0 = rf() } else { - r0 = ret.Get(0).(int64) - } - - if rf, ok := ret.Get(1).(func() int64); ok { - r1 = rf() - } else { - r1 = ret.Get(1).(int64) + r0 = ret.Get(0).(ChainInfo) } - return r0, r1 + return r0 } // IsSyncing provides a mock function with given fields: ctx diff --git a/common/client/mock_node_test.go b/common/client/mock_node_test.go index ee2cacb9274..5fc5b4833cb 100644 --- a/common/client/mock_node_test.go +++ b/common/client/mock_node_test.go @@ -4,11 +4,9 @@ package client import ( context "context" - big "math/big" - - mock "github.com/stretchr/testify/mock" types "github.com/smartcontractkit/chainlink/v2/common/types" + mock "github.com/stretchr/testify/mock" ) // mockNode is an autogenerated mock type for the Node type @@ -52,6 +50,24 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) ConfiguredChainID() CHAIN_ID { return r0 } +// HighestChainInfo provides a mock function with given fields: +func (_m *mockNode[CHAIN_ID, HEAD, RPC]) HighestChainInfo() ChainInfo { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for HighestChainInfo") + } + + var r0 ChainInfo + if rf, ok := ret.Get(0).(func() ChainInfo); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(ChainInfo) + } + + return r0 +} + // Name provides a mock function with given fields: func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Name() string { ret := _m.Called() @@ -106,6 +122,11 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) RPC() RPC { return r0 } +// SetPoolChainInfoProvider provides a mock function with given fields: _a0 +func (_m *mockNode[CHAIN_ID, HEAD, RPC]) SetPoolChainInfoProvider(_a0 PoolChainInfoProvider) { + _m.Called(_a0) +} + // Start provides a mock function with given fields: _a0 func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Start(_a0 context.Context) error { ret := _m.Called(_a0) @@ -142,18 +163,17 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) State() nodeState { return r0 } -// StateAndLatest provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, int64, *big.Int) { +// StateAndLatestChainInfo provides a mock function with given fields: +func (_m *mockNode[CHAIN_ID, HEAD, RPC]) StateAndLatestChainInfo() (nodeState, ChainInfo) { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for StateAndLatest") + panic("no return value specified for StateAndLatestChainInfo") } var r0 nodeState - var r1 int64 - var r2 *big.Int - if rf, ok := ret.Get(0).(func() (nodeState, int64, *big.Int)); ok { + var r1 ChainInfo + if rf, ok := ret.Get(0).(func() (nodeState, ChainInfo)); ok { return rf() } if rf, ok := ret.Get(0).(func() nodeState); ok { @@ -162,21 +182,13 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, int64, *bi r0 = ret.Get(0).(nodeState) } - if rf, ok := ret.Get(1).(func() int64); ok { + if rf, ok := ret.Get(1).(func() ChainInfo); ok { r1 = rf() } else { - r1 = ret.Get(1).(int64) - } - - if rf, ok := ret.Get(2).(func() *big.Int); ok { - r2 = rf() - } else { - if ret.Get(2) != nil { - r2 = ret.Get(2).(*big.Int) - } + r1 = ret.Get(1).(ChainInfo) } - return r0, r1, r2 + return r0, r1 } // String provides a mock function with given fields: diff --git a/common/client/mock_pool_chain_info_provider_test.go b/common/client/mock_pool_chain_info_provider_test.go new file mode 100644 index 00000000000..563641f701d --- /dev/null +++ b/common/client/mock_pool_chain_info_provider_test.go @@ -0,0 +1,70 @@ +// Code generated by mockery v2.42.2. DO NOT EDIT. + +package client + +import mock "github.com/stretchr/testify/mock" + +// mockPoolChainInfoProvider is an autogenerated mock type for the PoolChainInfoProvider type +type mockPoolChainInfoProvider struct { + mock.Mock +} + +// HighestChainInfo provides a mock function with given fields: +func (_m *mockPoolChainInfoProvider) HighestChainInfo() ChainInfo { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for HighestChainInfo") + } + + var r0 ChainInfo + if rf, ok := ret.Get(0).(func() ChainInfo); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(ChainInfo) + } + + return r0 +} + +// LatestChainInfo provides a mock function with given fields: +func (_m *mockPoolChainInfoProvider) LatestChainInfo() (int, ChainInfo) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for LatestChainInfo") + } + + var r0 int + var r1 ChainInfo + if rf, ok := ret.Get(0).(func() (int, ChainInfo)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() int); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int) + } + + if rf, ok := ret.Get(1).(func() ChainInfo); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(ChainInfo) + } + + return r0, r1 +} + +// newMockPoolChainInfoProvider creates a new instance of mockPoolChainInfoProvider. 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 newMockPoolChainInfoProvider(t interface { + mock.TestingT + Cleanup(func()) +}) *mockPoolChainInfoProvider { + mock := &mockPoolChainInfoProvider{} + 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 index 1840b391ab5..c16af11bcb9 100644 --- a/common/client/mock_rpc_test.go +++ b/common/client/mock_rpc_test.go @@ -367,31 +367,21 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // 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() (int64, int64) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) GetInterceptedChainInfo() ChainInfo { ret := _m.Called() if len(ret) == 0 { panic("no return value specified for GetInterceptedChainInfo") } - var r0 int64 - var r1 int64 - if rf, ok := ret.Get(0).(func() (int64, int64)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() int64); ok { + var r0 ChainInfo + if rf, ok := ret.Get(0).(func() ChainInfo); ok { r0 = rf() } else { - r0 = ret.Get(0).(int64) - } - - if rf, ok := ret.Get(1).(func() int64); ok { - r1 = rf() - } else { - r1 = ret.Get(1).(int64) + r0 = ret.Get(0).(ChainInfo) } - return r0, r1 + return r0 } // IsSyncing provides a mock function with given fields: ctx diff --git a/common/client/mocks/config.go b/common/client/mocks/config.go index 27b717f61a0..308fab55790 100644 --- a/common/client/mocks/config.go +++ b/common/client/mocks/config.go @@ -7,10 +7,11 @@ import ( ) type ChainConfig struct { - IsFinalityTagEnabled bool - FinalityDepthVal uint32 - NoNewHeadsThresholdVal time.Duration - ChainTypeVal commonconfig.ChainType + IsFinalityTagEnabled bool + FinalityDepthVal uint32 + NoNewHeadsThresholdVal time.Duration + ChainTypeVal commonconfig.ChainType + FinalizedBlockOffsetVal uint32 } func (t ChainConfig) ChainType() commonconfig.ChainType { @@ -28,3 +29,7 @@ func (t ChainConfig) FinalityDepth() uint32 { func (t ChainConfig) FinalityTagEnabled() bool { return t.IsFinalityTagEnabled } + +func (t ChainConfig) FinalizedBlockOffset() uint32 { + return t.FinalizedBlockOffsetVal +} diff --git a/common/client/multi_node.go b/common/client/multi_node.go index ce050b9c654..fe9e12e0617 100644 --- a/common/client/multi_node.go +++ b/common/client/multi_node.go @@ -185,14 +185,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP 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())) } - rawNode, ok := n.(*node[CHAIN_ID, HEAD, RPC_CLIENT]) - if ok { - // This is a bit hacky but it allows the node to be aware of - // pool state and prevent certain state transitions that might - // otherwise leave no nodes available. It is better to have one - // node in a degraded state than no nodes at all. - rawNode.nLiveNodes = c.nLiveNodes - } + n.SetPoolChainInfoProvider(c) // node will handle its own redialing and automatic recovery if err := ms.Start(ctx, n); err != nil { return err @@ -271,22 +264,37 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return c.activeNode, err } -// nLiveNodes returns the number of currently alive nodes, as well as the highest block number and greatest total difficulty. -// totalDifficulty will be 0 if all nodes return nil. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) nLiveNodes() (nLiveNodes int, blockNumber int64, totalDifficulty *big.Int) { - totalDifficulty = big.NewInt(0) +// 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) { + var nLiveNodes int + ch := ChainInfo{ + TotalDifficulty: big.NewInt(0), + } for _, n := range c.nodes { - if s, num, td := n.StateAndLatest(); s == nodeStateAlive { + if s, nodeChainInfo := n.StateAndLatestChainInfo(); s == nodeStateAlive { nLiveNodes++ - if num > blockNumber { - blockNumber = num - } - if td != nil && td.Cmp(totalDifficulty) > 0 { - totalDifficulty = td - } + ch.BlockNumber = max(ch.BlockNumber, nodeChainInfo.BlockNumber) + ch.FinalizedBlockNumber = max(ch.FinalizedBlockNumber, nodeChainInfo.FinalizedBlockNumber) + ch.SetTotalDifficultyIfGt(nodeChainInfo.TotalDifficulty) } } - return + return nLiveNodes, ch +} + +// HighestChainInfo - returns highest ChainInfo ever observed by any node in the pool. +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) HighestChainInfo() ChainInfo { + ch := ChainInfo{ + TotalDifficulty: big.NewInt(0), + } + for _, n := range c.nodes { + nodeChainInfo := n.HighestChainInfo() + ch.BlockNumber = max(ch.BlockNumber, nodeChainInfo.BlockNumber) + ch.FinalizedBlockNumber = max(ch.FinalizedBlockNumber, nodeChainInfo.FinalizedBlockNumber) + ch.SetTotalDifficultyIfGt(nodeChainInfo.TotalDifficulty) + } + 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() { diff --git a/common/client/multi_node_test.go b/common/client/multi_node_test.go index 9f6904fcaf2..9de0f414800 100644 --- a/common/client/multi_node_test.go +++ b/common/client/multi_node_test.go @@ -75,8 +75,10 @@ func newNodeWithState(t *testing.T, chainID types.ID, state nodeState) *mockNode node.On("Close").Return(nil).Once() node.On("State").Return(state).Maybe() node.On("String").Return(fmt.Sprintf("healthy_node_%d", rand.Int())).Maybe() + node.On("SetPoolChainInfoProvider", mock.Anything).Once() return node } + func TestMultiNode_Dial(t *testing.T) { t.Parallel() @@ -113,6 +115,7 @@ 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() mn := newTestMultiNode(t, multiNodeOpts{ @@ -130,6 +133,7 @@ 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() @@ -422,49 +426,94 @@ func TestMultiNode_selectNode(t *testing.T) { }) } -func TestMultiNode_nLiveNodes(t *testing.T) { +func TestMultiNode_ChainInfo(t *testing.T) { t.Parallel() type nodeParams struct { - BlockNumber int64 - TotalDifficulty *big.Int - State nodeState + LatestChainInfo ChainInfo + HighestChainInfo ChainInfo + State nodeState } testCases := []struct { - Name string - ExpectedNLiveNodes int - ExpectedBlockNumber int64 - ExpectedTotalDifficulty *big.Int - NodeParams []nodeParams + Name string + ExpectedNLiveNodes int + ExpectedLatestChainInfo ChainInfo + ExpectedHighestChainInfo ChainInfo + NodeParams []nodeParams }{ { - Name: "no nodes", - ExpectedTotalDifficulty: big.NewInt(0), + Name: "no nodes", + ExpectedLatestChainInfo: ChainInfo{ + TotalDifficulty: big.NewInt(0), + }, + ExpectedHighestChainInfo: ChainInfo{ + TotalDifficulty: big.NewInt(0), + }, }, { - Name: "Best node is not healthy", - ExpectedTotalDifficulty: big.NewInt(10), - ExpectedBlockNumber: 20, - ExpectedNLiveNodes: 3, + Name: "Best node is not healthy", + ExpectedNLiveNodes: 3, + ExpectedLatestChainInfo: ChainInfo{ + BlockNumber: 20, + FinalizedBlockNumber: 10, + TotalDifficulty: big.NewInt(10), + }, + ExpectedHighestChainInfo: ChainInfo{ + BlockNumber: 1005, + FinalizedBlockNumber: 995, + TotalDifficulty: big.NewInt(2005), + }, NodeParams: []nodeParams{ { - State: nodeStateOutOfSync, - BlockNumber: 1000, - TotalDifficulty: big.NewInt(2000), + State: nodeStateOutOfSync, + LatestChainInfo: ChainInfo{ + BlockNumber: 1000, + FinalizedBlockNumber: 990, + TotalDifficulty: big.NewInt(2000), + }, + HighestChainInfo: ChainInfo{ + BlockNumber: 1005, + FinalizedBlockNumber: 995, + TotalDifficulty: big.NewInt(2005), + }, }, { - State: nodeStateAlive, - BlockNumber: 20, - TotalDifficulty: big.NewInt(9), + State: nodeStateAlive, + LatestChainInfo: ChainInfo{ + BlockNumber: 20, + FinalizedBlockNumber: 10, + TotalDifficulty: big.NewInt(9), + }, + HighestChainInfo: ChainInfo{ + BlockNumber: 25, + FinalizedBlockNumber: 15, + TotalDifficulty: big.NewInt(14), + }, }, { - State: nodeStateAlive, - BlockNumber: 19, - TotalDifficulty: big.NewInt(10), + State: nodeStateAlive, + LatestChainInfo: ChainInfo{ + BlockNumber: 19, + FinalizedBlockNumber: 9, + TotalDifficulty: big.NewInt(10), + }, + HighestChainInfo: ChainInfo{ + BlockNumber: 24, + FinalizedBlockNumber: 14, + TotalDifficulty: big.NewInt(15), + }, }, { - State: nodeStateAlive, - BlockNumber: 11, - TotalDifficulty: nil, + State: nodeStateAlive, + LatestChainInfo: ChainInfo{ + BlockNumber: 11, + FinalizedBlockNumber: 1, + TotalDifficulty: nil, + }, + HighestChainInfo: ChainInfo{ + BlockNumber: 16, + FinalizedBlockNumber: 6, + TotalDifficulty: nil, + }, }, }, }, @@ -480,14 +529,17 @@ func TestMultiNode_nLiveNodes(t *testing.T) { t.Run(tc.Name, func(t *testing.T) { for _, params := range tc.NodeParams { node := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - node.On("StateAndLatest").Return(params.State, params.BlockNumber, params.TotalDifficulty) + node.On("StateAndLatestChainInfo").Return(params.State, params.LatestChainInfo) + node.On("HighestChainInfo").Return(params.HighestChainInfo) mn.nodes = append(mn.nodes, node) } - nNodes, blockNum, td := mn.nLiveNodes() + nNodes, latestChainInfo := mn.LatestChainInfo() assert.Equal(t, tc.ExpectedNLiveNodes, nNodes) - assert.Equal(t, tc.ExpectedTotalDifficulty, td) - assert.Equal(t, tc.ExpectedBlockNumber, blockNum) + assert.Equal(t, tc.ExpectedLatestChainInfo, latestChainInfo) + + highestChainInfo := mn.HighestChainInfo() + assert.Equal(t, tc.ExpectedHighestChainInfo, highestChainInfo) }) } } diff --git a/common/client/node.go b/common/client/node.go index 13bd32f11e4..7483158451c 100644 --- a/common/client/node.go +++ b/common/client/node.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "math/big" "net/url" "sync" "time" @@ -48,6 +47,7 @@ type NodeConfig interface { NodeIsSyncingEnabled() bool FinalizedBlockPollInterval() time.Duration Errors() config.ClientErrors + EnforceRepeatableRead() bool } type ChainConfig interface { @@ -55,18 +55,7 @@ type ChainConfig interface { FinalityDepth() uint32 FinalityTagEnabled() bool ChainType() commonconfig.ChainType -} - -type RPCChainInfo struct { - // MostRecentBlockNumber - latest block number - MostRecentBlockNumber int64 - // HighestBlockNumber - maximum block number ever observed - HighestBlockNumber int64 - // MostRecentlyFinalizedBlockNum - latest finalized block number - MostRecentlyFinalizedBlockNum int64 - // HighestFinalizedBlockNum - maximum block number ever observed for finalized block - HighestFinalizedBlockNum int64 - TotalDifficulty *big.Int + FinalizedBlockOffset() uint32 } //go:generate mockery --quiet --name Node --structname mockNode --filename "mock_node_test.go" --inpackage --case=underscore @@ -77,8 +66,11 @@ type Node[ ] interface { // State returns nodeState State() nodeState - // StateAndLatest returns nodeState with the latest received block number & total difficulty. - StateAndLatest() (nodeState, int64, *big.Int) + // StateAndLatestChainInfo returns nodeState with the latest ChainInfo observed by Node during current lifecycle. + StateAndLatestChainInfo() (nodeState, ChainInfo) + // HighestChainInfo - returns highest ChainInfo ever observed by the Node + HighestChainInfo() ChainInfo + SetPoolChainInfoProvider(PoolChainInfoProvider) // Name is a unique identifier for this node. Name() string String() string @@ -114,9 +106,9 @@ type node[ stateMu sync.RWMutex // protects state* fields state nodeState // Each node is tracking the last received head number and total difficulty - stateLatestBlockNumber int64 - stateLatestTotalDifficulty *big.Int - stateLatestFinalizedBlockNumber int64 + latestChainInfo ChainInfo + + poolInfoProvider PoolChainInfoProvider // nodeCtx is the node lifetime's context nodeCtx context.Context @@ -124,12 +116,6 @@ type node[ cancelNodeCtx context.CancelFunc // wg waits for subsidiary goroutines wg sync.WaitGroup - - // nLiveNodes is a passed in function that allows this node to: - // 1. see how many live nodes there are in total, so we can prevent the last alive node in a pool from being - // moved to out-of-sync state. It is better to have one out-of-sync node than no nodes at all. - // 2. compare against the highest head (by number or difficulty) to ensure we don't fall behind too far. - nLiveNodes func() (count int, blockNumber int64, totalDifficulty *big.Int) } func NewNode[ @@ -170,7 +156,7 @@ func NewNode[ "nodeOrder", n.order, ) n.lfcLog = logger.Named(lggr, "Lifecycle") - n.stateLatestBlockNumber = -1 + n.latestChainInfo.BlockNumber = -1 n.rpc = rpc n.chainFamily = chainFamily return n @@ -263,7 +249,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) verifyChainID(callerCtx context.Context, lgg promPoolRPCNodeVerifiesFailed.WithLabelValues(n.chainFamily, n.chainID.String(), n.name).Inc() } - st := n.State() + st := n.getCachedState() switch st { case nodeStateClosed: // The node is already closed, and any subsequent transition is invalid. @@ -278,7 +264,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) verifyChainID(callerCtx context.Context, lgg var err error if chainID, err = n.rpc.ChainID(callerCtx); err != nil { promFailed() - lggr.Errorw("Failed to verify chain ID for node", "err", err, "nodeState", n.State()) + lggr.Errorw("Failed to verify chain ID for node", "err", err, "nodeState", n.getCachedState()) return nodeStateUnreachable } else if chainID.String() != n.chainID.String() { promFailed() @@ -289,7 +275,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) verifyChainID(callerCtx context.Context, lgg n.name, errInvalidChainID, ) - lggr.Errorw("Failed to verify RPC node; remote endpoint returned the wrong chain ID", "err", err, "nodeState", n.State()) + lggr.Errorw("Failed to verify RPC node; remote endpoint returned the wrong chain ID", "err", err, "nodeState", n.getCachedState()) return nodeStateInvalidChainID } @@ -302,7 +288,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) verifyChainID(callerCtx context.Context, lgg // Returns desired state if one of the verifications fails. Otherwise, returns nodeStateAlive. func (n *node[CHAIN_ID, HEAD, RPC]) createVerifiedConn(ctx context.Context, lggr logger.Logger) nodeState { if err := n.rpc.Dial(ctx); err != nil { - n.lfcLog.Errorw("Dial failed: Node is unreachable", "err", err, "nodeState", n.State()) + n.lfcLog.Errorw("Dial failed: Node is unreachable", "err", err, "nodeState", n.getCachedState()) return nodeStateUnreachable } @@ -320,12 +306,12 @@ func (n *node[CHAIN_ID, HEAD, RPC]) verifyConn(ctx context.Context, lggr logger. if n.nodePoolCfg.NodeIsSyncingEnabled() { isSyncing, err := n.rpc.IsSyncing(ctx) if err != nil { - lggr.Errorw("Unexpected error while verifying RPC node synchronization status", "err", err, "nodeState", n.State()) + lggr.Errorw("Unexpected error while verifying RPC node synchronization status", "err", err, "nodeState", n.getCachedState()) return nodeStateUnreachable } if isSyncing { - lggr.Errorw("Verification failed: Node is syncing", "nodeState", n.State()) + lggr.Errorw("Verification failed: Node is syncing", "nodeState", n.getCachedState()) return nodeStateSyncing } } diff --git a/common/client/node_fsm.go b/common/client/node_fsm.go index e9105dcc060..95961ad7b81 100644 --- a/common/client/node_fsm.go +++ b/common/client/node_fsm.go @@ -63,6 +63,8 @@ func (n nodeState) String() string { return "Closed" case nodeStateSyncing: return "Syncing" + case nodeStateFinalizedBlockOutOfSync: + return "FinalizedBlockOutOfSync" default: return fmt.Sprintf("nodeState(%d)", n) } @@ -98,6 +100,8 @@ const ( // to other primary nodes configured in the MultiNode. In contrast, `nodeStateSyncing` represents the internal state of // the node (RPC). nodeStateSyncing + // nodeStateFinalizedBlockOutOfSync - node is lagging behind on latest finalized block + nodeStateFinalizedBlockOutOfSync // nodeStateLen tracks the number of states nodeStateLen ) @@ -115,15 +119,68 @@ func init() { // State allows reading the current state of the node. func (n *node[CHAIN_ID, HEAD, RPC]) State() nodeState { + n.stateMu.RLock() + defer n.stateMu.RUnlock() + return n.recalculateState() +} + +func (n *node[CHAIN_ID, HEAD, RPC]) getCachedState() nodeState { n.stateMu.RLock() defer n.stateMu.RUnlock() return n.state } -func (n *node[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, int64, *big.Int) { +func (n *node[CHAIN_ID, HEAD, RPC]) recalculateState() nodeState { + if n.state != nodeStateAlive || !n.nodePoolCfg.EnforceRepeatableRead() { + return n.state + } + + // double check that node is not lagging on finalized block + if n.isFinalizedStateOutOfSync() { + return nodeStateFinalizedBlockOutOfSync + } + + return nodeStateAlive +} + +func (n *node[CHAIN_ID, HEAD, RPC]) isFinalizedStateOutOfSync() bool { + if n.poolInfoProvider == nil { + return false + } + + highest := n.poolInfoProvider.HighestChainInfo() + if n.chainCfg.FinalityTagEnabled() { + return n.latestChainInfo.FinalizedBlockNumber < highest.FinalizedBlockNumber-int64(n.chainCfg.FinalizedBlockOffset()) + } + + return n.latestChainInfo.BlockNumber < highest.BlockNumber-int64(n.chainCfg.FinalizedBlockOffset()) +} + +// StateAndLatestChainInfo returns nodeState with the latest ChainInfo observed by Node during current lifecycle. +func (n *node[CHAIN_ID, HEAD, RPC]) StateAndLatestChainInfo() (nodeState, ChainInfo) { + n.stateMu.RLock() + defer n.stateMu.RUnlock() + return n.recalculateState(), n.latestChainInfo +} + +func (n *node[CHAIN_ID, HEAD, RPC]) getLatestChainInfo() ChainInfo { n.stateMu.RLock() defer n.stateMu.RUnlock() - return n.state, n.stateLatestBlockNumber, n.stateLatestTotalDifficulty + return n.latestChainInfo +} + +func (n *node[CHAIN_ID, HEAD, RPC]) setLatestChainInfo(ci ChainInfo) { + n.stateMu.Lock() + n.latestChainInfo = ci + n.stateMu.Unlock() +} + +// HighestChainInfo - returns highest ChainInfo ever observed by the Node +func (n *node[CHAIN_ID, HEAD, RPC]) HighestChainInfo() ChainInfo { + return n.rpc.GetInterceptedChainInfo() +} +func (n *node[CHAIN_ID, HEAD, RPC]) SetPoolChainInfoProvider(poolInfoProvider PoolChainInfoProvider) { + n.poolInfoProvider = poolInfoProvider } // setState is only used by internal state management methods. @@ -243,7 +300,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) transitionToUnreachable(fn func()) { } func (n *node[CHAIN_ID, HEAD, RPC]) declareState(state nodeState) { - if n.State() == nodeStateClosed { + if n.getCachedState() == nodeStateClosed { return } switch state { diff --git a/common/client/node_lifecycle.go b/common/client/node_lifecycle.go index 29c7249127f..92005e47e63 100644 --- a/common/client/node_lifecycle.go +++ b/common/client/node_lifecycle.go @@ -56,13 +56,6 @@ func zombieNodeCheckInterval(noNewHeadsThreshold time.Duration) time.Duration { return utils.WithJitter(interval) } -func (n *node[CHAIN_ID, HEAD, RPC]) setLatestReceived(blockNumber int64, totalDifficulty *big.Int) { - n.stateMu.Lock() - defer n.stateMu.Unlock() - n.stateLatestBlockNumber = blockNumber - n.stateLatestTotalDifficulty = totalDifficulty -} - const ( msgCannotDisable = "but cannot disable this connection because there are no other RPC endpoints, or all other RPC endpoints are dead." msgDegradedState = "Chainlink is now operating in a degraded state and urgent action is required to resolve the issue" @@ -80,7 +73,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { { // sanity check - state := n.State() + state := n.getCachedState() switch state { case nodeStateAlive: case nodeStateClosed: @@ -95,12 +88,12 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { pollInterval := n.nodePoolCfg.PollInterval() lggr := logger.Sugared(n.lfcLog).Named("Alive").With("noNewHeadsTimeoutThreshold", noNewHeadsTimeoutThreshold, "pollInterval", pollInterval, "pollFailureThreshold", pollFailureThreshold) - lggr.Tracew("Alive loop starting", "nodeState", n.State()) + lggr.Tracew("Alive loop starting", "nodeState", n.getCachedState()) headsC := make(chan HEAD) sub, err := n.rpc.SubscribeNewHead(n.nodeCtx, headsC) if err != nil { - lggr.Errorw("Initial subscribe for heads failed", "nodeState", n.State()) + lggr.Errorw("Initial subscribe for heads failed", "nodeState", n.getCachedState()) n.declareUnreachable() return } @@ -112,7 +105,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { var outOfSyncT *time.Ticker var outOfSyncTC <-chan time.Time if noNewHeadsTimeoutThreshold > 0 { - lggr.Debugw("Head liveness checking enabled", "nodeState", n.State()) + lggr.Debugw("Head liveness checking enabled", "nodeState", n.getCachedState()) outOfSyncT = time.NewTicker(noNewHeadsTimeoutThreshold) defer outOfSyncT.Stop() outOfSyncTC = outOfSyncT.C @@ -144,7 +137,15 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { pollFinalizedHeadCh = pollT.C } - _, highestReceivedBlockNumber, _ := n.StateAndLatest() + localHighestChainInfo := n.getLatestChainInfo() + defer func() { + // reset latest chain info to avoid following race condition: + // 1. Node observes block 100 and becomes unreachable. + // 2. While the node is down, its state is rolled back to block 90. + // 4. Node becomes reachable again. + // 5. Before new head is processed, we report that node is healthy and on block 100. + n.setLatestChainInfo(ChainInfo{}) + }() var pollFailures uint32 for { @@ -154,7 +155,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { case <-pollCh: var version string promPoolRPCNodePolls.WithLabelValues(n.chainID.String(), n.name).Inc() - lggr.Tracew("Polling for version", "nodeState", n.State(), "pollFailures", pollFailures) + lggr.Tracew("Polling for version", "nodeState", n.getCachedState(), "pollFailures", pollFailures) ctx, cancel := context.WithTimeout(n.nodeCtx, pollInterval) version, err := n.RPC().ClientVersion(ctx) cancel() @@ -164,16 +165,16 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { promPoolRPCNodePollsFailed.WithLabelValues(n.chainID.String(), n.name).Inc() pollFailures++ } - lggr.Warnw(fmt.Sprintf("Poll failure, RPC endpoint %s failed to respond properly", n.String()), "err", err, "pollFailures", pollFailures, "nodeState", n.State()) + 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.State(), "clientVersion", version) + lggr.Debugw("Version poll successful", "nodeState", n.getCachedState(), "clientVersion", version) promPoolRPCNodePollsSuccess.WithLabelValues(n.chainID.String(), n.name).Inc() pollFailures = 0 } if pollFailureThreshold > 0 && pollFailures >= pollFailureThreshold { - lggr.Errorw(fmt.Sprintf("RPC endpoint failed to respond to %d consecutive polls", pollFailures), "pollFailures", pollFailures, "nodeState", n.State()) - if n.nLiveNodes != nil { - if l, _, _ := n.nLiveNodes(); l < 2 { + lggr.Errorw(fmt.Sprintf("RPC endpoint failed to respond to %d consecutive polls", pollFailures), "pollFailures", pollFailures, "nodeState", n.getCachedState()) + if n.poolInfoProvider != nil { + if l, _ := n.poolInfoProvider.LatestChainInfo(); l < 2 { lggr.Criticalf("RPC endpoint failed to respond to polls; %s %s", msgCannotDisable, msgDegradedState) continue } @@ -181,10 +182,10 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { n.declareUnreachable() return } - _, num, td := n.StateAndLatest() - if outOfSync, liveNodes := n.syncStatus(num, td); outOfSync { + _, ci := n.StateAndLatestChainInfo() + if outOfSync, liveNodes := n.syncStatus(ci.BlockNumber, ci.TotalDifficulty); outOfSync { // note: there must be another live node for us to be out of sync - lggr.Errorw("RPC endpoint has fallen behind", "blockNumber", num, "totalDifficulty", td, "nodeState", n.State()) + lggr.Errorw("RPC endpoint has fallen behind", "blockNumber", ci.BlockNumber, "totalDifficulty", ci.TotalDifficulty, "nodeState", n.getCachedState()) if liveNodes < 2 { lggr.Criticalf("RPC endpoint has fallen behind; %s %s", msgCannotDisable, msgDegradedState) continue @@ -194,40 +195,40 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { } case bh, open := <-headsC: if !open { - lggr.Errorw("Subscription channel unexpectedly closed", "nodeState", n.State()) + lggr.Errorw("Subscription channel unexpectedly closed", "nodeState", n.getCachedState()) n.declareUnreachable() return } promPoolRPCNodeNumSeenBlocks.WithLabelValues(n.chainID.String(), n.name).Inc() lggr.Tracew("Got head", "head", bh) - if bh.BlockNumber() > highestReceivedBlockNumber { + if bh.BlockNumber() > localHighestChainInfo.BlockNumber { promPoolRPCNodeHighestSeenBlock.WithLabelValues(n.chainID.String(), n.name).Set(float64(bh.BlockNumber())) - lggr.Tracew("Got higher block number, resetting timer", "latestReceivedBlockNumber", highestReceivedBlockNumber, "blockNumber", bh.BlockNumber(), "nodeState", n.State()) - highestReceivedBlockNumber = bh.BlockNumber() + lggr.Tracew("Got higher block number, resetting timer", "latestReceivedBlockNumber", localHighestChainInfo.BlockNumber, "blockNumber", bh.BlockNumber(), "nodeState", n.getCachedState()) + localHighestChainInfo.BlockNumber = bh.BlockNumber() } else { - lggr.Tracew("Ignoring previously seen block number", "latestReceivedBlockNumber", highestReceivedBlockNumber, "blockNumber", bh.BlockNumber(), "nodeState", n.State()) + lggr.Tracew("Ignoring previously seen block number", "latestReceivedBlockNumber", localHighestChainInfo.BlockNumber, "blockNumber", bh.BlockNumber(), "nodeState", n.getCachedState()) } if outOfSyncT != nil { outOfSyncT.Reset(noNewHeadsTimeoutThreshold) } - n.setLatestReceived(bh.BlockNumber(), bh.BlockDifficulty()) + n.onNewHead(bh) if !n.chainCfg.FinalityTagEnabled() { latestFinalizedBN := max(bh.BlockNumber()-int64(n.chainCfg.FinalityDepth()), 0) - if latestFinalizedBN > n.stateLatestFinalizedBlockNumber { + if latestFinalizedBN > localHighestChainInfo.FinalizedBlockNumber { promPoolRPCNodeHighestFinalizedBlock.WithLabelValues(n.chainID.String(), n.name).Set(float64(latestFinalizedBN)) - n.stateLatestFinalizedBlockNumber = latestFinalizedBN + localHighestChainInfo.FinalizedBlockNumber = latestFinalizedBN } } case err := <-sub.Err(): - lggr.Errorw("Subscription was terminated", "err", err, "nodeState", n.State()) + lggr.Errorw("Subscription was terminated", "err", err, "nodeState", n.getCachedState()) n.declareUnreachable() return case <-outOfSyncTC: // We haven't received a head on the channel for at least the // threshold amount of time, mark it broken - lggr.Errorw(fmt.Sprintf("RPC endpoint detected out of sync; no new heads received for %s (last head received was %v)", noNewHeadsTimeoutThreshold, highestReceivedBlockNumber), "nodeState", n.State(), "latestReceivedBlockNumber", highestReceivedBlockNumber, "noNewHeadsTimeoutThreshold", noNewHeadsTimeoutThreshold) - if n.nLiveNodes != nil { - if l, _, _ := n.nLiveNodes(); l < 2 { + lggr.Errorw(fmt.Sprintf("RPC endpoint detected out of sync; no new heads received for %s (last head received was %v)", noNewHeadsTimeoutThreshold, localHighestChainInfo.BlockNumber), "nodeState", n.getCachedState(), "latestReceivedBlockNumber", localHighestChainInfo.BlockNumber, "noNewHeadsTimeoutThreshold", noNewHeadsTimeoutThreshold) + if n.poolInfoProvider != nil { + if l, _ := n.poolInfoProvider.LatestChainInfo(); l < 2 { lggr.Criticalf("RPC endpoint detected out of sync; %s %s", msgCannotDisable, msgDegradedState) // We don't necessarily want to wait the full timeout to check again, we should // check regularly and log noisily in this state @@ -235,7 +236,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { continue } } - n.declareOutOfSync(func(num int64, td *big.Int) bool { return num < highestReceivedBlockNumber }) + n.declareOutOfSync(func(num int64, td *big.Int) bool { return num < localHighestChainInfo.BlockNumber }) return case <-pollFinalizedHeadCh: ctx, cancel := context.WithTimeout(n.nodeCtx, n.nodePoolCfg.FinalizedBlockPollInterval()) @@ -251,10 +252,11 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { continue } + n.onNewFinalizedHead(latestFinalized) latestFinalizedBN := latestFinalized.BlockNumber() - if latestFinalizedBN > n.stateLatestFinalizedBlockNumber { + if latestFinalizedBN > localHighestChainInfo.FinalizedBlockNumber { promPoolRPCNodeHighestFinalizedBlock.WithLabelValues(n.chainID.String(), n.name).Set(float64(latestFinalizedBN)) - n.stateLatestFinalizedBlockNumber = latestFinalizedBN + n.latestChainInfo.FinalizedBlockNumber = latestFinalizedBN } } @@ -270,7 +272,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) isOutOfSync(num int64, td *big.Int) (outOfSy // Always returns outOfSync false for SyncThreshold 0. // liveNodes is only included when outOfSync is true. func (n *node[CHAIN_ID, HEAD, RPC]) syncStatus(num int64, td *big.Int) (outOfSync bool, liveNodes int) { - if n.nLiveNodes == nil { + if n.poolInfoProvider == nil { return // skip for tests } threshold := n.nodePoolCfg.SyncThreshold() @@ -278,14 +280,14 @@ func (n *node[CHAIN_ID, HEAD, RPC]) syncStatus(num int64, td *big.Int) (outOfSyn return // disabled } // Check against best node - ln, highest, greatest := n.nLiveNodes() + ln, ci := n.poolInfoProvider.LatestChainInfo() mode := n.nodePoolCfg.SelectionMode() switch mode { case NodeSelectionModeHighestHead, NodeSelectionModeRoundRobin, NodeSelectionModePriorityLevel: - return num < highest-int64(threshold), ln + return num < ci.BlockNumber-int64(threshold), ln case NodeSelectionModeTotalDifficulty: bigThreshold := big.NewInt(int64(threshold)) - return td.Cmp(bigmath.Sub(greatest, bigThreshold)) < 0, ln + return td.Cmp(bigmath.Sub(ci.TotalDifficulty, bigThreshold)) < 0, ln default: panic("unrecognized NodeSelectionMode: " + mode) } @@ -302,7 +304,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(isOutOfSync func(num int64, td { // sanity check - state := n.State() + state := n.getCachedState() switch state { case nodeStateOutOfSync: case nodeStateClosed: @@ -315,7 +317,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(isOutOfSync func(num int64, td outOfSyncAt := time.Now() lggr := logger.Sugared(logger.Named(n.lfcLog, "OutOfSync")) - lggr.Debugw("Trying to revive out-of-sync RPC node", "nodeState", n.State()) + lggr.Debugw("Trying to revive out-of-sync RPC node", "nodeState", n.getCachedState()) // Need to redial since out-of-sync nodes are automatically disconnected state := n.createVerifiedConn(n.nodeCtx, lggr) @@ -324,12 +326,12 @@ func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(isOutOfSync func(num int64, td return } - lggr.Tracew("Successfully subscribed to heads feed on out-of-sync RPC node", "nodeState", n.State()) + lggr.Tracew("Successfully subscribed to heads feed on out-of-sync RPC node", "nodeState", n.getCachedState()) ch := make(chan HEAD) sub, err := n.rpc.SubscribeNewHead(n.nodeCtx, ch) if err != nil { - lggr.Errorw("Failed to subscribe heads on out-of-sync RPC node", "nodeState", n.State(), "err", err) + lggr.Errorw("Failed to subscribe heads on out-of-sync RPC node", "nodeState", n.getCachedState(), "err", err) n.declareUnreachable() return } @@ -341,28 +343,28 @@ func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(isOutOfSync func(num int64, td return case head, open := <-ch: if !open { - lggr.Error("Subscription channel unexpectedly closed", "nodeState", n.State()) + lggr.Error("Subscription channel unexpectedly closed", "nodeState", n.getCachedState()) n.declareUnreachable() return } - n.setLatestReceived(head.BlockNumber(), head.BlockDifficulty()) + n.onNewHead(head) if !isOutOfSync(head.BlockNumber(), head.BlockDifficulty()) { // back in-sync! flip back into alive loop - lggr.Infow(fmt.Sprintf("%s: %s. Node was out-of-sync for %s", msgInSync, n.String(), time.Since(outOfSyncAt)), "blockNumber", head.BlockNumber(), "blockDifficulty", head.BlockDifficulty(), "nodeState", n.State()) + lggr.Infow(fmt.Sprintf("%s: %s. Node was out-of-sync for %s", msgInSync, n.String(), time.Since(outOfSyncAt)), "blockNumber", head.BlockNumber(), "blockDifficulty", head.BlockDifficulty(), "nodeState", n.getCachedState()) n.declareInSync() return } - lggr.Debugw(msgReceivedBlock, "blockNumber", head.BlockNumber(), "blockDifficulty", head.BlockDifficulty(), "nodeState", n.State()) + lggr.Debugw(msgReceivedBlock, "blockNumber", head.BlockNumber(), "blockDifficulty", head.BlockDifficulty(), "nodeState", n.getCachedState()) case <-time.After(zombieNodeCheckInterval(n.chainCfg.NodeNoNewHeadsThreshold())): - if n.nLiveNodes != nil { - if l, _, _ := n.nLiveNodes(); l < 1 { + if n.poolInfoProvider != nil { + if l, _ := n.poolInfoProvider.LatestChainInfo(); l < 1 { lggr.Critical("RPC endpoint is still out of sync, but there are no other available nodes. This RPC node will be forcibly moved back into the live pool in a degraded state") n.declareInSync() return } } case err := <-sub.Err(): - lggr.Errorw("Subscription was terminated", "nodeState", n.State(), "err", err) + lggr.Errorw("Subscription was terminated", "nodeState", n.getCachedState(), "err", err) n.declareUnreachable() return } @@ -374,7 +376,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) unreachableLoop() { { // sanity check - state := n.State() + state := n.getCachedState() switch state { case nodeStateUnreachable: case nodeStateClosed: @@ -387,7 +389,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) unreachableLoop() { unreachableAt := time.Now() lggr := logger.Sugared(logger.Named(n.lfcLog, "Unreachable")) - lggr.Debugw("Trying to revive unreachable RPC node", "nodeState", n.State()) + lggr.Debugw("Trying to revive unreachable RPC node", "nodeState", n.getCachedState()) dialRetryBackoff := iutils.NewRedialBackoff() @@ -396,11 +398,11 @@ func (n *node[CHAIN_ID, HEAD, RPC]) unreachableLoop() { case <-n.nodeCtx.Done(): return case <-time.After(dialRetryBackoff.Duration()): - lggr.Tracew("Trying to re-dial RPC node", "nodeState", n.State()) + lggr.Tracew("Trying to re-dial RPC node", "nodeState", n.getCachedState()) err := n.rpc.Dial(n.nodeCtx) if err != nil { - lggr.Errorw(fmt.Sprintf("Failed to redial RPC node; still unreachable: %v", err), "err", err, "nodeState", n.State()) + lggr.Errorw(fmt.Sprintf("Failed to redial RPC node; still unreachable: %v", err), "err", err, "nodeState", n.getCachedState()) continue } @@ -412,7 +414,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) unreachableLoop() { n.setState(nodeStateUnreachable) continue case nodeStateAlive: - lggr.Infow(fmt.Sprintf("Successfully redialled and verified RPC node %s. Node was offline for %s", n.String(), time.Since(unreachableAt)), "nodeState", n.State()) + lggr.Infow(fmt.Sprintf("Successfully redialled and verified RPC node %s. Node was offline for %s", n.String(), time.Since(unreachableAt)), "nodeState", n.getCachedState()) fallthrough default: n.declareState(state) @@ -427,7 +429,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) invalidChainIDLoop() { { // sanity check - state := n.State() + state := n.getCachedState() switch state { case nodeStateInvalidChainID: case nodeStateClosed: @@ -448,7 +450,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) invalidChainIDLoop() { return } - lggr.Debugw(fmt.Sprintf("Periodically re-checking RPC node %s with invalid chain ID", n.String()), "nodeState", n.State()) + lggr.Debugw(fmt.Sprintf("Periodically re-checking RPC node %s with invalid chain ID", n.String()), "nodeState", n.getCachedState()) chainIDRecheckBackoff := iutils.NewRedialBackoff() @@ -462,7 +464,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) invalidChainIDLoop() { case nodeStateInvalidChainID: continue case nodeStateAlive: - lggr.Infow(fmt.Sprintf("Successfully verified RPC node. Node was offline for %s", time.Since(invalidAt)), "nodeState", n.State()) + lggr.Infow(fmt.Sprintf("Successfully verified RPC node. Node was offline for %s", time.Since(invalidAt)), "nodeState", n.getCachedState()) fallthrough default: n.declareState(state) @@ -477,7 +479,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) syncingLoop() { { // sanity check - state := n.State() + state := n.getCachedState() switch state { case nodeStateSyncing: case nodeStateClosed: @@ -490,7 +492,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) syncingLoop() { syncingAt := time.Now() lggr := logger.Sugared(logger.Named(n.lfcLog, "Syncing")) - lggr.Debugw(fmt.Sprintf("Periodically re-checking RPC node %s with syncing status", n.String()), "nodeState", n.State()) + lggr.Debugw(fmt.Sprintf("Periodically re-checking RPC node %s with syncing status", n.String()), "nodeState", n.getCachedState()) // Need to redial since syncing nodes are automatically disconnected state := n.createVerifiedConn(n.nodeCtx, lggr) if state != nodeStateSyncing { @@ -505,23 +507,39 @@ func (n *node[CHAIN_ID, HEAD, RPC]) syncingLoop() { case <-n.nodeCtx.Done(): return case <-time.After(recheckBackoff.Duration()): - lggr.Tracew("Trying to recheck if the node is still syncing", "nodeState", n.State()) + lggr.Tracew("Trying to recheck if the node is still syncing", "nodeState", n.getCachedState()) isSyncing, err := n.rpc.IsSyncing(n.nodeCtx) if err != nil { - lggr.Errorw("Unexpected error while verifying RPC node synchronization status", "err", err, "nodeState", n.State()) + lggr.Errorw("Unexpected error while verifying RPC node synchronization status", "err", err, "nodeState", n.getCachedState()) n.declareUnreachable() return } if isSyncing { - lggr.Errorw("Verification failed: Node is syncing", "nodeState", n.State()) + lggr.Errorw("Verification failed: Node is syncing", "nodeState", n.getCachedState()) continue } - lggr.Infow(fmt.Sprintf("Successfully verified RPC node. Node was syncing for %s", time.Since(syncingAt)), "nodeState", n.State()) + lggr.Infow(fmt.Sprintf("Successfully verified RPC node. Node was syncing for %s", time.Since(syncingAt)), "nodeState", n.getCachedState()) n.declareAlive() return } } } + +func (n *node[CHAIN_ID, HEAD, RPC]) onNewHead(head HEAD) { + n.stateMu.Lock() + defer n.stateMu.Unlock() + n.latestChainInfo.BlockNumber = head.BlockNumber() + n.latestChainInfo.TotalDifficulty = head.BlockDifficulty() + if !n.chainCfg.FinalityTagEnabled() { + n.latestChainInfo.FinalizedBlockNumber = max(head.BlockNumber()-int64(n.chainCfg.FinalityDepth()), 0) + } +} + +func (n *node[CHAIN_ID, HEAD, RPC]) onNewFinalizedHead(head HEAD) { + n.stateMu.Lock() + defer n.stateMu.Unlock() + n.latestChainInfo.FinalizedBlockNumber = head.BlockNumber() +} diff --git a/common/client/node_lifecycle_test.go b/common/client/node_lifecycle_test.go index 30312575541..fea1605baa2 100644 --- a/common/client/node_lifecycle_test.go +++ b/common/client/node_lifecycle_test.go @@ -192,9 +192,12 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() - node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { - return 1, 20, big.NewInt(10) - } + poolInfo := newMockPoolChainInfoProvider(t) + poolInfo.On("LatestChainInfo").Return(1, ChainInfo{ + BlockNumber: 20, + }).Once() + node.SetPoolChainInfoProvider(poolInfo) + node.setLatestChainInfo(ChainInfo{BlockNumber: 20}) pollError := errors.New("failed to get ClientVersion") rpc.On("ClientVersion", mock.Anything).Return("", pollError) node.declareAlive() @@ -216,10 +219,13 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() - node.stateLatestBlockNumber = 20 - node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { - return 10, syncThreshold + node.stateLatestBlockNumber + 1, big.NewInt(10) - } + node.latestChainInfo.BlockNumber = 20 + poolInfo := newMockPoolChainInfoProvider(t) + poolInfo.On("LatestChainInfo").Return(10, ChainInfo{ + BlockNumber: syncThreshold + node.latestChainInfo.BlockNumber + 1, + 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) { @@ -249,10 +255,13 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() - node.stateLatestBlockNumber = 20 - node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { - return 1, syncThreshold + node.stateLatestBlockNumber + 1, big.NewInt(10) - } + node.latestChainInfo.BlockNumber = 20 + poolInfo := newMockPoolChainInfoProvider(t) + poolInfo.On("LatestChainInfo").Return(1, ChainInfo{ + BlockNumber: syncThreshold + node.latestChainInfo.BlockNumber + 1, + 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)) @@ -271,10 +280,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() - node.stateLatestBlockNumber = 20 - node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { - return 1, node.stateLatestBlockNumber + 100, big.NewInt(10) - } + node.latestChainInfo.BlockNumber = 20 rpc.On("ClientVersion", mock.Anything).Return("", nil) node.declareAlive() tests.AssertLogCountEventually(t, observedLogs, "Version poll successful", 2) @@ -320,9 +326,12 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { rpc: rpc, }) defer func() { assert.NoError(t, node.close()) }() - node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { - return 1, 20, big.NewInt(10) - } + poolInfo := newMockPoolChainInfoProvider(t) + poolInfo.On("LatestChainInfo").Return(1, ChainInfo{ + BlockNumber: 20, + TotalDifficulty: big.NewInt(10), + }).Once() + node.SetPoolChainInfoProvider(poolInfo) node.declareAlive() tests.AssertLogEventually(t, observedLogs, fmt.Sprintf("RPC endpoint detected out of sync; %s %s", msgCannotDisable, msgDegradedState)) assert.Equal(t, nodeStateAlive, node.State()) @@ -364,22 +373,25 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { sub := mocks.NewSubscription(t) sub.On("Err").Return((<-chan error)(nil)) sub.On("Unsubscribe").Once() - expectedBlockNumber := rand.Int64() - expectedDiff := big.NewInt(rand.Int64()) + expectedBlockNumber := rand.Int63() + expectedDiff := big.NewInt(rand.Int63()) rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { ch := args.Get(1).(chan<- Head) go writeHeads(t, ch, head{BlockNumber: expectedBlockNumber, BlockDifficulty: expectedDiff}) }).Return(sub, nil).Once() rpc.On("SetAliveLoopSub", sub).Once() node := newDialedNode(t, testNodeOpts{ - config: testNodeConfig{}, - rpc: rpc, + config: testNodeConfig{}, + chainConfig: clientMocks.ChainConfig{IsFinalityTagEnabled: true}, + rpc: rpc, }) defer func() { assert.NoError(t, node.close()) }() node.declareAlive() tests.AssertEventually(t, func() bool { - state, block, diff := node.StateAndLatest() - return state == nodeStateAlive && block == expectedBlockNumber == bigmath.Equal(diff, expectedDiff) + state, chainInfo := node.StateAndLatestChainInfo() + return state == nodeStateAlive && chainInfo.BlockNumber == expectedBlockNumber && bigmath.Equal(chainInfo.TotalDifficulty, expectedDiff) && + // finality tag is enabled, so must not update finalized block number + chainInfo.FinalizedBlockNumber == 0 }) }) t.Run("If finality tag is not enabled updates finalized block metric using finality depth and latest head", func(t *testing.T) { @@ -464,7 +476,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { node.declareAlive() tests.AssertLogEventually(t, observedLogs, "Latest finalized block is not valid") }) - t.Run("If finality tag and finalized block polling are enabled updates latest finalized block metric", func(t *testing.T) { + t.Run("If finality tag and finalized block polling are enabled updates latest finalized block metric & LatestChainInfo", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) const expectedBlock = 1101 @@ -501,7 +513,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { require.NoError(t, err) var m = &prom.Metric{} require.NoError(t, metric.Write(m)) - return float64(expectedBlock) == m.Gauge.GetValue() + return float64(expectedBlock) == m.Gauge.GetValue() && node.getLatestChainInfo().FinalizedBlockNumber == expectedBlock }) }) } @@ -819,9 +831,12 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() - node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { - return 0, 100, big.NewInt(200) - } + poolInfo := newMockPoolChainInfoProvider(t) + poolInfo.On("LatestChainInfo").Return(0, ChainInfo{ + BlockNumber: 100, + TotalDifficulty: big.NewInt(200), + }).Once() + node.SetPoolChainInfoProvider(poolInfo) rpc.On("Dial", mock.Anything).Return(nil).Once() rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() @@ -1310,9 +1325,8 @@ func TestUnit_NodeLifecycle_syncStatus(t *testing.T) { }) t.Run("skip if syncThreshold is not configured", func(t *testing.T) { node := newTestNode(t, testNodeOpts{}) - node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { - return - } + poolInfo := newMockPoolChainInfoProvider(t) + node.SetPoolChainInfoProvider(poolInfo) outOfSync, liveNodes := node.syncStatus(0, nil) assert.Equal(t, false, outOfSync) assert.Equal(t, 0, liveNodes) @@ -1321,9 +1335,9 @@ func TestUnit_NodeLifecycle_syncStatus(t *testing.T) { node := newTestNode(t, testNodeOpts{ config: testNodeConfig{syncThreshold: 1}, }) - node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { - return - } + poolInfo := newMockPoolChainInfoProvider(t) + poolInfo.On("LatestChainInfo").Return(1, ChainInfo{}).Once() + node.SetPoolChainInfoProvider(poolInfo) assert.Panics(t, func() { _, _ = node.syncStatus(0, nil) }) @@ -1367,9 +1381,12 @@ func TestUnit_NodeLifecycle_syncStatus(t *testing.T) { selectionMode: selectionMode, }, }) - node.nLiveNodes = func() (int, int64, *big.Int) { - return nodesNum, highestBlock, big.NewInt(totalDifficulty) - } + poolInfo := newMockPoolChainInfoProvider(t) + poolInfo.On("LatestChainInfo").Return(nodesNum, ChainInfo{ + BlockNumber: highestBlock, + TotalDifficulty: big.NewInt(totalDifficulty), + }) + node.SetPoolChainInfoProvider(poolInfo) for _, td := range []int64{totalDifficulty - syncThreshold - 1, totalDifficulty - syncThreshold, totalDifficulty, totalDifficulty + 1} { for _, testCase := range testCases { t.Run(fmt.Sprintf("%s: SelectionModeVal: %s: total difficulty: %d", testCase.name, selectionMode, td), func(t *testing.T) { @@ -1420,9 +1437,13 @@ func TestUnit_NodeLifecycle_syncStatus(t *testing.T) { selectionMode: NodeSelectionModeTotalDifficulty, }, }) - node.nLiveNodes = func() (int, int64, *big.Int) { - return nodesNum, highestBlock, big.NewInt(totalDifficulty) - } + + poolInfo := newMockPoolChainInfoProvider(t) + poolInfo.On("LatestChainInfo").Return(nodesNum, ChainInfo{ + BlockNumber: highestBlock, + TotalDifficulty: big.NewInt(totalDifficulty), + }) + node.SetPoolChainInfoProvider(poolInfo) for _, hb := range []int64{highestBlock - syncThreshold - 1, highestBlock - syncThreshold, highestBlock, highestBlock + 1} { for _, testCase := range testCases { t.Run(fmt.Sprintf("%s: SelectionModeVal: %s: highest block: %d", testCase.name, NodeSelectionModeTotalDifficulty, hb), func(t *testing.T) { @@ -1582,3 +1603,96 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { }) }) } + +func TestNode_State(t *testing.T) { + t.Run("If not Alive, returns as is", func(t *testing.T) { + for state := nodeState(0); state < nodeStateLen; state++ { + if state == nodeStateAlive { + continue + } + + node := newTestNode(t, testNodeOpts{}) + node.setState(state) + assert.Equal(t, state, node.State()) + } + }) + t.Run("If repeatable read is not enforced, returns alive", func(t *testing.T) { + node := newTestNode(t, testNodeOpts{}) + node.setState(nodeStateAlive) + assert.Equal(t, nodeStateAlive, node.State()) + }) + testCases := []struct { + Name string + FinalizedBlockOffsetVal uint32 + IsFinalityTagEnabled bool + PoolChainInfo ChainInfo + NodeChainInfo ChainInfo + ExpectedState nodeState + }{ + { + Name: "If finality lag does not exceeds offset, returns alive (FinalityDepth)", + FinalizedBlockOffsetVal: 15, + PoolChainInfo: ChainInfo{ + BlockNumber: 20, + }, + NodeChainInfo: ChainInfo{ + BlockNumber: 5, + }, + ExpectedState: nodeStateAlive, + }, + { + Name: "If finality lag does not exceeds offset, returns alive (FinalityTag)", + FinalizedBlockOffsetVal: 15, + IsFinalityTagEnabled: true, + PoolChainInfo: ChainInfo{ + FinalizedBlockNumber: 20, + }, + NodeChainInfo: ChainInfo{ + FinalizedBlockNumber: 5, + }, + ExpectedState: nodeStateAlive, + }, + { + Name: "If finality lag does not exceeds offset, returns nodeStateFinalizedBlockOutOfSync (FinalityDepth)", + FinalizedBlockOffsetVal: 15, + PoolChainInfo: ChainInfo{ + BlockNumber: 20, + }, + NodeChainInfo: ChainInfo{ + BlockNumber: 4, + }, + ExpectedState: nodeStateFinalizedBlockOutOfSync, + }, + { + Name: "If finality lag does not exceeds offset, returns nodeStateFinalizedBlockOutOfSync (FinalityTag)", + FinalizedBlockOffsetVal: 15, + IsFinalityTagEnabled: true, + PoolChainInfo: ChainInfo{ + FinalizedBlockNumber: 20, + }, + NodeChainInfo: ChainInfo{ + FinalizedBlockNumber: 4, + }, + ExpectedState: nodeStateFinalizedBlockOutOfSync, + }, + } + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + node := newTestNode(t, testNodeOpts{ + config: testNodeConfig{ + enforceRepeatableRead: true, + }, + chainConfig: clientMocks.ChainConfig{ + FinalizedBlockOffsetVal: tc.FinalizedBlockOffsetVal, + IsFinalityTagEnabled: tc.IsFinalityTagEnabled, + }, + }) + poolInfo := newMockPoolChainInfoProvider(t) + poolInfo.On("HighestChainInfo").Return(tc.PoolChainInfo).Once() + node.SetPoolChainInfoProvider(poolInfo) + node.setLatestChainInfo(tc.NodeChainInfo) + node.setState(nodeStateAlive) + assert.Equal(t, tc.ExpectedState, node.State()) + }) + } +} diff --git a/common/client/node_selector_highest_head.go b/common/client/node_selector_highest_head.go index 99a130004a9..f753a991782 100644 --- a/common/client/node_selector_highest_head.go +++ b/common/client/node_selector_highest_head.go @@ -24,7 +24,8 @@ func (s highestHeadNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HE var highestHeadNumber int64 = math.MinInt64 var highestHeadNodes []Node[CHAIN_ID, HEAD, RPC] for _, n := range s { - state, currentHeadNumber, _ := n.StateAndLatest() + state, currentChainInfo := n.StateAndLatestChainInfo() + currentHeadNumber := currentChainInfo.BlockNumber if state == nodeStateAlive && currentHeadNumber >= highestHeadNumber { if highestHeadNumber < currentHeadNumber { highestHeadNumber = currentHeadNumber diff --git a/common/client/node_selector_highest_head_test.go b/common/client/node_selector_highest_head_test.go index 6e47bbedcae..9b87fa240f6 100644 --- a/common/client/node_selector_highest_head_test.go +++ b/common/client/node_selector_highest_head_test.go @@ -24,13 +24,13 @@ func TestHighestHeadNodeSelector(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) if i == 0 { // first node is out of sync - node.On("StateAndLatest").Return(nodeStateOutOfSync, int64(-1), nil) + node.On("StateAndLatestChainInfo").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: int64(-1)}) } else if i == 1 { // second node is alive, LatestReceivedBlockNumber = 1 - node.On("StateAndLatest").Return(nodeStateAlive, int64(1), nil) + node.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(1)}) } else { // third node is alive, LatestReceivedBlockNumber = 2 (best node) - node.On("StateAndLatest").Return(nodeStateAlive, int64(2), nil) + node.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(2)}) } node.On("Order").Maybe().Return(int32(1)) nodes = append(nodes, node) @@ -42,7 +42,7 @@ func TestHighestHeadNodeSelector(t *testing.T) { t.Run("stick to the same node", func(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) // fourth node is alive, LatestReceivedBlockNumber = 2 (same as 3rd) - node.On("StateAndLatest").Return(nodeStateAlive, int64(2), nil) + node.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(2)}) node.On("Order").Return(int32(1)) nodes = append(nodes, node) @@ -53,7 +53,7 @@ func TestHighestHeadNodeSelector(t *testing.T) { t.Run("another best node", func(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) // fifth node is alive, LatestReceivedBlockNumber = 3 (better than 3rd and 4th) - node.On("StateAndLatest").Return(nodeStateAlive, int64(3), nil) + node.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) node.On("Order").Return(int32(1)) nodes = append(nodes, node) @@ -63,10 +63,10 @@ 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.On("StateAndLatest").Return(nodeStateAlive, int64(-1), nil) + node1.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(-1)}) node1.On("Order").Return(int32(1)) node2 := newMockNode[types.ID, Head, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, int64(-1), nil) + node2.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(-1)}) node2.On("Order").Return(int32(1)) selector := newNodeSelector(NodeSelectionModeHighestHead, []Node[types.ID, Head, nodeClient]{node1, node2}) assert.Same(t, node1, selector.Select()) @@ -83,10 +83,10 @@ func TestHighestHeadNodeSelector_None(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) if i == 0 { // first node is out of sync - node.On("StateAndLatest").Return(nodeStateOutOfSync, int64(-1), nil) + node.On("StateAndLatestChainInfo").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: int64(-1)}) } else { // others are unreachable - node.On("StateAndLatest").Return(nodeStateUnreachable, int64(1), nil) + node.On("StateAndLatestChainInfo").Return(nodeStateUnreachable, ChainInfo{BlockNumber: int64(-1)}) } nodes = append(nodes, node) } @@ -104,7 +104,7 @@ func TestHighestHeadNodeSelectorWithOrder(t *testing.T) { t.Run("same head and order", func(t *testing.T) { for i := 0; i < 3; i++ { node := newMockNode[types.ID, Head, nodeClient](t) - node.On("StateAndLatest").Return(nodeStateAlive, int64(1), nil) + node.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(1)}) node.On("Order").Return(int32(2)) nodes = append(nodes, node) } @@ -115,15 +115,15 @@ func TestHighestHeadNodeSelectorWithOrder(t *testing.T) { t.Run("same head but different order", func(t *testing.T) { node1 := newMockNode[types.ID, Head, nodeClient](t) - node1.On("StateAndLatest").Return(nodeStateAlive, int64(3), nil) + node1.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) node1.On("Order").Return(int32(3)) node2 := newMockNode[types.ID, Head, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, int64(3), nil) + node2.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) node2.On("Order").Return(int32(1)) node3 := newMockNode[types.ID, Head, nodeClient](t) - node3.On("StateAndLatest").Return(nodeStateAlive, int64(3), nil) + node3.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) node3.On("Order").Return(int32(2)) nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3} @@ -134,15 +134,15 @@ func TestHighestHeadNodeSelectorWithOrder(t *testing.T) { t.Run("different head but same order", func(t *testing.T) { node1 := newMockNode[types.ID, Head, nodeClient](t) - node1.On("StateAndLatest").Return(nodeStateAlive, int64(1), nil) + node1.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(1)}) node1.On("Order").Maybe().Return(int32(3)) node2 := newMockNode[types.ID, Head, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, int64(2), nil) + node2.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(2)}) node2.On("Order").Maybe().Return(int32(3)) node3 := newMockNode[types.ID, Head, nodeClient](t) - node3.On("StateAndLatest").Return(nodeStateAlive, int64(3), nil) + node3.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) node3.On("Order").Return(int32(3)) nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3} @@ -153,19 +153,19 @@ func TestHighestHeadNodeSelectorWithOrder(t *testing.T) { t.Run("different head and different order", func(t *testing.T) { node1 := newMockNode[types.ID, Head, nodeClient](t) - node1.On("StateAndLatest").Return(nodeStateAlive, int64(10), nil) + node1.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(10)}) node1.On("Order").Maybe().Return(int32(3)) node2 := newMockNode[types.ID, Head, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, int64(11), nil) + node2.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(11)}) node2.On("Order").Maybe().Return(int32(4)) node3 := newMockNode[types.ID, Head, nodeClient](t) - node3.On("StateAndLatest").Return(nodeStateAlive, int64(11), nil) + node3.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(11)}) node3.On("Order").Maybe().Return(int32(3)) node4 := newMockNode[types.ID, Head, nodeClient](t) - node4.On("StateAndLatest").Return(nodeStateAlive, int64(10), nil) + node4.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(10)}) node4.On("Order").Maybe().Return(int32(1)) nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3, node4} diff --git a/common/client/node_selector_total_difficulty.go b/common/client/node_selector_total_difficulty.go index 35491503bcc..3587353c748 100644 --- a/common/client/node_selector_total_difficulty.go +++ b/common/client/node_selector_total_difficulty.go @@ -27,11 +27,12 @@ func (s totalDifficultyNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID var aliveNodes []Node[CHAIN_ID, HEAD, RPC] for _, n := range s { - state, _, currentTD := n.StateAndLatest() + state, currentChainInfo := n.StateAndLatestChainInfo() if state != nodeStateAlive { continue } + currentTD := currentChainInfo.TotalDifficulty aliveNodes = append(aliveNodes, n) if currentTD != nil && (highestTD == nil || currentTD.Cmp(highestTD) >= 0) { if highestTD == nil || currentTD.Cmp(highestTD) > 0 { diff --git a/common/client/node_selector_total_difficulty_test.go b/common/client/node_selector_total_difficulty_test.go index 5c43cdd8472..9e11bced3c3 100644 --- a/common/client/node_selector_total_difficulty_test.go +++ b/common/client/node_selector_total_difficulty_test.go @@ -1,7 +1,7 @@ package client import ( - big "math/big" + "math/big" "testing" "github.com/smartcontractkit/chainlink/v2/common/types" @@ -24,13 +24,13 @@ func TestTotalDifficultyNodeSelector(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) if i == 0 { // first node is out of sync - node.On("StateAndLatest").Return(nodeStateOutOfSync, int64(-1), nil) + node.On("StateAndLatestChainInfo").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: -1}) } else if i == 1 { // second node is alive - node.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(7)) + node.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(7)}) } else { // third node is alive and best - node.On("StateAndLatest").Return(nodeStateAlive, int64(2), big.NewInt(8)) + node.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 2, TotalDifficulty: big.NewInt(8)}) } node.On("Order").Maybe().Return(int32(1)) nodes = append(nodes, node) @@ -42,7 +42,7 @@ func TestTotalDifficultyNodeSelector(t *testing.T) { t.Run("stick to the same node", func(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) // fourth node is alive (same as 3rd) - node.On("StateAndLatest").Return(nodeStateAlive, int64(2), big.NewInt(8)) + node.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 2, TotalDifficulty: big.NewInt(8)}) node.On("Order").Maybe().Return(int32(1)) nodes = append(nodes, node) @@ -53,7 +53,7 @@ func TestTotalDifficultyNodeSelector(t *testing.T) { t.Run("another best node", func(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) // fifth node is alive (better than 3rd and 4th) - node.On("StateAndLatest").Return(nodeStateAlive, int64(3), big.NewInt(11)) + node.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(11)}) node.On("Order").Maybe().Return(int32(1)) nodes = append(nodes, node) @@ -63,10 +63,10 @@ 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.On("StateAndLatest").Return(nodeStateAlive, int64(-1), nil) + node1.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: -1, TotalDifficulty: nil}) node1.On("Order").Maybe().Return(int32(1)) node2 := newMockNode[types.ID, Head, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, int64(-1), nil) + node2.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: -1, TotalDifficulty: nil}) node2.On("Order").Maybe().Return(int32(1)) nodes := []Node[types.ID, Head, nodeClient]{node1, node2} @@ -85,10 +85,10 @@ func TestTotalDifficultyNodeSelector_None(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) if i == 0 { // first node is out of sync - node.On("StateAndLatest").Return(nodeStateOutOfSync, int64(-1), nil) + node.On("StateAndLatestChainInfo").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: -1, TotalDifficulty: nil}) } else { // others are unreachable - node.On("StateAndLatest").Return(nodeStateUnreachable, int64(1), big.NewInt(7)) + node.On("StateAndLatestChainInfo").Return(nodeStateUnreachable, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(7)}) } nodes = append(nodes, node) } @@ -106,7 +106,7 @@ func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { t.Run("same td and order", func(t *testing.T) { for i := 0; i < 3; i++ { node := newMockNode[types.ID, Head, nodeClient](t) - node.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(10)) + node.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(10)}) node.On("Order").Return(int32(2)) nodes = append(nodes, node) } @@ -117,15 +117,15 @@ func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { t.Run("same td but different order", func(t *testing.T) { node1 := newMockNode[types.ID, Head, nodeClient](t) - node1.On("StateAndLatest").Return(nodeStateAlive, int64(3), big.NewInt(10)) + node1.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(10)}) node1.On("Order").Return(int32(3)) node2 := newMockNode[types.ID, Head, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, int64(3), big.NewInt(10)) + node2.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(10)}) node2.On("Order").Return(int32(1)) node3 := newMockNode[types.ID, Head, nodeClient](t) - node3.On("StateAndLatest").Return(nodeStateAlive, int64(3), big.NewInt(10)) + node3.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(10)}) node3.On("Order").Return(int32(2)) nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3} @@ -136,15 +136,15 @@ func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { t.Run("different td but same order", func(t *testing.T) { node1 := newMockNode[types.ID, Head, nodeClient](t) - node1.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(10)) + node1.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(10)}) node1.On("Order").Maybe().Return(int32(3)) node2 := newMockNode[types.ID, Head, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(11)) + node2.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(11)}) node2.On("Order").Maybe().Return(int32(3)) node3 := newMockNode[types.ID, Head, nodeClient](t) - node3.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(12)) + node3.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(12)}) node3.On("Order").Return(int32(3)) nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3} @@ -155,19 +155,19 @@ func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { t.Run("different head and different order", func(t *testing.T) { node1 := newMockNode[types.ID, Head, nodeClient](t) - node1.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(100)) + node1.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(100)}) node1.On("Order").Maybe().Return(int32(4)) node2 := newMockNode[types.ID, Head, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(110)) + node2.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(110)}) node2.On("Order").Maybe().Return(int32(5)) node3 := newMockNode[types.ID, Head, nodeClient](t) - node3.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(110)) + node3.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(110)}) node3.On("Order").Maybe().Return(int32(1)) node4 := newMockNode[types.ID, Head, nodeClient](t) - node4.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(105)) + node4.On("StateAndLatestChainInfo").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} diff --git a/common/client/node_test.go b/common/client/node_test.go index 85c96145740..8190e822879 100644 --- a/common/client/node_test.go +++ b/common/client/node_test.go @@ -18,6 +18,7 @@ type testNodeConfig struct { selectionMode string syncThreshold uint32 nodeIsSyncingEnabled bool + enforceRepeatableRead bool finalizedBlockPollInterval time.Duration errors config.ClientErrors } @@ -50,6 +51,10 @@ func (n testNodeConfig) Errors() config.ClientErrors { return n.errors } +func (n testNodeConfig) EnforceRepeatableRead() bool { + return n.enforceRepeatableRead +} + type testNode struct { *node[types.ID, Head, NodeClient[types.ID, Head]] } diff --git a/common/client/types.go b/common/client/types.go index 34a90259707..30473d83ecb 100644 --- a/common/client/types.go +++ b/common/client/types.go @@ -74,7 +74,7 @@ type NodeClient[ UnsubscribeAllExceptAliveLoop() IsSyncing(ctx context.Context) (bool, error) LatestFinalizedBlock(ctx context.Context) (HEAD, error) - GetInterceptedChainInfo() (highestBlockNumber, highestLatestFinalizedBlock int64) + GetInterceptedChainInfo() ChainInfo } // clientAPI includes all the direct RPC methods required by the generalized common client to implement its own. @@ -148,3 +148,31 @@ type connection[ Dial(ctx context.Context) error SubscribeNewHead(ctx context.Context, channel chan<- HEAD) (types.Subscription, error) } + +// PoolChainInfoProvider - provides aggregation of nodes pool ChainInfo +// +//go:generate mockery --quiet --name PoolChainInfoProvider --structname mockPoolChainInfoProvider --filename "mock_pool_chain_info_provider_test.go" --inpackage --case=underscore +type PoolChainInfoProvider interface { + // LatestChainInfo - returns number of live nodes available in the pool, so we can prevent the last alive node in a pool from being. + // Return highest latest ChainInfo within the alive nodes. E.g. most recent block number and highest block number + // observed by Node A are 10 and 15; Node B - 12 and 14. This method will return 12. + LatestChainInfo() (int, ChainInfo) + // HighestChainInfo - returns highest ChainInfo ever observed by any node in the pool. + HighestChainInfo() ChainInfo +} + +// ChainInfo - defines RPC's or MultiNode's view on the chain +type ChainInfo struct { + BlockNumber int64 + FinalizedBlockNumber int64 + TotalDifficulty *big.Int +} + +func (c *ChainInfo) SetTotalDifficultyIfGt(td *big.Int) { + if td == nil || (c.TotalDifficulty != nil && c.TotalDifficulty.Cmp(td) >= 0) { + return + } + + // allocate new Int, to prevent read/write race + c.TotalDifficulty = big.NewInt(0).Set(td) +} diff --git a/core/chains/evm/client/helpers_test.go b/core/chains/evm/client/helpers_test.go index 1db8958443c..44ce68fa59f 100644 --- a/core/chains/evm/client/helpers_test.go +++ b/core/chains/evm/client/helpers_test.go @@ -87,6 +87,7 @@ type TestNodePoolConfig struct { NodeIsSyncingEnabledVal bool NodeFinalizedBlockPollInterval time.Duration NodeErrors config.ClientErrors + EnforceRepeatableReadVal bool } func (tc TestNodePoolConfig) PollFailureThreshold() uint32 { return tc.NodePollFailureThreshold } @@ -109,6 +110,10 @@ func (tc TestNodePoolConfig) Errors() config.ClientErrors { return tc.NodeErrors } +func (tc TestNodePoolConfig) EnforceRepeatableRead() bool { + return tc.EnforceRepeatableReadVal +} + func NewClientWithTestNode(t *testing.T, nodePoolCfg config.NodePool, noNewHeadsThreshold time.Duration, rpcUrl string, rpcHTTPURL *url.URL, sendonlyRPCURLs []url.URL, id int32, chainID *big.Int) (*client, error) { parsed, err := url.ParseRequestURI(rpcUrl) if err != nil { diff --git a/core/chains/evm/client/mocks/rpc_client.go b/core/chains/evm/client/mocks/rpc_client.go index 42aa245a69a..8fbb4f169b2 100644 --- a/core/chains/evm/client/mocks/rpc_client.go +++ b/core/chains/evm/client/mocks/rpc_client.go @@ -9,6 +9,8 @@ import ( 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" @@ -443,31 +445,21 @@ func (_m *RPCClient) FilterEvents(ctx context.Context, query ethereum.FilterQuer } // GetInterceptedChainInfo provides a mock function with given fields: -func (_m *RPCClient) GetInterceptedChainInfo() (int64, int64) { +func (_m *RPCClient) GetInterceptedChainInfo() commonclient.ChainInfo { ret := _m.Called() if len(ret) == 0 { panic("no return value specified for GetInterceptedChainInfo") } - var r0 int64 - var r1 int64 - if rf, ok := ret.Get(0).(func() (int64, int64)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() int64); ok { + var r0 commonclient.ChainInfo + if rf, ok := ret.Get(0).(func() commonclient.ChainInfo); ok { r0 = rf() } else { - r0 = ret.Get(0).(int64) + r0 = ret.Get(0).(commonclient.ChainInfo) } - if rf, ok := ret.Get(1).(func() int64); ok { - r1 = rf() - } else { - r1 = ret.Get(1).(int64) - } - - return r0, r1 + return r0 } // HeaderByHash provides a mock function with given fields: ctx, h diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index 7816633237c..6b192e15614 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -56,7 +56,7 @@ type RPCClient interface { 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() (highestBlockNumber, highestLatestFinalizedBlock int64) + GetInterceptedChainInfo() commonclient.ChainInfo } type rpcClient struct { @@ -84,10 +84,7 @@ type rpcClient struct { chStopInFlight chan struct{} // intercepted values seen by callers of the rpcClient. Need to ensure MultiNode provides repeatable read guarantee - // highestBlockNumber - maximum block number ever observed - highestBlockNumber int64 - // highestFinalizedBlockNum - maximum block number ever observed for finalized block - highestFinalizedBlockNum int64 + chainInfo commonclient.ChainInfo } // NewRPCCLient returns a new *rpcClient as commonclient.RPC @@ -1087,7 +1084,8 @@ func (r *rpcClient) oneNewHead(head *evmtypes.Head) { r.stateMu.Lock() defer r.stateMu.Unlock() - r.highestBlockNumber = max(r.highestBlockNumber, head.Number) + r.chainInfo.BlockNumber = max(r.chainInfo.BlockNumber, head.Number) + r.chainInfo.SetTotalDifficultyIfGt(head.TotalDifficulty) } func (r *rpcClient) oneNewFinalizedHead(head *evmtypes.Head) { @@ -1096,12 +1094,11 @@ func (r *rpcClient) oneNewFinalizedHead(head *evmtypes.Head) { } r.stateMu.Lock() defer r.stateMu.Unlock() - r.highestFinalizedBlockNum = max(r.highestFinalizedBlockNum, head.Number) + r.chainInfo.FinalizedBlockNumber = max(r.chainInfo.FinalizedBlockNumber, head.Number) } -func (r *rpcClient) GetInterceptedChainInfo() (highestBlockNumber, highestLatestFinalizedBlock int64) { +func (r *rpcClient) GetInterceptedChainInfo() commonclient.ChainInfo { r.stateMu.RLock() - head, finalized := r.highestBlockNumber, r.highestFinalizedBlockNum - r.stateMu.RUnlock() - return head, finalized + defer r.stateMu.RUnlock() + return r.chainInfo } diff --git a/core/chains/evm/client/rpc_client_test.go b/core/chains/evm/client/rpc_client_test.go index a604c3cf4e5..d0524a26107 100644 --- a/core/chains/evm/client/rpc_client_test.go +++ b/core/chains/evm/client/rpc_client_test.go @@ -71,9 +71,9 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { rpc := client.NewRPCClient(lggr, *server.URL, nil, "rpc", 1, chainId, commonclient.Primary) defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) - maxBlockNumber, maxFinalizedBlockNumber := rpc.GetInterceptedChainInfo() - require.Equal(t, int64(0), maxBlockNumber) - require.Equal(t, int64(0), maxFinalizedBlockNumber) + chainInfo := rpc.GetInterceptedChainInfo() + require.Equal(t, int64(0), chainInfo.BlockNumber) + require.Equal(t, int64(0), chainInfo.FinalizedBlockNumber) server.Head = &evmtypes.Head{ Number: 256, TotalDifficulty: big.NewInt(1000), @@ -84,9 +84,9 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { TotalDifficulty: big.NewInt(1000), } _ = receiveNewHead(rpc) - maxBlockNumber, maxFinalizedBlockNumber = rpc.GetInterceptedChainInfo() - assert.Equal(t, int64(256), maxBlockNumber) - assert.Equal(t, int64(0), maxFinalizedBlockNumber) + chainInfo = rpc.GetInterceptedChainInfo() + assert.Equal(t, int64(256), chainInfo.BlockNumber) + assert.Equal(t, int64(0), chainInfo.FinalizedBlockNumber) }) t.Run("Block's chain ID matched configured", func(t *testing.T) { server := createRPCServer() @@ -218,16 +218,16 @@ func TestRPCClient_LatestFinalizedBlock(t *testing.T) { // updates chain info _, err := rpc.LatestFinalizedBlock(ctx) require.NoError(t, err) - maxBlockNumber, maxFinalizedBlockNumber := rpc.GetInterceptedChainInfo() - assert.Equal(t, int64(0), maxBlockNumber) - assert.Equal(t, int64(128), maxFinalizedBlockNumber) + chainInfo := rpc.GetInterceptedChainInfo() + assert.Equal(t, int64(0), chainInfo.BlockNumber) + assert.Equal(t, int64(128), chainInfo.FinalizedBlockNumber) // lower block number does not update Highest server.Head = &evmtypes.Head{Number: 127} _, err = rpc.LatestFinalizedBlock(ctx) require.NoError(t, err) - maxBlockNumber, maxFinalizedBlockNumber = rpc.GetInterceptedChainInfo() - assert.Equal(t, int64(0), maxBlockNumber) - assert.Equal(t, int64(128), maxFinalizedBlockNumber) + chainInfo = rpc.GetInterceptedChainInfo() + assert.Equal(t, int64(0), chainInfo.BlockNumber) + assert.Equal(t, int64(128), chainInfo.FinalizedBlockNumber) } diff --git a/core/chains/evm/config/chain_scoped.go b/core/chains/evm/config/chain_scoped.go index 8f94fef09f4..c43f56757a6 100644 --- a/core/chains/evm/config/chain_scoped.go +++ b/core/chains/evm/config/chain_scoped.go @@ -179,3 +179,8 @@ func (e *EVMConfig) OperatorFactoryAddress() string { func (e *EVMConfig) LogPrunePageSize() uint32 { return *e.C.LogPrunePageSize } + +func (e *EVMConfig) FinalizedBlockOffset() uint32 { + // TODO: DH implement me + return 0 +} diff --git a/core/chains/evm/config/chain_scoped_node_pool.go b/core/chains/evm/config/chain_scoped_node_pool.go index 7f071e5506f..91efbc5ec02 100644 --- a/core/chains/evm/config/chain_scoped_node_pool.go +++ b/core/chains/evm/config/chain_scoped_node_pool.go @@ -39,3 +39,8 @@ func (n *NodePoolConfig) FinalizedBlockPollInterval() time.Duration { } func (n *NodePoolConfig) Errors() ClientErrors { return &clientErrorsConfig{c: n.C.Errors} } + +func (n *NodePoolConfig) EnforceRepeatableRead() bool { + // TODO: DH implement me + return false +} diff --git a/core/chains/evm/config/config.go b/core/chains/evm/config/config.go index 34de754de21..ba9775ad560 100644 --- a/core/chains/evm/config/config.go +++ b/core/chains/evm/config/config.go @@ -44,6 +44,7 @@ type EVM interface { OperatorFactoryAddress() string RPCDefaultBatchSize() uint32 NodeNoNewHeadsThreshold() time.Duration + FinalizedBlockOffset() uint32 IsEnabled() bool TOMLString() (string, error) @@ -159,6 +160,7 @@ type NodePool interface { NodeIsSyncingEnabled() bool FinalizedBlockPollInterval() time.Duration Errors() ClientErrors + EnforceRepeatableRead() bool } // TODO BCF-2509 does the chainscopedconfig really need the entire app config? From 0a3013790a8e306362e2c7776518c941ad4e5e73 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Tue, 7 May 2024 17:03:17 +0200 Subject: [PATCH 06/53] fix race --- common/client/node_lifecycle.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/client/node_lifecycle.go b/common/client/node_lifecycle.go index 92005e47e63..07f5c93777b 100644 --- a/common/client/node_lifecycle.go +++ b/common/client/node_lifecycle.go @@ -256,7 +256,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { latestFinalizedBN := latestFinalized.BlockNumber() if latestFinalizedBN > localHighestChainInfo.FinalizedBlockNumber { promPoolRPCNodeHighestFinalizedBlock.WithLabelValues(n.chainID.String(), n.name).Set(float64(latestFinalizedBN)) - n.latestChainInfo.FinalizedBlockNumber = latestFinalizedBN + localHighestChainInfo.FinalizedBlockNumber = latestFinalizedBN } } From 1431d776cc1c3dbf793b79d31a1ddb9014cf0f7d Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Wed, 8 May 2024 21:36:47 +0200 Subject: [PATCH 07/53] minor refactor --- common/client/node_fsm.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/common/client/node_fsm.go b/common/client/node_fsm.go index 95961ad7b81..d15657505fd 100644 --- a/common/client/node_fsm.go +++ b/common/client/node_fsm.go @@ -131,29 +131,29 @@ func (n *node[CHAIN_ID, HEAD, RPC]) getCachedState() nodeState { } func (n *node[CHAIN_ID, HEAD, RPC]) recalculateState() nodeState { - if n.state != nodeStateAlive || !n.nodePoolCfg.EnforceRepeatableRead() { + if n.state != nodeStateAlive { return n.state } // double check that node is not lagging on finalized block - if n.isFinalizedStateOutOfSync() { + if n.nodePoolCfg.EnforceRepeatableRead() && n.isFinalizedBlockOutOfSync() { return nodeStateFinalizedBlockOutOfSync } return nodeStateAlive } -func (n *node[CHAIN_ID, HEAD, RPC]) isFinalizedStateOutOfSync() bool { +func (n *node[CHAIN_ID, HEAD, RPC]) isFinalizedBlockOutOfSync() bool { if n.poolInfoProvider == nil { return false } - highest := n.poolInfoProvider.HighestChainInfo() + observedByCaller := n.poolInfoProvider.HighestChainInfo() if n.chainCfg.FinalityTagEnabled() { - return n.latestChainInfo.FinalizedBlockNumber < highest.FinalizedBlockNumber-int64(n.chainCfg.FinalizedBlockOffset()) + return n.latestChainInfo.FinalizedBlockNumber < observedByCaller.FinalizedBlockNumber-int64(n.chainCfg.FinalizedBlockOffset()) } - return n.latestChainInfo.BlockNumber < highest.BlockNumber-int64(n.chainCfg.FinalizedBlockOffset()) + return n.latestChainInfo.BlockNumber < observedByCaller.BlockNumber-int64(n.chainCfg.FinalizedBlockOffset()) } // StateAndLatestChainInfo returns nodeState with the latest ChainInfo observed by Node during current lifecycle. From a1b8b6d2f92bafaadddf0b14abae82fddc512c00 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Fri, 10 May 2024 13:58:24 +0200 Subject: [PATCH 08/53] Added FinalizedBlockOffset to config --- common/headtracker/types/config.go | 1 + core/chains/evm/config/chain_scoped.go | 3 +- core/chains/evm/config/toml/config.go | 1 + core/chains/evm/config/toml/defaults.go | 3 + .../evm/config/toml/defaults/fallback.toml | 1 + core/chains/evm/headtracker/config.go | 1 + core/config/docs/chains-evm.toml | 4 ++ core/services/chainlink/config_test.go | 8 ++- .../chainlink/testdata/config-full.toml | 1 + .../config-multi-chain-effective.toml | 3 + .../testdata/config-multi-chain.toml | 1 + core/web/resolver/testdata/config-full.toml | 1 + .../config-multi-chain-effective.toml | 3 + .../resolver/testdata/config-multi-chain.toml | 1 + docs/CONFIG.md | 67 +++++++++++++++++++ .../disk-based-logging-disabled.txtar | 1 + .../validate/disk-based-logging-no-dir.txtar | 1 + .../node/validate/disk-based-logging.txtar | 1 + testdata/scripts/node/validate/invalid.txtar | 1 + testdata/scripts/node/validate/valid.txtar | 1 + 20 files changed, 100 insertions(+), 4 deletions(-) diff --git a/common/headtracker/types/config.go b/common/headtracker/types/config.go index 019aa9847d9..42df1aebd0b 100644 --- a/common/headtracker/types/config.go +++ b/common/headtracker/types/config.go @@ -6,6 +6,7 @@ type Config interface { BlockEmissionIdleWarningThreshold() time.Duration FinalityDepth() uint32 FinalityTagEnabled() bool + FinalizedBlockOffset() uint32 } type HeadTrackerConfig interface { diff --git a/core/chains/evm/config/chain_scoped.go b/core/chains/evm/config/chain_scoped.go index c43f56757a6..ee3c61236de 100644 --- a/core/chains/evm/config/chain_scoped.go +++ b/core/chains/evm/config/chain_scoped.go @@ -181,6 +181,5 @@ func (e *EVMConfig) LogPrunePageSize() uint32 { } func (e *EVMConfig) FinalizedBlockOffset() uint32 { - // TODO: DH implement me - return 0 + return *e.C.FinalizedBlockOffset } diff --git a/core/chains/evm/config/toml/config.go b/core/chains/evm/config/toml/config.go index 1b1baf41094..23a0c6c166d 100644 --- a/core/chains/evm/config/toml/config.go +++ b/core/chains/evm/config/toml/config.go @@ -362,6 +362,7 @@ type Chain struct { OperatorFactoryAddress *types.EIP55Address RPCDefaultBatchSize *uint32 RPCBlockQueryDelay *uint16 + FinalizedBlockOffset *uint32 Transactions Transactions `toml:",omitempty"` BalanceMonitor BalanceMonitor `toml:",omitempty"` diff --git a/core/chains/evm/config/toml/defaults.go b/core/chains/evm/config/toml/defaults.go index 951246eeb22..503780b999e 100644 --- a/core/chains/evm/config/toml/defaults.go +++ b/core/chains/evm/config/toml/defaults.go @@ -164,6 +164,9 @@ func (c *Chain) SetFrom(f *Chain) { if v := f.RPCBlockQueryDelay; v != nil { c.RPCBlockQueryDelay = v } + if v := f.FinalizedBlockOffset; v != nil { + c.FinalizedBlockOffset = v + } c.Transactions.setFrom(&f.Transactions) c.BalanceMonitor.setFrom(&f.BalanceMonitor) diff --git a/core/chains/evm/config/toml/defaults/fallback.toml b/core/chains/evm/config/toml/defaults/fallback.toml index d65d0a1b0c1..8d963e685c4 100644 --- a/core/chains/evm/config/toml/defaults/fallback.toml +++ b/core/chains/evm/config/toml/defaults/fallback.toml @@ -14,6 +14,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false diff --git a/core/chains/evm/headtracker/config.go b/core/chains/evm/headtracker/config.go index 85fe084470d..8a2807489cf 100644 --- a/core/chains/evm/headtracker/config.go +++ b/core/chains/evm/headtracker/config.go @@ -13,6 +13,7 @@ type Config interface { BlockEmissionIdleWarningThreshold() time.Duration FinalityDepth() uint32 FinalityTagEnabled() bool + FinalizedBlockOffset() uint32 } type HeadTrackerConfig interface { diff --git a/core/config/docs/chains-evm.toml b/core/config/docs/chains-evm.toml index 49360caad21..15c1f7d3e6d 100644 --- a/core/config/docs/chains-evm.toml +++ b/core/config/docs/chains-evm.toml @@ -87,6 +87,10 @@ RPCDefaultBatchSize = 250 # Default # available from the connected node via RPC, due to race conditions in the code of the remote ETH node. In this case you will get false # "zero" blocks that are missing transactions. RPCBlockQueryDelay = 1 # Default +# FinalizedBlockOffset defines the number of blocks by which the latest finalized block will be shifted. E.g if RPC returns block 100 as latest finalized, node will treat block `100 - FinalizedBlockOffset` as the latest finalized block. +# In case of `FinalityTagEnabled = false` Node will treat block `latest - FinalityDepth - FinalizedBlockOffset` as the latest finalized block. +# With `EnforceRepeatableRead = true`, RPC is considered healthy only if its most recent finalized block is larger or equal to the highest finalized block observed by the Node minus `FinalizedBlockOffset`. +FinalizedBlockOffset = 0 # Default [EVM.Transactions] # ForwardersEnabled enables or disables sending transactions through forwarder contracts. diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 11d286fbcd5..2486ae67b71 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -25,6 +25,7 @@ import ( "github.com/smartcontractkit/chainlink-solana/pkg/solana" solcfg "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" stkcfg "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" + commonconfig "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" @@ -101,8 +102,9 @@ var ( { ChainID: ubig.NewI(1), Chain: evmcfg.Chain{ - FinalityDepth: ptr[uint32](26), - FinalityTagEnabled: ptr[bool](false), + FinalityDepth: ptr[uint32](26), + FinalityTagEnabled: ptr[bool](false), + FinalizedBlockOffset: ptr[uint32](12), }, Nodes: []*evmcfg.Node{ { @@ -499,6 +501,7 @@ func TestConfig_Marshal(t *testing.T) { FinalityDepth: ptr[uint32](42), FinalityTagEnabled: ptr[bool](false), FlagsContractAddress: mustAddress("0xae4E781a6218A8031764928E88d457937A954fC3"), + FinalizedBlockOffset: ptr[uint32](16), GasEstimator: evmcfg.GasEstimator{ Mode: ptr("SuggestedPrice"), @@ -979,6 +982,7 @@ NoNewHeadsThreshold = '1m0s' OperatorFactoryAddress = '0xa5B85635Be42F21f94F28034B7DA440EeFF0F418' RPCDefaultBatchSize = 17 RPCBlockQueryDelay = 10 +FinalizedBlockOffset = 16 [EVM.Transactions] ForwardersEnabled = true diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index b199ae530f5..8fcc26a121f 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -282,6 +282,7 @@ NoNewHeadsThreshold = '1m0s' OperatorFactoryAddress = '0xa5B85635Be42F21f94F28034B7DA440EeFF0F418' RPCDefaultBatchSize = 17 RPCBlockQueryDelay = 10 +FinalizedBlockOffset = 16 [EVM.Transactions] ForwardersEnabled = true diff --git a/core/services/chainlink/testdata/config-multi-chain-effective.toml b/core/services/chainlink/testdata/config-multi-chain-effective.toml index 7aa3bb50b35..d2e3edc7467 100644 --- a/core/services/chainlink/testdata/config-multi-chain-effective.toml +++ b/core/services/chainlink/testdata/config-multi-chain-effective.toml @@ -269,6 +269,7 @@ NoNewHeadsThreshold = '3m0s' OperatorFactoryAddress = '0x3E64Cd889482443324F91bFA9c84fE72A511f48A' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 12 [EVM.Transactions] ForwardersEnabled = false @@ -360,6 +361,7 @@ NoNewHeadsThreshold = '3m0s' OperatorFactoryAddress = '0x8007e24251b1D2Fc518Eb843A701d9cD21fe0aA3' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [EVM.Transactions] ForwardersEnabled = false @@ -445,6 +447,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 100 RPCBlockQueryDelay = 10 +FinalizedBlockOffset = 0 [EVM.Transactions] ForwardersEnabled = false diff --git a/core/services/chainlink/testdata/config-multi-chain.toml b/core/services/chainlink/testdata/config-multi-chain.toml index e45255a4373..5373e0e62d3 100644 --- a/core/services/chainlink/testdata/config-multi-chain.toml +++ b/core/services/chainlink/testdata/config-multi-chain.toml @@ -39,6 +39,7 @@ CPUProfileRate = 7 ChainID = '1' FinalityDepth = 26 FinalityTagEnabled = false +FinalizedBlockOffset = 12 [[EVM.Nodes]] Name = 'primary' diff --git a/core/web/resolver/testdata/config-full.toml b/core/web/resolver/testdata/config-full.toml index 75fad4d2fc9..630bdf6d5da 100644 --- a/core/web/resolver/testdata/config-full.toml +++ b/core/web/resolver/testdata/config-full.toml @@ -282,6 +282,7 @@ NoNewHeadsThreshold = '1m0s' OperatorFactoryAddress = '0xa5B85635Be42F21f94F28034B7DA440EeFF0F418' RPCDefaultBatchSize = 17 RPCBlockQueryDelay = 10 +FinalizedBlockOffset = 0 [EVM.Transactions] ForwardersEnabled = true diff --git a/core/web/resolver/testdata/config-multi-chain-effective.toml b/core/web/resolver/testdata/config-multi-chain-effective.toml index 7aa3bb50b35..eada9a7a56c 100644 --- a/core/web/resolver/testdata/config-multi-chain-effective.toml +++ b/core/web/resolver/testdata/config-multi-chain-effective.toml @@ -269,6 +269,7 @@ NoNewHeadsThreshold = '3m0s' OperatorFactoryAddress = '0x3E64Cd889482443324F91bFA9c84fE72A511f48A' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [EVM.Transactions] ForwardersEnabled = false @@ -360,6 +361,7 @@ NoNewHeadsThreshold = '3m0s' OperatorFactoryAddress = '0x8007e24251b1D2Fc518Eb843A701d9cD21fe0aA3' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [EVM.Transactions] ForwardersEnabled = false @@ -445,6 +447,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 100 RPCBlockQueryDelay = 10 +FinalizedBlockOffset = 0 [EVM.Transactions] ForwardersEnabled = false diff --git a/core/web/resolver/testdata/config-multi-chain.toml b/core/web/resolver/testdata/config-multi-chain.toml index 3598e92cdc2..9abb1719402 100644 --- a/core/web/resolver/testdata/config-multi-chain.toml +++ b/core/web/resolver/testdata/config-multi-chain.toml @@ -39,6 +39,7 @@ CPUProfileRate = 7 ChainID = '1' FinalityDepth = 26 FinalityTagEnabled = false +FinalizedBlockOffset = 0 [EVM.OCR2] [EVM.OCR2.Automation] diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 61ba9256d9d..a75f126dbe8 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -1749,6 +1749,7 @@ NoNewHeadsThreshold = '3m0s' OperatorFactoryAddress = '0x3E64Cd889482443324F91bFA9c84fE72A511f48A' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -1834,6 +1835,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -1919,6 +1921,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -2004,6 +2007,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -2090,6 +2094,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '40s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -2175,6 +2180,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -2260,6 +2266,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -2346,6 +2353,7 @@ NoNewHeadsThreshold = '3m0s' OperatorFactoryAddress = '0x8007e24251b1D2Fc518Eb843A701d9cD21fe0aA3' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -2431,6 +2439,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 2 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -2515,6 +2524,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -2599,6 +2609,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -2684,6 +2695,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 2 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -2770,6 +2782,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -2855,6 +2868,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 2 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -2940,6 +2954,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 100 RPCBlockQueryDelay = 10 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -3025,6 +3040,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '12m0s' RPCDefaultBatchSize = 100 RPCBlockQueryDelay = 15 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -3110,6 +3126,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '6m0s' RPCDefaultBatchSize = 100 RPCBlockQueryDelay = 15 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -3195,6 +3212,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 2 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -3280,6 +3298,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '40s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -3365,6 +3384,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '1m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -3450,6 +3470,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '1m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -3535,6 +3556,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '1m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -3621,6 +3643,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '40s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -3706,6 +3729,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -3790,6 +3814,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -3875,6 +3900,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -3959,6 +3985,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '6m0s' RPCDefaultBatchSize = 100 RPCBlockQueryDelay = 15 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -4044,6 +4071,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -4129,6 +4157,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -4213,6 +4242,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -4297,6 +4327,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '12m0s' RPCDefaultBatchSize = 100 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -4382,6 +4413,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '40s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -4466,6 +4498,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '12m0s' RPCDefaultBatchSize = 100 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -4551,6 +4584,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 2 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -4635,6 +4669,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -4720,6 +4755,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '40s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -4805,6 +4841,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -4891,6 +4928,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -4976,6 +5014,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '1m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -5061,6 +5100,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 2 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -5146,6 +5186,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 2 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -5231,6 +5272,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '1m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -5315,6 +5357,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -5399,6 +5442,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -5483,6 +5527,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -5568,6 +5613,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -5653,6 +5699,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 100 RPCBlockQueryDelay = 10 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -5737,6 +5784,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 100 RPCBlockQueryDelay = 10 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -5822,6 +5870,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '40s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -5907,6 +5956,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '40s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -5993,6 +6043,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -6079,6 +6130,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -6164,6 +6216,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -6249,6 +6302,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -6334,6 +6388,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -6419,6 +6474,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -6504,6 +6560,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '40s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -6589,6 +6646,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -6674,6 +6732,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '30s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [Transactions] ForwardersEnabled = false @@ -6913,6 +6972,14 @@ block, but it is possible to receive a head BEFORE that block is actually available from the connected node via RPC, due to race conditions in the code of the remote ETH node. In this case you will get false "zero" blocks that are missing transactions. +### FinalizedBlockOffset +```toml +FinalizedBlockOffset = 0 # Default +``` +FinalizedBlockOffset defines the number of blocks by which the latest finalized block will be shifted. E.g if RPC returns block 100 as latest finalized, node will treat block `100 - FinalizedBlockOffset` as the latest finalized block. +In case of `FinalityTagEnabled = false` Node will treat block `latest - FinalityDepth - FinalizedBlockOffset` as the latest finalized block. +With `EnforceRepeatableRead = true`, RPC is considered healthy only if its most recent finalized block is larger or equal to the highest finalized block observed by the Node minus `FinalizedBlockOffset`. + ## EVM.Transactions ```toml [EVM.Transactions] diff --git a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar index feaf546f022..e84155a399d 100644 --- a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar @@ -325,6 +325,7 @@ NoNewHeadsThreshold = '3m0s' OperatorFactoryAddress = '0x3E64Cd889482443324F91bFA9c84fE72A511f48A' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [EVM.Transactions] ForwardersEnabled = false diff --git a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar index b37fed41150..95f42dc810d 100644 --- a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar @@ -325,6 +325,7 @@ NoNewHeadsThreshold = '3m0s' OperatorFactoryAddress = '0x3E64Cd889482443324F91bFA9c84fE72A511f48A' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [EVM.Transactions] ForwardersEnabled = false diff --git a/testdata/scripts/node/validate/disk-based-logging.txtar b/testdata/scripts/node/validate/disk-based-logging.txtar index 6ae02ab38f4..333250ad217 100644 --- a/testdata/scripts/node/validate/disk-based-logging.txtar +++ b/testdata/scripts/node/validate/disk-based-logging.txtar @@ -325,6 +325,7 @@ NoNewHeadsThreshold = '3m0s' OperatorFactoryAddress = '0x3E64Cd889482443324F91bFA9c84fE72A511f48A' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [EVM.Transactions] ForwardersEnabled = false diff --git a/testdata/scripts/node/validate/invalid.txtar b/testdata/scripts/node/validate/invalid.txtar index df0118bbbbf..96f45f5769a 100644 --- a/testdata/scripts/node/validate/invalid.txtar +++ b/testdata/scripts/node/validate/invalid.txtar @@ -315,6 +315,7 @@ NoNewHeadsThreshold = '3m0s' OperatorFactoryAddress = '0x3E64Cd889482443324F91bFA9c84fE72A511f48A' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [EVM.Transactions] ForwardersEnabled = false diff --git a/testdata/scripts/node/validate/valid.txtar b/testdata/scripts/node/validate/valid.txtar index edb07fd5e4f..e7ec931fac0 100644 --- a/testdata/scripts/node/validate/valid.txtar +++ b/testdata/scripts/node/validate/valid.txtar @@ -322,6 +322,7 @@ NoNewHeadsThreshold = '3m0s' OperatorFactoryAddress = '0x3E64Cd889482443324F91bFA9c84fE72A511f48A' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [EVM.Transactions] ForwardersEnabled = false From c7a38cd0f21fbb521243b0c8b5ecb15d7e5976b8 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Fri, 10 May 2024 13:58:57 +0200 Subject: [PATCH 09/53] HeadTracker support of FinalizedBlockOffset --- common/client/node_lifecycle_test.go | 4 +- common/headtracker/head_saver.go | 2 + common/headtracker/head_tracker.go | 95 ++++++++---- common/headtracker/mocks/head_tracker.go | 38 ++++- core/chains/evm/headtracker/head_saver.go | 5 + .../chains/evm/headtracker/head_saver_test.go | 5 + core/chains/evm/headtracker/head_tracker.go | 5 +- .../evm/headtracker/head_tracker_test.go | 146 ++++++++++++++---- core/chains/evm/headtracker/heads.go | 23 ++- core/chains/evm/headtracker/heads_test.go | 27 ++++ core/chains/evm/headtracker/mocks/config.go | 18 +++ 11 files changed, 300 insertions(+), 68 deletions(-) diff --git a/common/client/node_lifecycle_test.go b/common/client/node_lifecycle_test.go index fea1605baa2..b28e9979b87 100644 --- a/common/client/node_lifecycle_test.go +++ b/common/client/node_lifecycle_test.go @@ -1653,7 +1653,7 @@ func TestNode_State(t *testing.T) { ExpectedState: nodeStateAlive, }, { - Name: "If finality lag does not exceeds offset, returns nodeStateFinalizedBlockOutOfSync (FinalityDepth)", + Name: "If finality lag exceeds offset, returns nodeStateFinalizedBlockOutOfSync (FinalityDepth)", FinalizedBlockOffsetVal: 15, PoolChainInfo: ChainInfo{ BlockNumber: 20, @@ -1664,7 +1664,7 @@ func TestNode_State(t *testing.T) { ExpectedState: nodeStateFinalizedBlockOutOfSync, }, { - Name: "If finality lag does not exceeds offset, returns nodeStateFinalizedBlockOutOfSync (FinalityTag)", + Name: "If finality lag exceeds offset, returns nodeStateFinalizedBlockOutOfSync (FinalityTag)", FinalizedBlockOffsetVal: 15, IsFinalityTagEnabled: true, PoolChainInfo: ChainInfo{ diff --git a/common/headtracker/head_saver.go b/common/headtracker/head_saver.go index 6f461c61225..561396a337a 100644 --- a/common/headtracker/head_saver.go +++ b/common/headtracker/head_saver.go @@ -20,4 +20,6 @@ type HeadSaver[H types.Head[BLOCK_HASH], BLOCK_HASH types.Hashable] interface { Chain(hash BLOCK_HASH) H // MarkFinalized - marks matching block and all it's direct ancestors as finalized MarkFinalized(ctx context.Context, latestFinalized H) error + // ChainWithLatestFinalized - returns highest block, whose ancestor is marked as finalized + ChainWithLatestFinalized() (H, error) } diff --git a/common/headtracker/head_tracker.go b/common/headtracker/head_tracker.go index bc7a4910b39..6821f397c7b 100644 --- a/common/headtracker/head_tracker.go +++ b/common/headtracker/head_tracker.go @@ -40,8 +40,10 @@ const HeadsBufferSize = 10 type HeadTracker[H types.Head[BLOCK_HASH], BLOCK_HASH types.Hashable] interface { services.Service // Backfill given a head will fill in any missing heads up to latestFinalized - Backfill(ctx context.Context, headWithChain, latestFinalized H) (err error) + Backfill(ctx context.Context, headWithChain H) (err error) LatestChain() H + // ChainWithLatestFinalized - returns highest block, whose ancestor is marked as finalized + ChainWithLatestFinalized() (H, error) } type headTracker[ @@ -195,7 +197,12 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) HealthReport() map[string]error { return report } -func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) Backfill(ctx context.Context, headWithChain, latestFinalized HTH) (err error) { +func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) Backfill(ctx context.Context, headWithChain HTH) (err error) { + latestFinalized, err := ht.calculateLatestFinalized(ctx, headWithChain) + if err != nil { + return fmt.Errorf("failed to calculate finalized block: %w", err) + } + if !latestFinalized.IsValid() { return errors.New("can not perform backfill without a valid latestFinalized head") } @@ -316,13 +323,7 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) backfillLoop() { break } { - latestFinalized, err := ht.calculateLatestFinalized(ctx, head) - if err != nil { - ht.log.Warnw("Failed to calculate finalized block", "err", err) - continue - } - - err = ht.Backfill(ctx, head, latestFinalized) + err := ht.Backfill(ctx, head) if err != nil { ht.log.Warnw("Unexpected error while backfilling heads", "err", err) } else if ctx.Err() != nil { @@ -334,24 +335,69 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) backfillLoop() { } } +func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) LatestAndFinalizedBlock(ctx context.Context) (latest, finalized HTH, err error) { + latest, err = ht.client.HeadByNumber(ctx, nil) + if err != nil { + err = fmt.Errorf("failed to get latest block: %w", err) + return + } + + finalized, err = ht.calculateLatestFinalized(ctx, latest) + if err != nil { + err = fmt.Errorf("failded to calculate latest finalized block: %w", err) + return + } + + return +} + // calculateLatestFinalized - returns latest finalized block. It's expected that currentHeadNumber - is the head of // canonical chain. There is no guaranties that returned block belongs to the canonical chain. Additional verification // must be performed before usage. -func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) calculateLatestFinalized(ctx context.Context, currentHead HTH) (h HTH, err error) { +func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) calculateLatestFinalized(ctx context.Context, currentHead HTH) (HTH, error) { if ht.config.FinalityTagEnabled() { - return ht.client.LatestFinalizedBlock(ctx) + latestFinalized, err := ht.client.LatestFinalizedBlock(ctx) + if err != nil || !latestFinalized.IsValid() || ht.config.FinalizedBlockOffset() == 0 { + return latestFinalized, err + } + + finalizedBlockNumber := max(latestFinalized.BlockNumber()-int64(ht.config.FinalizedBlockOffset()), 0) + finalizedWithOffset, _, err := ht.fetchAncestorAtHeight(ctx, latestFinalized, finalizedBlockNumber) + return finalizedWithOffset, err } // no need to make an additional RPC call on chains with instant finality - if ht.config.FinalityDepth() == 0 { + if ht.config.FinalityDepth() == 0 && ht.config.FinalizedBlockOffset() == 0 { return currentHead, nil } - finalizedBlockNumber := currentHead.BlockNumber() - int64(ht.config.FinalityDepth()) + finalizedBlockNumber := currentHead.BlockNumber() - int64(ht.config.FinalityDepth()) - int64(ht.config.FinalizedBlockOffset()) if finalizedBlockNumber <= 0 { finalizedBlockNumber = 0 } return ht.client.HeadByNumber(ctx, big.NewInt(finalizedBlockNumber)) } +func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) fetchAncestorAtHeight(ctx context.Context, head HTH, baseHeight int64) (ancestor HTH, fetched int, err error) { + for i := head.BlockNumber() - 1; i >= baseHeight; i-- { + // NOTE: Sequential requests here mean it's a potential performance bottleneck, be aware! + existingHead := ht.headSaver.Chain(head.GetParentHash()) + if existingHead.IsValid() { + head = existingHead + continue + } + var err error + head, err = ht.fetchAndSaveHead(ctx, i, head.GetParentHash()) + fetched++ + if ctx.Err() != nil { + ht.log.Debugw("context canceled, aborting backfill", "err", err, "ctx.Err", ctx.Err()) + return ancestor, fetched, fmt.Errorf("fetchAndSaveHead failed: %w", ctx.Err()) + } else if err != nil { + return ancestor, fetched, fmt.Errorf("fetchAndSaveHead failed: %w", err) + } + } + + return head, fetched, nil +} + // backfill fetches all missing heads up until the latestFinalizedHead func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) backfill(ctx context.Context, head, latestFinalizedHead HTH) (err error) { headBlockNumber := head.BlockNumber() @@ -374,21 +420,9 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) backfill(ctx context.Context, hea "err", err) }() - for i := head.BlockNumber() - 1; i >= baseHeight; i-- { - // NOTE: Sequential requests here mean it's a potential performance bottleneck, be aware! - existingHead := ht.headSaver.Chain(head.GetParentHash()) - if existingHead.IsValid() { - head = existingHead - continue - } - head, err = ht.fetchAndSaveHead(ctx, i, head.GetParentHash()) - fetched++ - if ctx.Err() != nil { - ht.log.Debugw("context canceled, aborting backfill", "err", err, "ctx.Err", ctx.Err()) - return fmt.Errorf("fetchAndSaveHead failed: %w", ctx.Err()) - } else if err != nil { - return fmt.Errorf("fetchAndSaveHead failed: %w", err) - } + head, fetched, err = ht.fetchAncestorAtHeight(ctx, head, baseHeight) + if err != nil { + return fmt.Errorf("failed to fetch ancestor with height %d: %w", baseHeight, err) } if head.BlockHash() != latestFinalizedHead.BlockHash() { @@ -426,3 +460,8 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) fetchAndSaveHead(ctx context.Cont } return head, nil } + +// ChainWithLatestFinalized - returns highest block, whose ancestor is marked as finalized +func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) ChainWithLatestFinalized() (HTH, error) { + return ht.headSaver.ChainWithLatestFinalized() +} diff --git a/common/headtracker/mocks/head_tracker.go b/common/headtracker/mocks/head_tracker.go index 2321c3bfdca..c467986c946 100644 --- a/common/headtracker/mocks/head_tracker.go +++ b/common/headtracker/mocks/head_tracker.go @@ -15,17 +15,17 @@ type HeadTracker[H types.Head[BLOCK_HASH], BLOCK_HASH types.Hashable] struct { mock.Mock } -// Backfill provides a mock function with given fields: ctx, headWithChain, latestFinalized -func (_m *HeadTracker[H, BLOCK_HASH]) Backfill(ctx context.Context, headWithChain H, latestFinalized H) error { - ret := _m.Called(ctx, headWithChain, latestFinalized) +// Backfill provides a mock function with given fields: ctx, headWithChain +func (_m *HeadTracker[H, BLOCK_HASH]) Backfill(ctx context.Context, headWithChain H) error { + ret := _m.Called(ctx, headWithChain) if len(ret) == 0 { panic("no return value specified for Backfill") } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, H, H) error); ok { - r0 = rf(ctx, headWithChain, latestFinalized) + if rf, ok := ret.Get(0).(func(context.Context, H) error); ok { + r0 = rf(ctx, headWithChain) } else { r0 = ret.Error(0) } @@ -33,6 +33,34 @@ func (_m *HeadTracker[H, BLOCK_HASH]) Backfill(ctx context.Context, headWithChai return r0 } +// ChainWithLatestFinalized provides a mock function with given fields: +func (_m *HeadTracker[H, BLOCK_HASH]) ChainWithLatestFinalized() (H, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for ChainWithLatestFinalized") + } + + var r0 H + var r1 error + if rf, ok := ret.Get(0).(func() (H, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() H); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(H) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // Close provides a mock function with given fields: func (_m *HeadTracker[H, BLOCK_HASH]) Close() error { ret := _m.Called() diff --git a/core/chains/evm/headtracker/head_saver.go b/core/chains/evm/headtracker/head_saver.go index 8913d2dec21..ca69ab7de96 100644 --- a/core/chains/evm/headtracker/head_saver.go +++ b/core/chains/evm/headtracker/head_saver.go @@ -86,6 +86,10 @@ func (hs *headSaver) MarkFinalized(ctx context.Context, finalized *evmtypes.Head return hs.orm.TrimOldHeads(ctx, minBlockToKeep) } +func (hs *headSaver) ChainWithLatestFinalized() (*evmtypes.Head, error) { + return hs.heads.ChainWithLatestFinalized() +} + var NullSaver httypes.HeadSaver = &nullSaver{} type nullSaver struct{} @@ -100,3 +104,4 @@ func (*nullSaver) Chain(hash common.Hash) *evmtypes.Head func (*nullSaver) MarkFinalized(ctx context.Context, latestFinalized *evmtypes.Head) error { return nil } +func (*nullSaver) ChainWithLatestFinalized() (*evmtypes.Head, error) { return nil, nil } diff --git a/core/chains/evm/headtracker/head_saver_test.go b/core/chains/evm/headtracker/head_saver_test.go index e53ea0cd629..910191d2fd6 100644 --- a/core/chains/evm/headtracker/head_saver_test.go +++ b/core/chains/evm/headtracker/head_saver_test.go @@ -40,6 +40,7 @@ type config struct { finalityDepth uint32 blockEmissionIdleWarningThreshold time.Duration finalityTagEnabled bool + finalizedBlockOffset uint32 } func (c *config) FinalityDepth() uint32 { return c.finalityDepth } @@ -51,6 +52,10 @@ func (c *config) FinalityTagEnabled() bool { return c.finalityTagEnabled } +func (c *config) FinalizedBlockOffset() uint32 { + return c.finalizedBlockOffset +} + type saverOpts struct { headTrackerConfig *headTrackerConfig } diff --git a/core/chains/evm/headtracker/head_tracker.go b/core/chains/evm/headtracker/head_tracker.go index 414dba23833..fa69e02b2fb 100644 --- a/core/chains/evm/headtracker/head_tracker.go +++ b/core/chains/evm/headtracker/head_tracker.go @@ -48,7 +48,8 @@ func (*nullTracker) Ready() error { return nil } func (*nullTracker) HealthReport() map[string]error { return map[string]error{} } func (*nullTracker) Name() string { return "" } func (*nullTracker) SetLogLevel(zapcore.Level) {} -func (*nullTracker) Backfill(ctx context.Context, headWithChain, latestFinalized *evmtypes.Head) (err error) { +func (*nullTracker) Backfill(ctx context.Context, headWithChain *evmtypes.Head) (err error) { return nil } -func (*nullTracker) LatestChain() *evmtypes.Head { return nil } +func (*nullTracker) LatestChain() *evmtypes.Head { return nil } +func (*nullTracker) ChainWithLatestFinalized() (*evmtypes.Head, error) { return nil, nil } diff --git a/core/chains/evm/headtracker/head_tracker_test.go b/core/chains/evm/headtracker/head_tracker_test.go index b8bdb1f5703..587855813d1 100644 --- a/core/chains/evm/headtracker/head_tracker_test.go +++ b/core/chains/evm/headtracker/head_tracker_test.go @@ -814,10 +814,17 @@ func TestHeadTracker_Backfill(t *testing.T) { ctx := testutils.Context(t) type opts struct { - Heads []evmtypes.Head + Heads []evmtypes.Head + FinalityTagEnabled bool + FinalizedBlockOffset uint32 + FinalityDepth uint32 } newHeadTrackerUniverse := func(t *testing.T, opts opts) *headTrackerUniverse { - cfg := configtest.NewGeneralConfig(t, nil) + cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, secrets *chainlink.Secrets) { + c.EVM[0].FinalityTagEnabled = ptr(opts.FinalityTagEnabled) + c.EVM[0].FinalizedBlockOffset = ptr(opts.FinalizedBlockOffset) + c.EVM[0].FinalityDepth = ptr(opts.FinalityDepth) + }) evmcfg := evmtest.NewChainScopedConfig(t, cfg) db := pgtest.NewSqlxDB(t) @@ -833,26 +840,37 @@ func TestHeadTracker_Backfill(t *testing.T) { return ht } + t.Run("returns error if failed to get latestFinalized block", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true}) + const expectedError = "failed to fetch latest finalized block" + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(nil, errors.New(expectedError)).Once() + + err := htu.headTracker.Backfill(ctx, &h12) + require.ErrorContains(t, err, expectedError) + }) t.Run("returns error if latestFinalized is not valid", func(t *testing.T) { - htu := newHeadTrackerUniverse(t, opts{}) + htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true}) + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(nil, nil).Once() - err := htu.headTracker.Backfill(ctx, &h12, nil) + err := htu.headTracker.Backfill(ctx, &h12) require.EqualError(t, err, "can not perform backfill without a valid latestFinalized head") }) t.Run("Returns error if finalized head is ahead of canonical", func(t *testing.T) { - htu := newHeadTrackerUniverse(t, opts{}) + htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true}) + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&h14Orphaned, nil).Once() - err := htu.headTracker.Backfill(ctx, &h12, &h14Orphaned) + err := htu.headTracker.Backfill(ctx, &h12) require.EqualError(t, err, "invariant violation: expected head of canonical chain to be ahead of the latestFinalized") }) t.Run("Returns error if finalizedHead is not present in the canonical chain", func(t *testing.T) { - htu := newHeadTrackerUniverse(t, opts{Heads: heads}) + htu := newHeadTrackerUniverse(t, opts{Heads: heads, FinalityTagEnabled: true}) + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&h14Orphaned, nil).Once() - err := htu.headTracker.Backfill(ctx, &h15, &h14Orphaned) + err := htu.headTracker.Backfill(ctx, &h15) require.EqualError(t, err, "expected finalized block to be present in canonical chain") }) t.Run("Marks all blocks in chain that are older than finalized", func(t *testing.T) { - htu := newHeadTrackerUniverse(t, opts{Heads: heads}) + htu := newHeadTrackerUniverse(t, opts{Heads: heads, FinalityTagEnabled: true}) assertFinalized := func(expectedFinalized bool, msg string, heads ...evmtypes.Head) { for _, h := range heads { @@ -861,18 +879,20 @@ func TestHeadTracker_Backfill(t *testing.T) { } } - err := htu.headTracker.Backfill(ctx, &h15, &h14) + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&h14, nil).Once() + err := htu.headTracker.Backfill(ctx, &h15) require.NoError(t, err) assertFinalized(true, "expected heads to be marked as finalized after backfill", h14, h13, h12, h11) assertFinalized(false, "expected heads to remain unfinalized", h15, head10) }) t.Run("fetches a missing head", func(t *testing.T) { - htu := newHeadTrackerUniverse(t, opts{Heads: heads}) + htu := newHeadTrackerUniverse(t, opts{Heads: heads, FinalityTagEnabled: true}) + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&h9, nil).Once() htu.ethClient.On("HeadByHash", mock.Anything, head10.Hash). Return(&head10, nil) - err := htu.headTracker.Backfill(ctx, &h12, &h9) + err := htu.headTracker.Backfill(ctx, &h12) require.NoError(t, err) h := htu.headSaver.Chain(h12.Hash) @@ -889,16 +909,16 @@ func TestHeadTracker_Backfill(t *testing.T) { require.NoError(t, err) assert.Equal(t, int64(10), writtenHead.Number) }) - t.Run("fetches only heads that are missing", func(t *testing.T) { - htu := newHeadTrackerUniverse(t, opts{Heads: heads}) + htu := newHeadTrackerUniverse(t, opts{Heads: heads, FinalityTagEnabled: true}) + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&head8, nil).Once() htu.ethClient.On("HeadByHash", mock.Anything, head10.Hash). Return(&head10, nil) htu.ethClient.On("HeadByHash", mock.Anything, head8.Hash). Return(&head8, nil) - err := htu.headTracker.Backfill(ctx, &h15, &head8) + err := htu.headTracker.Backfill(ctx, &h15) require.NoError(t, err) h := htu.headSaver.Chain(h15.Hash) @@ -910,7 +930,8 @@ func TestHeadTracker_Backfill(t *testing.T) { }) t.Run("abandons backfill and returns error if the eth node returns not found", func(t *testing.T) { - htu := newHeadTrackerUniverse(t, opts{Heads: heads}) + htu := newHeadTrackerUniverse(t, opts{Heads: heads, FinalityTagEnabled: true}) + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&head8, nil).Once() htu.ethClient.On("HeadByHash", mock.Anything, head10.Hash). Return(&head10, nil). Once() @@ -918,9 +939,9 @@ func TestHeadTracker_Backfill(t *testing.T) { Return(nil, ethereum.NotFound). Once() - err := htu.headTracker.Backfill(ctx, &h12, &head8) + err := htu.headTracker.Backfill(ctx, &h12) require.Error(t, err) - require.EqualError(t, err, "fetchAndSaveHead failed: not found") + require.ErrorContains(t, err, "fetchAndSaveHead failed: not found") h := htu.headSaver.Chain(h12.Hash) @@ -930,7 +951,8 @@ func TestHeadTracker_Backfill(t *testing.T) { }) t.Run("abandons backfill and returns error if the context time budget is exceeded", func(t *testing.T) { - htu := newHeadTrackerUniverse(t, opts{Heads: heads}) + htu := newHeadTrackerUniverse(t, opts{Heads: heads, FinalityTagEnabled: true}) + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&head8, nil).Once() htu.ethClient.On("HeadByHash", mock.Anything, head10.Hash). Return(&head10, nil) lctx, cancel := context.WithCancel(ctx) @@ -939,9 +961,9 @@ func TestHeadTracker_Backfill(t *testing.T) { cancel() }) - err := htu.headTracker.Backfill(lctx, &h12, &head8) + err := htu.headTracker.Backfill(lctx, &h12) require.Error(t, err) - require.EqualError(t, err, "fetchAndSaveHead failed: context canceled") + require.ErrorContains(t, err, "fetchAndSaveHead failed: context canceled") h := htu.headSaver.Chain(h12.Hash) @@ -949,17 +971,17 @@ func TestHeadTracker_Backfill(t *testing.T) { assert.Equal(t, 4, int(h.ChainLength())) assert.Equal(t, int64(9), h.EarliestInChain().BlockNumber()) }) - t.Run("abandons backfill and returns error when fetching a block by hash fails, indicating a reorg", func(t *testing.T) { - htu := newHeadTrackerUniverse(t, opts{}) + htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true}) + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&h11, nil).Once() htu.ethClient.On("HeadByHash", mock.Anything, h14.Hash).Return(&h14, nil).Once() htu.ethClient.On("HeadByHash", mock.Anything, h13.Hash).Return(&h13, nil).Once() htu.ethClient.On("HeadByHash", mock.Anything, h12.Hash).Return(nil, errors.New("not found")).Once() - err := htu.headTracker.Backfill(ctx, &h15, &h11) + err := htu.headTracker.Backfill(ctx, &h15) require.Error(t, err) - require.EqualError(t, err, "fetchAndSaveHead failed: not found") + require.ErrorContains(t, err, "fetchAndSaveHead failed: not found") h := htu.headSaver.Chain(h14.Hash) @@ -968,9 +990,10 @@ func TestHeadTracker_Backfill(t *testing.T) { assert.Equal(t, int64(13), h.EarliestInChain().BlockNumber()) }) t.Run("marks head as finalized, if latestHead = finalizedHead (0 finality depth)", func(t *testing.T) { - htu := newHeadTrackerUniverse(t, opts{Heads: []evmtypes.Head{h15}}) + htu := newHeadTrackerUniverse(t, opts{Heads: []evmtypes.Head{h15}, FinalityTagEnabled: true}) finalizedH15 := h15 // copy h15 to have different addresses - err := htu.headTracker.Backfill(ctx, &h15, &finalizedH15) + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&finalizedH15, nil).Once() + err := htu.headTracker.Backfill(ctx, &h15) require.NoError(t, err) h := htu.headSaver.LatestChain() @@ -981,6 +1004,71 @@ func TestHeadTracker_Backfill(t *testing.T) { assert.Equal(t, h15.BlockNumber(), h.BlockNumber()) assert.Equal(t, h15.Hash, h.Hash) }) + t.Run("marks block as finalized according to FinalizedBlockOffset (finality tag)", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{Heads: []evmtypes.Head{h15}, FinalityTagEnabled: true, FinalizedBlockOffset: 2}) + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&h14, nil).Once() + // calculateLatestFinalizedBlock fetches blocks down to the FinalizedBlockOffset + htu.ethClient.On("HeadByHash", mock.Anything, h13.Hash).Return(&h13, nil).Once() + htu.ethClient.On("HeadByHash", mock.Anything, h12.Hash).Return(&h12, nil).Once() + // backfill from 15 to 12 + htu.ethClient.On("HeadByHash", mock.Anything, h14.Hash).Return(&h14, nil).Once() + err := htu.headTracker.Backfill(ctx, &h15) + require.NoError(t, err) + + h := htu.headSaver.LatestChain() + // h - must contain 15, 14, 13, 12 and only 12 is finalized + assert.Equal(t, 4, int(h.ChainLength())) + for ; h.Hash != h12.Hash; h = h.Parent { + assert.False(t, h.IsFinalized) + } + + assert.True(t, h.IsFinalized) + assert.Equal(t, h12.BlockNumber(), h.BlockNumber()) + assert.Equal(t, h12.Hash, h.Hash) + }) + t.Run("marks block as finalized according to FinalizedBlockOffset (finality depth)", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{Heads: []evmtypes.Head{h15}, FinalityDepth: 1, FinalizedBlockOffset: 2}) + htu.ethClient.On("HeadByNumber", mock.Anything, big.NewInt(12)).Return(&h12, nil).Once() + + // backfill from 15 to 12 + htu.ethClient.On("HeadByHash", mock.Anything, h14.Hash).Return(&h14, nil).Once() + htu.ethClient.On("HeadByHash", mock.Anything, h13.Hash).Return(&h13, nil).Once() + htu.ethClient.On("HeadByHash", mock.Anything, h12.Hash).Return(&h12, nil).Once() + err := htu.headTracker.Backfill(ctx, &h15) + require.NoError(t, err) + + h := htu.headSaver.LatestChain() + // h - must contain 15, 14, 13, 12 and only 12 is finalized + assert.Equal(t, 4, int(h.ChainLength())) + for ; h.Hash != h12.Hash; h = h.Parent { + assert.False(t, h.IsFinalized) + } + + assert.True(t, h.IsFinalized) + assert.Equal(t, h12.BlockNumber(), h.BlockNumber()) + assert.Equal(t, h12.Hash, h.Hash) + }) + t.Run("marks block as finalized according to FinalizedBlockOffset even with instant finality", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{Heads: []evmtypes.Head{h15}, FinalityDepth: 0, FinalizedBlockOffset: 2}) + htu.ethClient.On("HeadByNumber", mock.Anything, big.NewInt(13)).Return(&h13, nil).Once() + + // backfill from 15 to 13 + htu.ethClient.On("HeadByHash", mock.Anything, h14.Hash).Return(&h14, nil).Once() + htu.ethClient.On("HeadByHash", mock.Anything, h13.Hash).Return(&h13, nil).Once() + err := htu.headTracker.Backfill(ctx, &h15) + require.NoError(t, err) + + h := htu.headSaver.LatestChain() + // h - must contain 15, 14, 13, only 13 is finalized + assert.Equal(t, 3, int(h.ChainLength())) + for ; h.Hash != h13.Hash; h = h.Parent { + assert.False(t, h.IsFinalized) + } + + assert.True(t, h.IsFinalized) + assert.Equal(t, h13.BlockNumber(), h.BlockNumber()) + assert.Equal(t, h13.Hash, h.Hash) + }) } func createHeadTracker(t *testing.T, ethClient *evmclimocks.Client, config headtracker.Config, htConfig headtracker.HeadTrackerConfig, orm headtracker.ORM) *headTrackerUniverse { @@ -1031,8 +1119,8 @@ type headTrackerUniverse struct { ethClient *evmclimocks.Client } -func (u *headTrackerUniverse) Backfill(ctx context.Context, head, finalizedHead *evmtypes.Head) error { - return u.headTracker.Backfill(ctx, head, finalizedHead) +func (u *headTrackerUniverse) Backfill(ctx context.Context, head *evmtypes.Head) error { + return u.headTracker.Backfill(ctx, head) } func (u *headTrackerUniverse) Start(t *testing.T) { diff --git a/core/chains/evm/headtracker/heads.go b/core/chains/evm/headtracker/heads.go index 1edfb3e3788..b847782f4b5 100644 --- a/core/chains/evm/headtracker/heads.go +++ b/core/chains/evm/headtracker/heads.go @@ -1,6 +1,7 @@ package headtracker import ( + "errors" "sort" "sync" @@ -23,11 +24,15 @@ type Heads interface { // MarkFinalized - finds `finalized` in the LatestHead and marks it and all direct ancestors as finalized. // Trims old blocks whose height is smaller than minBlockToKeep MarkFinalized(finalized common.Hash, minBlockToKeep int64) bool + // ChainWithLatestFinalized - returns highest block, whose ancestor is marked as finalized, + // or error if such block is not available + ChainWithLatestFinalized() (*evmtypes.Head, error) } type heads struct { - heads []*evmtypes.Head - mu sync.RWMutex + heads []*evmtypes.Head + headWithLatestFinalized *evmtypes.Head + mu sync.RWMutex } func NewHeads() Heads { @@ -44,6 +49,16 @@ func (h *heads) LatestHead() *evmtypes.Head { return h.heads[0] } +func (h *heads) ChainWithLatestFinalized() (*evmtypes.Head, error) { + h.mu.RLock() + defer h.mu.RUnlock() + + if h.headWithLatestFinalized == nil { + return nil, errors.New("latest finalized block is not available yet") + } + return h.headWithLatestFinalized, nil +} + func (h *heads) HeadByHash(hash common.Hash) *evmtypes.Head { h.mu.RLock() defer h.mu.RUnlock() @@ -89,6 +104,10 @@ func (h *heads) MarkFinalized(finalized common.Hash, minBlockToKeep int64) bool head = head.Parent } + if foundFinalized { + h.headWithLatestFinalized = h.heads[0] + } + return foundFinalized } diff --git a/core/chains/evm/headtracker/heads_test.go b/core/chains/evm/headtracker/heads_test.go index 4241b462363..e1a74dbd457 100644 --- a/core/chains/evm/headtracker/heads_test.go +++ b/core/chains/evm/headtracker/heads_test.go @@ -129,6 +129,8 @@ func TestHeads_MarkFinalized(t *testing.T) { allHeads := []*evmtypes.Head{h0, h1, h1Uncle, h2, h2Uncle, h3, h4, h5} heads.AddHeads(allHeads...) + _, err := heads.ChainWithLatestFinalized() + require.NotNil(t, err, "expected error, if there is no finalized block") // mark h3 and all ancestors as finalized require.True(t, heads.MarkFinalized(h3.Hash, h1.BlockNumber()), "expected MarkFinalized succeed") @@ -160,4 +162,29 @@ func TestHeads_MarkFinalized(t *testing.T) { heads.AddHeads(h0, h1, h2, h2Uncle, h3, h4, h5) t.Run("blocks remain finalized after re adding them to the Heads", ensureProperFinalization) + // ChainWithLatestFinalized + containsFinalized := func(head *evmtypes.Head) bool { + for ; head != nil; head = head.Parent { + if head.IsFinalized { + return true + } + } + + return false + } + + chainWithFinalized, err := heads.ChainWithLatestFinalized() + require.NoError(t, err) + assert.True(t, containsFinalized(chainWithFinalized), "Expected finalized head to be present in the chain") + // add new head with gap + h6 := newHead(6, h5.Hash) + h7 := newHead(7, h6.Hash) + heads.AddHeads(h7) + assert.Equal(t, int64(7), heads.LatestHead().BlockNumber()) + assert.False(t, containsFinalized(heads.LatestHead()), "Expected latest head not to be connect to the finalized") + chainWithFinalized, err = heads.ChainWithLatestFinalized() + require.NoError(t, err) + assert.Equal(t, int64(5), chainWithFinalized.Number) + assert.True(t, containsFinalized(chainWithFinalized), "Expected finalized head to be still available in the chain") + } diff --git a/core/chains/evm/headtracker/mocks/config.go b/core/chains/evm/headtracker/mocks/config.go index fea7d4629b5..05f923d17a7 100644 --- a/core/chains/evm/headtracker/mocks/config.go +++ b/core/chains/evm/headtracker/mocks/config.go @@ -67,6 +67,24 @@ func (_m *Config) FinalityTagEnabled() bool { return r0 } +// FinalizedBlockOffset provides a mock function with given fields: +func (_m *Config) FinalizedBlockOffset() uint32 { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for FinalizedBlockOffset") + } + + var r0 uint32 + if rf, ok := ret.Get(0).(func() uint32); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(uint32) + } + + return r0 +} + // NewConfig creates a new instance of Config. 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 NewConfig(t interface { From 6a2e4be67ab69e01644d48dfa4b7b2a159107c04 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Fri, 10 May 2024 15:25:24 +0200 Subject: [PATCH 10/53] fix lint issues --- core/chains/evm/client/rpc_client_test.go | 1 - core/chains/evm/client/sub_forwarder_test.go | 1 - 2 files changed, 2 deletions(-) diff --git a/core/chains/evm/client/rpc_client_test.go b/core/chains/evm/client/rpc_client_test.go index d0524a26107..4566f3943e7 100644 --- a/core/chains/evm/client/rpc_client_test.go +++ b/core/chains/evm/client/rpc_client_test.go @@ -229,5 +229,4 @@ func TestRPCClient_LatestFinalizedBlock(t *testing.T) { chainInfo = rpc.GetInterceptedChainInfo() assert.Equal(t, int64(0), chainInfo.BlockNumber) assert.Equal(t, int64(128), chainInfo.FinalizedBlockNumber) - } diff --git a/core/chains/evm/client/sub_forwarder_test.go b/core/chains/evm/client/sub_forwarder_test.go index 7f7cacb38fc..d249793a567 100644 --- a/core/chains/evm/client/sub_forwarder_test.go +++ b/core/chains/evm/client/sub_forwarder_test.go @@ -179,6 +179,5 @@ func TestSubscriptionErrorWrapper(t *testing.T) { _, ok = <-wrapper.Err() return !ok }) - }) } From a377ccbf7b53a18eafe17847ef7543a765b46b76 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Fri, 10 May 2024 19:40:17 +0200 Subject: [PATCH 11/53] switch logpoller to head tracker --- .../evm/forwarders/forwarder_manager_test.go | 7 +- .../evm/headtracker/simulated_head_tracker.go | 49 ++++++++ core/chains/evm/logpoller/helper_test.go | 6 +- core/chains/evm/logpoller/log_poller.go | 50 ++++---- .../evm/logpoller/log_poller_internal_test.go | 115 ++++++------------ core/chains/evm/logpoller/log_poller_test.go | 29 ++++- core/chains/evm/txmgr/txmgr_test.go | 7 +- core/chains/legacyevm/chain.go | 2 +- ...annel_definition_cache_integration_test.go | 12 +- .../v21/logprovider/integration_test.go | 5 +- .../promreporter/prom_reporter_test.go | 6 +- core/services/relay/evm/chain_reader_test.go | 6 +- core/services/relay/evm/config_poller_test.go | 6 +- .../relay/evm/functions/config_poller_test.go | 6 +- .../relay/evm/mercury/helpers_test.go | 6 +- .../vrf/v2/listener_v2_log_listener_test.go | 6 +- 16 files changed, 192 insertions(+), 126 deletions(-) create mode 100644 core/chains/evm/headtracker/simulated_head_tracker.go diff --git a/core/chains/evm/forwarders/forwarder_manager_test.go b/core/chains/evm/forwarders/forwarder_manager_test.go index 3a515e7ab39..99b93aaa0e1 100644 --- a/core/chains/evm/forwarders/forwarder_manager_test.go +++ b/core/chains/evm/forwarders/forwarder_manager_test.go @@ -18,6 +18,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" @@ -70,7 +71,8 @@ func TestFwdMgr_MaybeForwardTransaction(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), evmClient, lggr, lpOpts) + ht := headtracker.NewSimulatedHeadTracker(ctx, evmClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), evmClient, lggr, ht, lpOpts) fwdMgr := forwarders.NewFwdMgr(db, evmClient, lp, lggr, evmcfg.EVM()) fwdMgr.ORM = forwarders.NewORM(db) @@ -131,7 +133,8 @@ func TestFwdMgr_AccountUnauthorizedToForward_SkipsForwarding(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), evmClient, lggr, lpOpts) + ht := headtracker.NewSimulatedHeadTracker(ctx, evmClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), evmClient, lggr, ht, lpOpts) fwdMgr := forwarders.NewFwdMgr(db, evmClient, lp, lggr, evmcfg.EVM()) fwdMgr.ORM = forwarders.NewORM(db) diff --git a/core/chains/evm/headtracker/simulated_head_tracker.go b/core/chains/evm/headtracker/simulated_head_tracker.go new file mode 100644 index 00000000000..79792d6bd3b --- /dev/null +++ b/core/chains/evm/headtracker/simulated_head_tracker.go @@ -0,0 +1,49 @@ +package headtracker + +import ( + "context" + "fmt" + "math/big" + + evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" +) + +// simulatedHeadTracker - simplified version of HeadTracker that works with simulated backed +type simulatedHeadTracker struct { + ec evmclient.Client + useFinalityTag bool + finalityDepth int64 + ctx context.Context +} + +func NewSimulatedHeadTracker(ctx context.Context, ec evmclient.Client, useFinalityTag bool, finalityDepth int64) *simulatedHeadTracker { + return &simulatedHeadTracker{ + ec: ec, + useFinalityTag: useFinalityTag, + finalityDepth: finalityDepth, + ctx: ctx, + } +} + +func (ht *simulatedHeadTracker) ChainWithLatestFinalized() (*evmtypes.Head, error) { + latestHead, err := ht.ec.HeadByNumber(ht.ctx, nil) + if err != nil { + return nil, err + } + + var finalizedBlock *evmtypes.Head + if ht.useFinalityTag { + finalizedBlock, err = ht.ec.LatestFinalizedBlock(ht.ctx) + } else { + finalizedBlock, err = ht.ec.HeadByNumber(ht.ctx, big.NewInt(max(latestHead.Number-ht.finalityDepth, 0))) + } + + if err != nil { + return nil, fmt.Errorf("simulatedHeadTracker failed to get finalized block") + } + + finalizedBlock.IsFinalized = true + latestHead.Parent = finalizedBlock + return latestHead, nil +} diff --git a/core/chains/evm/logpoller/helper_test.go b/core/chains/evm/logpoller/helper_test.go index 3b2a10df6c8..2b9a2f1c709 100644 --- a/core/chains/evm/logpoller/helper_test.go +++ b/core/chains/evm/logpoller/helper_test.go @@ -10,6 +10,8 @@ import ( pkgerrors "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" @@ -23,6 +25,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -68,10 +71,11 @@ func SetupTH(t testing.TB, opts logpoller.Opts) TestHarness { head := esc.Backend().Blockchain().CurrentHeader() esc.Backend().Blockchain().SetFinalized(head) + headTracker := headtracker.NewSimulatedHeadTracker(tests.Context(t), esc, opts.UseFinalityTag, opts.FinalityDepth) if opts.PollPeriod == 0 { opts.PollPeriod = 1 * time.Hour } - lp := logpoller.NewLogPoller(o, esc, lggr, opts) + lp := logpoller.NewLogPoller(o, esc, lggr, headTracker, opts) emitterAddress1, _, emitter1, err := log_emitter.DeployLogEmitter(owner, ec) require.NoError(t, err) emitterAddress2, _, emitter2, err := log_emitter.DeployLogEmitter(owner, ec) diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index b1d7d1da623..1512abea059 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -27,6 +27,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types/query" "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink-common/pkg/utils/mathutil" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" @@ -87,6 +88,12 @@ type Client interface { ConfiguredChainID() *big.Int } +//go:generate mockery --quiet --name HeadTracker --output ./ --case=underscore --structname HeadTracker --filename head_tracker_mock.go +type HeadTracker interface { + // ChainWithLatestFinalized - returns highest block, whose ancestor is marked as finalized + ChainWithLatestFinalized() (*evmtypes.Head, error) +} + var ( _ LogPollerTest = &logPoller{} ErrReplayRequestAborted = pkgerrors.New("aborted, replay request cancelled") @@ -99,6 +106,7 @@ type logPoller struct { services.StateMachine ec Client orm ORM + headTracker HeadTracker lggr logger.SugaredLogger pollPeriod time.Duration // poll period set by block production rate useFinalityTag bool // indicates whether logPoller should use chain's finality or pick a fixed depth for finality @@ -150,13 +158,14 @@ type Opts struct { // // How fast that can be done depends largely on network speed and DB, but even for the fastest // support chain, polygon, which has 2s block times, we need RPCs roughly with <= 500ms latency -func NewLogPoller(orm ORM, ec Client, lggr logger.Logger, opts Opts) *logPoller { +func NewLogPoller(orm ORM, ec Client, lggr logger.Logger, headTracker HeadTracker, opts Opts) *logPoller { ctx, cancel := context.WithCancel(context.Background()) return &logPoller{ ctx: ctx, cancel: cancel, ec: ec, orm: orm, + headTracker: headTracker, lggr: logger.Sugared(logger.Named(lggr, "LogPoller")), replayStart: make(chan int64), replayComplete: make(chan error), @@ -582,7 +591,7 @@ func (lp *logPoller) run() { } // Otherwise this is the first poll _ever_ on a new chain. // Only safe thing to do is to start at the first finalized block. - latestBlock, latestFinalizedBlockNumber, err := lp.latestBlocks(lp.ctx) + latestBlock, latestFinalizedBlockNumber, err := lp.latestBlocks() if err != nil { lp.lggr.Warnw("Unable to get latest for first poll", "err", err) continue @@ -702,7 +711,7 @@ func (lp *logPoller) BackupPollAndSaveLogs(ctx context.Context) { lp.backupPollerNextBlock = mathutil.Max(backupStartBlock, 0) } - _, latestFinalizedBlockNumber, err := lp.latestBlocks(ctx) + _, latestFinalizedBlockNumber, err := lp.latestBlocks() if err != nil { lp.lggr.Warnw("Backup logpoller failed to get latest block", "err", err) return @@ -919,7 +928,7 @@ func (lp *logPoller) PollAndSaveLogs(ctx context.Context, currentBlockNumber int lp.lggr.Debugw("Polling for logs", "currentBlockNumber", currentBlockNumber) // Intentionally not using logPoller.finalityDepth directly but the latestFinalizedBlockNumber returned from lp.latestBlocks() // latestBlocks knows how to pick a proper latestFinalizedBlockNumber based on the logPoller's configuration - latestBlock, latestFinalizedBlockNumber, err := lp.latestBlocks(ctx) + latestBlock, latestFinalizedBlockNumber, err := lp.latestBlocks() if err != nil { lp.lggr.Warnw("Unable to get latestBlockNumber block", "err", err, "currentBlockNumber", currentBlockNumber) return @@ -1003,33 +1012,16 @@ func (lp *logPoller) PollAndSaveLogs(ctx context.Context, currentBlockNumber int } } -// Returns information about latestBlock, latestFinalizedBlockNumber -// If finality tag is not enabled, latestFinalizedBlockNumber is calculated as latestBlockNumber - lp.finalityDepth (configured param) -// Otherwise, we return last finalized block number returned from chain -func (lp *logPoller) latestBlocks(ctx context.Context) (*evmtypes.Head, int64, error) { - // If finality is not enabled, we can only fetch the latest block - if !lp.useFinalityTag { - // Example: - // finalityDepth = 2 - // Blocks: 1->2->3->4->5(latestBlock) - // latestFinalizedBlockNumber would be 3 - latestBlock, err := lp.ec.HeadByNumber(ctx, nil) - if err != nil { - return nil, 0, err - } - // If chain has fewer blocks than finalityDepth, return 0 - return latestBlock, mathutil.Max(latestBlock.Number-lp.finalityDepth, 0), nil - } - - // If finality is enabled, we need to get the latest and finalized blocks. - blocks, err := lp.batchFetchBlocks(ctx, []string{rpc.LatestBlockNumber.String(), rpc.FinalizedBlockNumber.String()}, 2) +// Returns information about latestBlock, latestFinalizedBlockNumber provided by HeadTracker +func (lp *logPoller) latestBlocks() (*evmtypes.Head, int64, error) { + chain, err := lp.headTracker.ChainWithLatestFinalized() if err != nil { - return nil, 0, err + return nil, 0, fmt.Errorf("failed to get chain with latest finalized block from HeadTracker: %w", err) } - latest := blocks[0] - finalized := blocks[1] - lp.lggr.Debugw("Latest blocks read from chain", "latest", latest.Number, "finalized", finalized.Number) - return latest, finalized.Number, nil + + finalized := chain.LatestFinalizedHead() + lp.lggr.Debugw("Latest blocks read from chain", "latest", chain.Number, "finalized", finalized.BlockNumber()) + return chain, finalized.BlockNumber(), nil } // Find the first place where our chain and their chain have the same block, diff --git a/core/chains/evm/logpoller/log_poller_internal_test.go b/core/chains/evm/logpoller/log_poller_internal_test.go index b7dbb074568..632f302866d 100644 --- a/core/chains/evm/logpoller/log_poller_internal_test.go +++ b/core/chains/evm/logpoller/log_poller_internal_test.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "math/big" - "reflect" "strings" "sync" "testing" @@ -26,6 +25,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + htMocks "github.com/smartcontractkit/chainlink/v2/common/headtracker/mocks" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" @@ -73,7 +73,7 @@ func TestLogPoller_RegisterFilter(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := NewLogPoller(orm, nil, lggr, lpOpts) + lp := NewLogPoller(orm, nil, lggr, nil, lpOpts) // We expect a zero Filter if nothing registered yet. f := lp.Filter(nil, nil, nil) @@ -208,8 +208,9 @@ func TestLogPoller_BackupPollerStartup(t *testing.T) { db := pgtest.NewSqlxDB(t) orm := NewORM(chainID, db, lggr) latestBlock := int64(4) + const finalityDepth = 2 - head := evmtypes.Head{Number: latestBlock} + head := evmtypes.Head{Number: latestBlock, Parent: &evmtypes.Head{Number: latestBlock - finalityDepth, IsFinalized: true}} events := []common.Hash{EmitterABI.Events["Log1"].ID} log1 := types.Log{ Index: 0, @@ -222,20 +223,22 @@ func TestLogPoller_BackupPollerStartup(t *testing.T) { } ec := evmclimocks.NewClient(t) - ec.On("HeadByNumber", mock.Anything, mock.Anything).Return(&head, nil) ec.On("FilterLogs", mock.Anything, mock.Anything).Return([]types.Log{log1}, nil) ec.On("ConfiguredChainID").Return(chainID, nil) + headTracker := htMocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) + headTracker.On("ChainWithLatestFinalized").Return(&head, nil) + ctx := testutils.Context(t) lpOpts := Opts{ PollPeriod: time.Hour, - FinalityDepth: 2, + FinalityDepth: finalityDepth, BackfillBatchSize: 3, RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, BackupPollerBlockDelay: 0, } - lp := NewLogPoller(orm, ec, lggr, lpOpts) + lp := NewLogPoller(orm, ec, lggr, headTracker, lpOpts) lp.BackupPollAndSaveLogs(ctx) assert.Equal(t, int64(0), lp.backupPollerNextBlock) assert.Equal(t, 1, observedLogs.FilterMessageSnippet("ran before first successful log poller run").Len()) @@ -310,7 +313,15 @@ func TestLogPoller_Replay(t *testing.T) { KeepFinalizedBlocksDepth: 20, BackupPollerBlockDelay: 0, } - lp := NewLogPoller(orm, ec, lggr, lpOpts) + headTracker := htMocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) + + headTracker.On("ChainWithLatestFinalized").Return(func() (*evmtypes.Head, error) { + headCopy := head + finalized := &evmtypes.Head{Number: headCopy.Number - lpOpts.FinalityDepth, IsFinalized: true} + headCopy.Parent = finalized + return &headCopy, nil + }) + lp := NewLogPoller(orm, ec, lggr, headTracker, lpOpts) // process 1 log in block 3 lp.PollAndSaveLogs(ctx, 4) @@ -518,10 +529,6 @@ func (lp *logPoller) reset() { func Test_latestBlockAndFinalityDepth(t *testing.T) { lggr := logger.Test(t) - chainID := testutils.FixtureChainID - db := pgtest.NewSqlxDB(t) - orm := NewORM(chainID, db, lggr) - ctx := testutils.Context(t) lpOpts := Opts{ PollPeriod: time.Hour, @@ -530,71 +537,27 @@ func Test_latestBlockAndFinalityDepth(t *testing.T) { KeepFinalizedBlocksDepth: 20, } - t.Run("pick latest block from chain and use finality from config with finality disabled", func(t *testing.T) { - head := evmtypes.Head{Number: 4} - - lpOpts.UseFinalityTag = false - lpOpts.FinalityDepth = int64(3) - ec := evmclimocks.NewClient(t) - ec.On("HeadByNumber", mock.Anything, mock.Anything).Return(&head, nil) + t.Run("headTracker returns an error", func(t *testing.T) { + headTracker := htMocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) + const expectedError = "finalized block is not available yet" + headTracker.On("ChainWithLatestFinalized").Return(&evmtypes.Head{}, fmt.Errorf(expectedError)) - lp := NewLogPoller(orm, ec, lggr, lpOpts) - latestBlock, lastFinalizedBlockNumber, err := lp.latestBlocks(ctx) - require.NoError(t, err) - require.Equal(t, latestBlock.Number, head.Number) - require.Equal(t, lpOpts.FinalityDepth, latestBlock.Number-lastFinalizedBlockNumber) + lp := NewLogPoller(nil, nil, lggr, headTracker, lpOpts) + _, _, err := lp.latestBlocks() + require.ErrorContains(t, err, expectedError) }) - - t.Run("finality tags in use", func(t *testing.T) { - t.Run("client returns data properly", func(t *testing.T) { - expectedLatestBlockNumber := int64(20) - expectedLastFinalizedBlockNumber := int64(12) - ec := evmclimocks.NewClient(t) - ec.On("BatchCallContext", mock.Anything, mock.MatchedBy(func(b []rpc.BatchElem) bool { - return len(b) == 2 && - reflect.DeepEqual(b[0].Args, []interface{}{"latest", false}) && - reflect.DeepEqual(b[1].Args, []interface{}{"finalized", false}) - })).Return(nil).Run(func(args mock.Arguments) { - elems := args.Get(1).([]rpc.BatchElem) - // Latest block details - *(elems[0].Result.(*evmtypes.Head)) = evmtypes.Head{Number: expectedLatestBlockNumber, Hash: utils.RandomBytes32()} - // Finalized block details - *(elems[1].Result.(*evmtypes.Head)) = evmtypes.Head{Number: expectedLastFinalizedBlockNumber, Hash: utils.RandomBytes32()} - }) - - lpOpts.UseFinalityTag = true - lp := NewLogPoller(orm, ec, lggr, lpOpts) - - latestBlock, lastFinalizedBlockNumber, err := lp.latestBlocks(ctx) - require.NoError(t, err) - require.Equal(t, expectedLatestBlockNumber, latestBlock.Number) - require.Equal(t, expectedLastFinalizedBlockNumber, lastFinalizedBlockNumber) - }) - - t.Run("client returns error for at least one of the calls", func(t *testing.T) { - ec := evmclimocks.NewClient(t) - ec.On("BatchCallContext", mock.Anything, mock.Anything).Return(nil).Run(func(args mock.Arguments) { - elems := args.Get(1).([]rpc.BatchElem) - // Latest block details - *(elems[0].Result.(*evmtypes.Head)) = evmtypes.Head{Number: 10} - // Finalized block details - elems[1].Error = fmt.Errorf("some error") - }) - - lpOpts.UseFinalityTag = true - lp := NewLogPoller(orm, ec, lggr, lpOpts) - _, _, err := lp.latestBlocks(ctx) - require.Error(t, err) - }) - - t.Run("BatchCall returns an error", func(t *testing.T) { - ec := evmclimocks.NewClient(t) - ec.On("BatchCallContext", mock.Anything, mock.Anything).Return(fmt.Errorf("some error")) - lpOpts.UseFinalityTag = true - lp := NewLogPoller(orm, ec, lggr, lpOpts) - _, _, err := lp.latestBlocks(ctx) - require.Error(t, err) - }) + t.Run("headTracker returns valid chain", func(t *testing.T) { + headTracker := htMocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) + finalizedBlock := &evmtypes.Head{Number: 2, IsFinalized: true} + head := &evmtypes.Head{Number: 10, Parent: &evmtypes.Head{Number: 9, Parent: finalizedBlock}} + headTracker.On("ChainWithLatestFinalized").Return(head, nil) + + lp := NewLogPoller(nil, nil, lggr, headTracker, lpOpts) + latestBlock, finalizedBlockNumber, err := lp.latestBlocks() + require.NoError(t, err) + require.NotNil(t, latestBlock) + assert.Equal(t, head.BlockNumber(), latestBlock.BlockNumber()) + assert.Equal(t, finalizedBlock.Number, finalizedBlockNumber) }) } @@ -638,7 +601,7 @@ func Test_FetchBlocks(t *testing.T) { errors.New("Received unfinalized block 9 while expecting finalized block (latestFinalizedBlockNumber = 5)"), }} - lp := NewLogPoller(orm, ec, lggr, lpOpts) + lp := NewLogPoller(orm, ec, lggr, nil, lpOpts) for _, tc := range cases { for _, lp.useFinalityTag = range []bool{false, true} { blockValidationReq := latestBlock @@ -678,7 +641,7 @@ func benchmarkFilter(b *testing.B, nFilters, nAddresses, nEvents int) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := NewLogPoller(nil, nil, lggr, lpOpts) + lp := NewLogPoller(nil, nil, lggr, nil, lpOpts) for i := 0; i < nFilters; i++ { var addresses []common.Address var events []common.Hash diff --git a/core/chains/evm/logpoller/log_poller_test.go b/core/chains/evm/logpoller/log_poller_test.go index 6ef16921503..4cfcafcd8b5 100644 --- a/core/chains/evm/logpoller/log_poller_test.go +++ b/core/chains/evm/logpoller/log_poller_test.go @@ -25,10 +25,14 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-common/pkg/logger" commonutils "github.com/smartcontractkit/chainlink-common/pkg/utils" + htMocks "github.com/smartcontractkit/chainlink/v2/common/headtracker/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" @@ -717,7 +721,9 @@ func TestLogPoller_SynchronizedWithGeth(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(orm, client.NewSimulatedBackendClient(t, ec, chainID), lggr, lpOpts) + simulatedClient := client.NewSimulatedBackendClient(t, ec, chainID) + ht := headtracker.NewSimulatedHeadTracker(tests.Context(t), simulatedClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(orm, simulatedClient, lggr, ht, lpOpts) for i := 0; i < finalityDepth; i++ { // Have enough blocks that we could reorg the full finalityDepth-1. ec.Commit() } @@ -1493,7 +1499,7 @@ func TestLogPoller_DBErrorHandling(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(o, client.NewSimulatedBackendClient(t, ec, chainID2), lggr, lpOpts) + lp := logpoller.NewLogPoller(o, client.NewSimulatedBackendClient(t, ec, chainID2), lggr, nil, lpOpts) err = lp.Replay(ctx, 5) // block number too high require.ErrorContains(t, err, "Invalid replay block number") @@ -1548,7 +1554,8 @@ func TestTooManyLogResults(t *testing.T) { RpcBatchSize: 10, KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(o, ec, lggr, lpOpts) + headTracker := htMocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) + lp := logpoller.NewLogPoller(o, ec, lggr, headTracker, lpOpts) expected := []int64{10, 5, 2, 1} clientErr := client.JsonError{ @@ -1557,9 +1564,18 @@ func TestTooManyLogResults(t *testing.T) { Message: "query returned more than 10000 results. Try with this block range [0x100E698, 0x100E6D4].", } + newHeadWithFinalized := func(blockNumber int64) *evmtypes.Head { + head := &evmtypes.Head{Number: blockNumber} + finalized := &evmtypes.Head{Number: blockNumber - lpOpts.FinalityDepth, IsFinalized: true} + head.Parent = finalized + return head + } + + // Simulate currentBlock = 300 + headTracker.On("ChainWithLatestFinalized").Return(newHeadWithFinalized(300), nil).Once() call1 := ec.On("HeadByNumber", mock.Anything, mock.Anything).Return(func(ctx context.Context, blockNumber *big.Int) (*evmtypes.Head, error) { if blockNumber == nil { - return &evmtypes.Head{Number: 300}, nil // Simulate currentBlock = 300 + panic("unexpected call to get current head") } return &evmtypes.Head{Number: blockNumber.Int64()}, nil }) @@ -1601,9 +1617,10 @@ func TestTooManyLogResults(t *testing.T) { // Now jump to block 500, but return error no matter how small the block range gets. // Should exit the loop with a critical error instead of hanging. + headTracker.On("ChainWithLatestFinalized").Return(newHeadWithFinalized(500), nil).Once() call1.On("HeadByNumber", mock.Anything, mock.Anything).Return(func(ctx context.Context, blockNumber *big.Int) (*evmtypes.Head, error) { if blockNumber == nil { - return &evmtypes.Head{Number: 500}, nil // Simulate currentBlock = 300 + panic("unexpected call to get current head") } return &evmtypes.Head{Number: blockNumber.Int64()}, nil }) @@ -1938,7 +1955,7 @@ func TestFindLCA(t *testing.T) { KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(orm, ec, lggr, lpOpts) + lp := logpoller.NewLogPoller(orm, ec, lggr, nil, lpOpts) t.Run("Fails, if failed to select oldest block", func(t *testing.T) { _, err := lp.FindLCA(ctx) require.ErrorContains(t, err, "failed to select the latest block") diff --git a/core/chains/evm/txmgr/txmgr_test.go b/core/chains/evm/txmgr/txmgr_test.go index 85d25d8a70b..cd6958452fd 100644 --- a/core/chains/evm/txmgr/txmgr_test.go +++ b/core/chains/evm/txmgr/txmgr_test.go @@ -18,6 +18,8 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -32,6 +34,7 @@ import ( evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/keystore" ksmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/keystore/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" @@ -56,7 +59,9 @@ func makeTestEvmTxm( RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), ethClient, lggr, lpOpts) + + ht := headtracker.NewSimulatedHeadTracker(tests.Context(t), ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), ethClient, lggr, ht, lpOpts) // logic for building components (from evm/evm_txm.go) ------- lggr.Infow("Initializing EVM transaction manager", diff --git a/core/chains/legacyevm/chain.go b/core/chains/legacyevm/chain.go index 27e0155da52..24ea295a4f5 100644 --- a/core/chains/legacyevm/chain.go +++ b/core/chains/legacyevm/chain.go @@ -245,7 +245,7 @@ func newChain(ctx context.Context, cfg *evmconfig.ChainScoped, nodes []*toml.Nod LogPrunePageSize: int64(cfg.EVM().LogPrunePageSize()), BackupPollerBlockDelay: int64(cfg.EVM().BackupLogPollerBlockDelay()), } - logPoller = logpoller.NewLogPoller(logpoller.NewObservedORM(chainID, opts.DS, l), client, l, lpOpts) + logPoller = logpoller.NewLogPoller(logpoller.NewObservedORM(chainID, opts.DS, l), client, l, headTracker, lpOpts) } } diff --git a/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go b/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go index ea8f64c02fa..b1dd596085e 100644 --- a/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go +++ b/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go @@ -14,12 +14,15 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/channel_config_store" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" @@ -84,8 +87,9 @@ func Test_ChannelDefinitionCache_Integration(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } + ht := headtracker.NewSimulatedHeadTracker(tests.Context(t), ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) lp := logpoller.NewLogPoller( - logpoller.NewORM(testutils.SimulatedChainID, db, lggr), ethClient, lggr, lpOpts) + logpoller.NewORM(testutils.SimulatedChainID, db, lggr), ethClient, lggr, ht, lpOpts) servicetest.Run(t, lp) cdc := llo.NewChannelDefinitionCache(lggr, orm, lp, configStoreAddress, 0) @@ -156,8 +160,9 @@ func Test_ChannelDefinitionCache_Integration(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } + ht := headtracker.NewSimulatedHeadTracker(tests.Context(t), ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) lp := &mockLogPoller{ - LogPoller: logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr), ethClient, lggr, lpOpts), + LogPoller: logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr), ethClient, lggr, ht, lpOpts), LatestBlockFn: func(ctx context.Context) (int64, error) { return 0, nil }, @@ -198,7 +203,8 @@ func Test_ChannelDefinitionCache_Integration(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr), ethClient, lggr, lpOpts) + ht := headtracker.NewSimulatedHeadTracker(tests.Context(t), ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr), ethClient, lggr, ht, lpOpts) servicetest.Run(t, lp) cdc := llo.NewChannelDefinitionCache(lggr, orm, lp, configStoreAddress, channel2Block.Number().Int64()+1) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/integration_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/integration_test.go index 46314dde418..e7ad1a8c0e3 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/integration_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/integration_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" @@ -21,6 +22,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_upkeep_counter_wrapper" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" @@ -506,7 +508,8 @@ func setupDependencies(t *testing.T, db *sqlx.DB, backend *backends.SimulatedBac RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(lorm, ethClient, pollerLggr, lpOpts) + ht := headtracker.NewSimulatedHeadTracker(tests.Context(t), ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(lorm, ethClient, pollerLggr, ht, lpOpts) return lp, ethClient } diff --git a/core/services/promreporter/prom_reporter_test.go b/core/services/promreporter/prom_reporter_test.go index f17b4aafed2..8439039e829 100644 --- a/core/services/promreporter/prom_reporter_test.go +++ b/core/services/promreporter/prom_reporter_test.go @@ -10,9 +10,12 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -45,7 +48,8 @@ func newLegacyChainContainer(t *testing.T, db *sqlx.DB) legacyevm.LegacyChainCon RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), ethClient, lggr, lpOpts) + ht := headtracker.NewSimulatedHeadTracker(tests.Context(t), ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), ethClient, lggr, ht, lpOpts) txm, err := txmgr.NewTxm( db, diff --git a/core/services/relay/evm/chain_reader_test.go b/core/services/relay/evm/chain_reader_test.go index c3cb36b93e3..1db0ce9fb1f 100644 --- a/core/services/relay/evm/chain_reader_test.go +++ b/core/services/relay/evm/chain_reader_test.go @@ -23,6 +23,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-common/pkg/codec" clcommontypes "github.com/smartcontractkit/chainlink-common/pkg/types" @@ -31,6 +33,7 @@ import ( commontestutils "github.com/smartcontractkit/chainlink-common/pkg/loop/testutils" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/chain_reader_tester" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -268,7 +271,8 @@ func (it *chainReaderInterfaceTester) GetChainReader(t *testing.T) clcommontypes RpcBatchSize: 1, KeepFinalizedBlocksDepth: 10000, } - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr), it.client, lggr, lpOpts) + ht := headtracker.NewSimulatedHeadTracker(tests.Context(t), it.client, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr), it.client, lggr, ht, lpOpts) require.NoError(t, lp.Start(ctx)) cr, err := evm.NewChainReaderService(ctx, lggr, lp, it.client, it.chainConfig) require.NoError(t, err) diff --git a/core/services/relay/evm/config_poller_test.go b/core/services/relay/evm/config_poller_test.go index 8c02c4e2e7e..4c31812baa3 100644 --- a/core/services/relay/evm/config_poller_test.go +++ b/core/services/relay/evm/config_poller_test.go @@ -19,6 +19,8 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" "github.com/smartcontractkit/libocr/gethwrappers2/ocrconfigurationstoreevmsimple" testoffchainaggregator2 "github.com/smartcontractkit/libocr/gethwrappers2/testocr2aggregator" @@ -32,6 +34,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmClientMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" @@ -99,7 +102,8 @@ func TestConfigPoller(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp = logpoller.NewLogPoller(lorm, ethClient, lggr, lpOpts) + ht := headtracker.NewSimulatedHeadTracker(tests.Context(t), ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp = logpoller.NewLogPoller(lorm, ethClient, lggr, ht, lpOpts) servicetest.Run(t, lp) } diff --git a/core/services/relay/evm/functions/config_poller_test.go b/core/services/relay/evm/functions/config_poller_test.go index 2d96b2fd15d..ad0406519af 100644 --- a/core/services/relay/evm/functions/config_poller_test.go +++ b/core/services/relay/evm/functions/config_poller_test.go @@ -15,6 +15,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" testoffchainaggregator2 "github.com/smartcontractkit/libocr/gethwrappers2/testocr2aggregator" confighelper2 "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" @@ -23,6 +25,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" @@ -88,7 +91,8 @@ func runTest(t *testing.T, pluginType functions.FunctionsPluginType, expectedDig RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(lorm, ethClient, lggr, lpOpts) + ht := headtracker.NewSimulatedHeadTracker(tests.Context(t), ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(lorm, ethClient, lggr, ht, lpOpts) servicetest.Run(t, lp) configPoller, err := functions.NewFunctionsConfigPoller(pluginType, lp, lggr) require.NoError(t, err) diff --git a/core/services/relay/evm/mercury/helpers_test.go b/core/services/relay/evm/mercury/helpers_test.go index f2923696bfc..39cfa32405d 100644 --- a/core/services/relay/evm/mercury/helpers_test.go +++ b/core/services/relay/evm/mercury/helpers_test.go @@ -14,12 +14,15 @@ import ( "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/libocr/offchainreporting2plus/chains/evmutil" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/verifier" @@ -174,7 +177,8 @@ func SetupTH(t *testing.T, feedID common.Hash) TestHarness { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(lorm, ethClient, lggr, lpOpts) + ht := headtracker.NewSimulatedHeadTracker(tests.Context(t), ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(lorm, ethClient, lggr, ht, lpOpts) servicetest.Run(t, lp) configPoller, err := NewConfigPoller(testutils.Context(t), lggr, lp, verifierAddress, feedID) diff --git a/core/services/vrf/v2/listener_v2_log_listener_test.go b/core/services/vrf/v2/listener_v2_log_listener_test.go index a393aec3ee3..1f121cd57be 100644 --- a/core/services/vrf/v2/listener_v2_log_listener_test.go +++ b/core/services/vrf/v2/listener_v2_log_listener_test.go @@ -20,7 +20,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" @@ -103,7 +106,8 @@ func setupVRFLogPollerListenerTH(t *testing.T, RpcBatchSize: rpcBatchSize, KeepFinalizedBlocksDepth: keepFinalizedBlocksDepth, } - lp := logpoller.NewLogPoller(o, esc, lggr, lpOpts) + ht := headtracker.NewSimulatedHeadTracker(tests.Context(t), esc, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(o, esc, lggr, ht, lpOpts) emitterAddress1, _, emitter1, err := log_emitter.DeployLogEmitter(owner, ec) require.NoError(t, err) From d79ef7e5ccbaf0045eea142f647f23ade60ac25c Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Fri, 10 May 2024 19:49:30 +0200 Subject: [PATCH 12/53] remove redundant changes --- common/client/mock_node_test.go | 6 +- common/client/multi_node.go | 2 +- common/client/multi_node_test.go | 2 +- common/client/node.go | 4 +- common/client/node_fsm.go | 4 +- common/client/node_lifecycle.go | 2 +- common/client/node_lifecycle_test.go | 2 +- common/client/node_selector_highest_head.go | 2 +- .../client/node_selector_highest_head_test.go | 40 ++++++------- .../client/node_selector_total_difficulty.go | 2 +- .../node_selector_total_difficulty_test.go | 40 ++++++------- .../chains/evm/logpoller/head_tracker_mock.go | 57 +++++++++++++++++++ 12 files changed, 110 insertions(+), 53 deletions(-) create mode 100644 core/chains/evm/logpoller/head_tracker_mock.go diff --git a/common/client/mock_node_test.go b/common/client/mock_node_test.go index 5fc5b4833cb..e90063b33e8 100644 --- a/common/client/mock_node_test.go +++ b/common/client/mock_node_test.go @@ -163,12 +163,12 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) State() nodeState { return r0 } -// StateAndLatestChainInfo provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) StateAndLatestChainInfo() (nodeState, ChainInfo) { +// StateAndLatest provides a mock function with given fields: +func (_m *mockNode[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, ChainInfo) { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for StateAndLatestChainInfo") + panic("no return value specified for StateAndLatest") } var r0 nodeState diff --git a/common/client/multi_node.go b/common/client/multi_node.go index 80c229ef37d..271f9045a5f 100644 --- a/common/client/multi_node.go +++ b/common/client/multi_node.go @@ -272,7 +272,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP TotalDifficulty: big.NewInt(0), } for _, n := range c.nodes { - if s, nodeChainInfo := n.StateAndLatestChainInfo(); s == nodeStateAlive { + if s, nodeChainInfo := n.StateAndLatest(); s == nodeStateAlive { nLiveNodes++ ch.BlockNumber = max(ch.BlockNumber, nodeChainInfo.BlockNumber) ch.FinalizedBlockNumber = max(ch.FinalizedBlockNumber, nodeChainInfo.FinalizedBlockNumber) diff --git a/common/client/multi_node_test.go b/common/client/multi_node_test.go index a0ea719d0ac..ec1b90b55e7 100644 --- a/common/client/multi_node_test.go +++ b/common/client/multi_node_test.go @@ -526,7 +526,7 @@ func TestMultiNode_ChainInfo(t *testing.T) { t.Run(tc.Name, func(t *testing.T) { for _, params := range tc.NodeParams { node := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - node.On("StateAndLatestChainInfo").Return(params.State, params.LatestChainInfo) + node.On("StateAndLatest").Return(params.State, params.LatestChainInfo) node.On("HighestChainInfo").Return(params.HighestChainInfo) mn.nodes = append(mn.nodes, node) } diff --git a/common/client/node.go b/common/client/node.go index 7483158451c..de35504de3b 100644 --- a/common/client/node.go +++ b/common/client/node.go @@ -66,8 +66,8 @@ type Node[ ] interface { // State returns nodeState State() nodeState - // StateAndLatestChainInfo returns nodeState with the latest ChainInfo observed by Node during current lifecycle. - StateAndLatestChainInfo() (nodeState, ChainInfo) + // StateAndLatest returns nodeState with the latest ChainInfo observed by Node during current lifecycle. + StateAndLatest() (nodeState, ChainInfo) // HighestChainInfo - returns highest ChainInfo ever observed by the Node HighestChainInfo() ChainInfo SetPoolChainInfoProvider(PoolChainInfoProvider) diff --git a/common/client/node_fsm.go b/common/client/node_fsm.go index d15657505fd..a929e6b5f7e 100644 --- a/common/client/node_fsm.go +++ b/common/client/node_fsm.go @@ -156,8 +156,8 @@ func (n *node[CHAIN_ID, HEAD, RPC]) isFinalizedBlockOutOfSync() bool { return n.latestChainInfo.BlockNumber < observedByCaller.BlockNumber-int64(n.chainCfg.FinalizedBlockOffset()) } -// StateAndLatestChainInfo returns nodeState with the latest ChainInfo observed by Node during current lifecycle. -func (n *node[CHAIN_ID, HEAD, RPC]) StateAndLatestChainInfo() (nodeState, ChainInfo) { +// StateAndLatest returns nodeState with the latest ChainInfo observed by Node during current lifecycle. +func (n *node[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, ChainInfo) { n.stateMu.RLock() defer n.stateMu.RUnlock() return n.recalculateState(), n.latestChainInfo diff --git a/common/client/node_lifecycle.go b/common/client/node_lifecycle.go index d43acd42f65..64048e15fbb 100644 --- a/common/client/node_lifecycle.go +++ b/common/client/node_lifecycle.go @@ -182,7 +182,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { n.declareUnreachable() return } - _, ci := n.StateAndLatestChainInfo() + _, ci := n.StateAndLatest() if outOfSync, liveNodes := n.syncStatus(ci.BlockNumber, ci.TotalDifficulty); outOfSync { // note: there must be another live node for us to be out of sync lggr.Errorw("RPC endpoint has fallen behind", "blockNumber", ci.BlockNumber, "totalDifficulty", ci.TotalDifficulty, "nodeState", n.getCachedState()) diff --git a/common/client/node_lifecycle_test.go b/common/client/node_lifecycle_test.go index ac9c056c9b2..5e6f81dbba0 100644 --- a/common/client/node_lifecycle_test.go +++ b/common/client/node_lifecycle_test.go @@ -384,7 +384,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() node.declareAlive() tests.AssertEventually(t, func() bool { - state, chainInfo := node.StateAndLatestChainInfo() + state, chainInfo := node.StateAndLatest() return state == nodeStateAlive && chainInfo.BlockNumber == expectedBlockNumber && bigmath.Equal(chainInfo.TotalDifficulty, expectedDiff) && // finality tag is enabled, so must not update finalized block number chainInfo.FinalizedBlockNumber == 0 diff --git a/common/client/node_selector_highest_head.go b/common/client/node_selector_highest_head.go index f753a991782..25a931fc01b 100644 --- a/common/client/node_selector_highest_head.go +++ b/common/client/node_selector_highest_head.go @@ -24,7 +24,7 @@ func (s highestHeadNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HE var highestHeadNumber int64 = math.MinInt64 var highestHeadNodes []Node[CHAIN_ID, HEAD, RPC] for _, n := range s { - state, currentChainInfo := n.StateAndLatestChainInfo() + state, currentChainInfo := n.StateAndLatest() currentHeadNumber := currentChainInfo.BlockNumber if state == nodeStateAlive && currentHeadNumber >= highestHeadNumber { if highestHeadNumber < currentHeadNumber { diff --git a/common/client/node_selector_highest_head_test.go b/common/client/node_selector_highest_head_test.go index 9b87fa240f6..e245924589c 100644 --- a/common/client/node_selector_highest_head_test.go +++ b/common/client/node_selector_highest_head_test.go @@ -24,13 +24,13 @@ func TestHighestHeadNodeSelector(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) if i == 0 { // first node is out of sync - node.On("StateAndLatestChainInfo").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: int64(-1)}) + node.On("StateAndLatest").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: int64(-1)}) } else if i == 1 { // second node is alive, LatestReceivedBlockNumber = 1 - node.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(1)}) + node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(1)}) } else { // third node is alive, LatestReceivedBlockNumber = 2 (best node) - node.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(2)}) + node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(2)}) } node.On("Order").Maybe().Return(int32(1)) nodes = append(nodes, node) @@ -42,7 +42,7 @@ func TestHighestHeadNodeSelector(t *testing.T) { t.Run("stick to the same node", func(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) // fourth node is alive, LatestReceivedBlockNumber = 2 (same as 3rd) - node.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(2)}) + node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(2)}) node.On("Order").Return(int32(1)) nodes = append(nodes, node) @@ -53,7 +53,7 @@ func TestHighestHeadNodeSelector(t *testing.T) { t.Run("another best node", func(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) // fifth node is alive, LatestReceivedBlockNumber = 3 (better than 3rd and 4th) - node.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) + node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) node.On("Order").Return(int32(1)) nodes = append(nodes, node) @@ -63,10 +63,10 @@ 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.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(-1)}) + node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(-1)}) node1.On("Order").Return(int32(1)) node2 := newMockNode[types.ID, Head, nodeClient](t) - node2.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(-1)}) + 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}) assert.Same(t, node1, selector.Select()) @@ -83,10 +83,10 @@ func TestHighestHeadNodeSelector_None(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) if i == 0 { // first node is out of sync - node.On("StateAndLatestChainInfo").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: int64(-1)}) + node.On("StateAndLatest").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: int64(-1)}) } else { // others are unreachable - node.On("StateAndLatestChainInfo").Return(nodeStateUnreachable, ChainInfo{BlockNumber: int64(-1)}) + node.On("StateAndLatest").Return(nodeStateUnreachable, ChainInfo{BlockNumber: int64(-1)}) } nodes = append(nodes, node) } @@ -104,7 +104,7 @@ func TestHighestHeadNodeSelectorWithOrder(t *testing.T) { t.Run("same head and order", func(t *testing.T) { for i := 0; i < 3; i++ { node := newMockNode[types.ID, Head, nodeClient](t) - node.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(1)}) + node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(1)}) node.On("Order").Return(int32(2)) nodes = append(nodes, node) } @@ -115,15 +115,15 @@ func TestHighestHeadNodeSelectorWithOrder(t *testing.T) { t.Run("same head but different order", func(t *testing.T) { node1 := newMockNode[types.ID, Head, nodeClient](t) - node1.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) + node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) node1.On("Order").Return(int32(3)) node2 := newMockNode[types.ID, Head, nodeClient](t) - node2.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) + node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) node2.On("Order").Return(int32(1)) node3 := newMockNode[types.ID, Head, nodeClient](t) - node3.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) + node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) node3.On("Order").Return(int32(2)) nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3} @@ -134,15 +134,15 @@ func TestHighestHeadNodeSelectorWithOrder(t *testing.T) { t.Run("different head but same order", func(t *testing.T) { node1 := newMockNode[types.ID, Head, nodeClient](t) - node1.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(1)}) + node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(1)}) node1.On("Order").Maybe().Return(int32(3)) node2 := newMockNode[types.ID, Head, nodeClient](t) - node2.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(2)}) + node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(2)}) node2.On("Order").Maybe().Return(int32(3)) node3 := newMockNode[types.ID, Head, nodeClient](t) - node3.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) + node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) node3.On("Order").Return(int32(3)) nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3} @@ -153,19 +153,19 @@ func TestHighestHeadNodeSelectorWithOrder(t *testing.T) { t.Run("different head and different order", func(t *testing.T) { node1 := newMockNode[types.ID, Head, nodeClient](t) - node1.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(10)}) + node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(10)}) node1.On("Order").Maybe().Return(int32(3)) node2 := newMockNode[types.ID, Head, nodeClient](t) - node2.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(11)}) + node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(11)}) node2.On("Order").Maybe().Return(int32(4)) node3 := newMockNode[types.ID, Head, nodeClient](t) - node3.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(11)}) + node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(11)}) node3.On("Order").Maybe().Return(int32(3)) node4 := newMockNode[types.ID, Head, nodeClient](t) - node4.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(10)}) + 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} diff --git a/common/client/node_selector_total_difficulty.go b/common/client/node_selector_total_difficulty.go index 3587353c748..6b45e75528b 100644 --- a/common/client/node_selector_total_difficulty.go +++ b/common/client/node_selector_total_difficulty.go @@ -27,7 +27,7 @@ func (s totalDifficultyNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID var aliveNodes []Node[CHAIN_ID, HEAD, RPC] for _, n := range s { - state, currentChainInfo := n.StateAndLatestChainInfo() + state, currentChainInfo := n.StateAndLatest() if state != nodeStateAlive { continue } diff --git a/common/client/node_selector_total_difficulty_test.go b/common/client/node_selector_total_difficulty_test.go index 9e11bced3c3..0bc214918d7 100644 --- a/common/client/node_selector_total_difficulty_test.go +++ b/common/client/node_selector_total_difficulty_test.go @@ -24,13 +24,13 @@ func TestTotalDifficultyNodeSelector(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) if i == 0 { // first node is out of sync - node.On("StateAndLatestChainInfo").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: -1}) + node.On("StateAndLatest").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: -1}) } else if i == 1 { // second node is alive - node.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(7)}) + node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(7)}) } else { // third node is alive and best - node.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 2, TotalDifficulty: big.NewInt(8)}) + node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 2, TotalDifficulty: big.NewInt(8)}) } node.On("Order").Maybe().Return(int32(1)) nodes = append(nodes, node) @@ -42,7 +42,7 @@ func TestTotalDifficultyNodeSelector(t *testing.T) { t.Run("stick to the same node", func(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) // fourth node is alive (same as 3rd) - node.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 2, TotalDifficulty: big.NewInt(8)}) + node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 2, TotalDifficulty: big.NewInt(8)}) node.On("Order").Maybe().Return(int32(1)) nodes = append(nodes, node) @@ -53,7 +53,7 @@ func TestTotalDifficultyNodeSelector(t *testing.T) { t.Run("another best node", func(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) // fifth node is alive (better than 3rd and 4th) - node.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(11)}) + node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(11)}) node.On("Order").Maybe().Return(int32(1)) nodes = append(nodes, node) @@ -63,10 +63,10 @@ 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.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: -1, TotalDifficulty: nil}) + 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.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: -1, TotalDifficulty: nil}) + 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} @@ -85,10 +85,10 @@ func TestTotalDifficultyNodeSelector_None(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) if i == 0 { // first node is out of sync - node.On("StateAndLatestChainInfo").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: -1, TotalDifficulty: nil}) + node.On("StateAndLatest").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: -1, TotalDifficulty: nil}) } else { // others are unreachable - node.On("StateAndLatestChainInfo").Return(nodeStateUnreachable, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(7)}) + node.On("StateAndLatest").Return(nodeStateUnreachable, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(7)}) } nodes = append(nodes, node) } @@ -106,7 +106,7 @@ func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { t.Run("same td and order", func(t *testing.T) { for i := 0; i < 3; i++ { node := newMockNode[types.ID, Head, nodeClient](t) - node.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(10)}) + node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(10)}) node.On("Order").Return(int32(2)) nodes = append(nodes, node) } @@ -117,15 +117,15 @@ func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { t.Run("same td but different order", func(t *testing.T) { node1 := newMockNode[types.ID, Head, nodeClient](t) - node1.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(10)}) + 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.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(10)}) + 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.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(10)}) + 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} @@ -136,15 +136,15 @@ func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { t.Run("different td but same order", func(t *testing.T) { node1 := newMockNode[types.ID, Head, nodeClient](t) - node1.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(10)}) + 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.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(11)}) + 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.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(12)}) + 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} @@ -155,19 +155,19 @@ func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { t.Run("different head and different order", func(t *testing.T) { node1 := newMockNode[types.ID, Head, nodeClient](t) - node1.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(100)}) + 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.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(110)}) + 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.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(110)}) + 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.On("StateAndLatestChainInfo").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(105)}) + 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} diff --git a/core/chains/evm/logpoller/head_tracker_mock.go b/core/chains/evm/logpoller/head_tracker_mock.go new file mode 100644 index 00000000000..77c304f8993 --- /dev/null +++ b/core/chains/evm/logpoller/head_tracker_mock.go @@ -0,0 +1,57 @@ +// Code generated by mockery v2.42.2. DO NOT EDIT. + +package mocks + +import ( + types "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + mock "github.com/stretchr/testify/mock" +) + +// HeadTracker is an autogenerated mock type for the HeadTracker type +type HeadTracker struct { + mock.Mock +} + +// ChainWithLatestFinalized provides a mock function with given fields: +func (_m *HeadTracker) ChainWithLatestFinalized() (*types.Head, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for ChainWithLatestFinalized") + } + + var r0 *types.Head + var r1 error + if rf, ok := ret.Get(0).(func() (*types.Head, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() *types.Head); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Head) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewHeadTracker creates a new instance of HeadTracker. 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 NewHeadTracker(t interface { + mock.TestingT + Cleanup(func()) +}) *HeadTracker { + mock := &HeadTracker{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} From 27b146a3b18deaecff161589122729e53c258a18 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Fri, 10 May 2024 19:55:55 +0200 Subject: [PATCH 13/53] fix build --- .../chains/evm/logpoller/head_tracker_mock.go | 57 ------------------- core/chains/evm/logpoller/log_poller.go | 1 - 2 files changed, 58 deletions(-) delete mode 100644 core/chains/evm/logpoller/head_tracker_mock.go diff --git a/core/chains/evm/logpoller/head_tracker_mock.go b/core/chains/evm/logpoller/head_tracker_mock.go deleted file mode 100644 index 77c304f8993..00000000000 --- a/core/chains/evm/logpoller/head_tracker_mock.go +++ /dev/null @@ -1,57 +0,0 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. - -package mocks - -import ( - types "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - mock "github.com/stretchr/testify/mock" -) - -// HeadTracker is an autogenerated mock type for the HeadTracker type -type HeadTracker struct { - mock.Mock -} - -// ChainWithLatestFinalized provides a mock function with given fields: -func (_m *HeadTracker) ChainWithLatestFinalized() (*types.Head, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ChainWithLatestFinalized") - } - - var r0 *types.Head - var r1 error - if rf, ok := ret.Get(0).(func() (*types.Head, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() *types.Head); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*types.Head) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NewHeadTracker creates a new instance of HeadTracker. 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 NewHeadTracker(t interface { - mock.TestingT - Cleanup(func()) -}) *HeadTracker { - mock := &HeadTracker{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index 1512abea059..fcb94adc384 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -88,7 +88,6 @@ type Client interface { ConfiguredChainID() *big.Int } -//go:generate mockery --quiet --name HeadTracker --output ./ --case=underscore --structname HeadTracker --filename head_tracker_mock.go type HeadTracker interface { // ChainWithLatestFinalized - returns highest block, whose ancestor is marked as finalized ChainWithLatestFinalized() (*evmtypes.Head, error) From ab31c32c94c02805e1cc80cf1adbd5931f34dc47 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Mon, 13 May 2024 17:55:22 +0200 Subject: [PATCH 14/53] Capture latest chain info on RPC level to avoid race on state transition if we are communicating with RPC with highest finalized block --- common/client/mock_node_client_test.go | 14 +- common/client/mock_rpc_test.go | 14 +- common/client/node.go | 3 - common/client/node_fsm.go | 25 +-- common/client/node_lifecycle.go | 29 +-- common/client/node_lifecycle_test.go | 67 +++---- common/client/types.go | 13 +- core/chains/evm/client/chain_client_test.go | 1 + core/chains/evm/client/mocks/rpc_client.go | 14 +- core/chains/evm/client/node_lifecycle_test.go | 1 + core/chains/evm/client/rpc_client.go | 48 +++-- core/chains/evm/client/rpc_client_test.go | 169 ++++++++++-------- 12 files changed, 218 insertions(+), 180 deletions(-) diff --git a/common/client/mock_node_client_test.go b/common/client/mock_node_client_test.go index 6ecbaf25e7c..c0253148e47 100644 --- a/common/client/mock_node_client_test.go +++ b/common/client/mock_node_client_test.go @@ -117,7 +117,7 @@ func (_m *mockNodeClient[CHAIN_ID, HEAD]) DisconnectAll() { } // GetInterceptedChainInfo provides a mock function with given fields: -func (_m *mockNodeClient[CHAIN_ID, HEAD]) GetInterceptedChainInfo() ChainInfo { +func (_m *mockNodeClient[CHAIN_ID, HEAD]) GetInterceptedChainInfo() (ChainInfo, ChainInfo) { ret := _m.Called() if len(ret) == 0 { @@ -125,13 +125,23 @@ func (_m *mockNodeClient[CHAIN_ID, HEAD]) GetInterceptedChainInfo() ChainInfo { } 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) } - return r0 + if rf, ok := ret.Get(1).(func() ChainInfo); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(ChainInfo) + } + + return r0, r1 } // IsSyncing provides a mock function with given fields: ctx diff --git a/common/client/mock_rpc_test.go b/common/client/mock_rpc_test.go index c16af11bcb9..909bb3e9b78 100644 --- a/common/client/mock_rpc_test.go +++ b/common/client/mock_rpc_test.go @@ -367,7 +367,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // 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 { +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 { @@ -375,13 +375,23 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } 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) } - return r0 + if rf, ok := ret.Get(1).(func() ChainInfo); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(ChainInfo) + } + + return r0, r1 } // IsSyncing provides a mock function with given fields: ctx diff --git a/common/client/node.go b/common/client/node.go index de35504de3b..504ab6d06e2 100644 --- a/common/client/node.go +++ b/common/client/node.go @@ -105,8 +105,6 @@ type node[ stateMu sync.RWMutex // protects state* fields state nodeState - // Each node is tracking the last received head number and total difficulty - latestChainInfo ChainInfo poolInfoProvider PoolChainInfoProvider @@ -156,7 +154,6 @@ func NewNode[ "nodeOrder", n.order, ) n.lfcLog = logger.Named(lggr, "Lifecycle") - n.latestChainInfo.BlockNumber = -1 n.rpc = rpc n.chainFamily = chainFamily return n diff --git a/common/client/node_fsm.go b/common/client/node_fsm.go index a929e6b5f7e..ba343f40aef 100644 --- a/common/client/node_fsm.go +++ b/common/client/node_fsm.go @@ -148,36 +148,27 @@ func (n *node[CHAIN_ID, HEAD, RPC]) isFinalizedBlockOutOfSync() bool { return false } - observedByCaller := n.poolInfoProvider.HighestChainInfo() + highestObservedByCaller := n.poolInfoProvider.HighestChainInfo() + latest, _ := n.rpc.GetInterceptedChainInfo() if n.chainCfg.FinalityTagEnabled() { - return n.latestChainInfo.FinalizedBlockNumber < observedByCaller.FinalizedBlockNumber-int64(n.chainCfg.FinalizedBlockOffset()) + return latest.FinalizedBlockNumber < highestObservedByCaller.FinalizedBlockNumber-int64(n.chainCfg.FinalizedBlockOffset()) } - return n.latestChainInfo.BlockNumber < observedByCaller.BlockNumber-int64(n.chainCfg.FinalizedBlockOffset()) + return latest.BlockNumber < highestObservedByCaller.BlockNumber-int64(n.chainCfg.FinalizedBlockOffset()) } // StateAndLatest returns nodeState with the latest ChainInfo observed by Node during current lifecycle. func (n *node[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, ChainInfo) { n.stateMu.RLock() defer n.stateMu.RUnlock() - return n.recalculateState(), n.latestChainInfo -} - -func (n *node[CHAIN_ID, HEAD, RPC]) getLatestChainInfo() ChainInfo { - n.stateMu.RLock() - defer n.stateMu.RUnlock() - return n.latestChainInfo -} - -func (n *node[CHAIN_ID, HEAD, RPC]) setLatestChainInfo(ci ChainInfo) { - n.stateMu.Lock() - n.latestChainInfo = ci - n.stateMu.Unlock() + latest, _ := n.rpc.GetInterceptedChainInfo() + return n.recalculateState(), latest } // HighestChainInfo - returns highest ChainInfo ever observed by the Node func (n *node[CHAIN_ID, HEAD, RPC]) HighestChainInfo() ChainInfo { - return n.rpc.GetInterceptedChainInfo() + _, highest := n.rpc.GetInterceptedChainInfo() + return highest } func (n *node[CHAIN_ID, HEAD, RPC]) SetPoolChainInfoProvider(poolInfoProvider PoolChainInfoProvider) { n.poolInfoProvider = poolInfoProvider diff --git a/common/client/node_lifecycle.go b/common/client/node_lifecycle.go index 64048e15fbb..40fef09f25d 100644 --- a/common/client/node_lifecycle.go +++ b/common/client/node_lifecycle.go @@ -137,15 +137,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { pollFinalizedHeadCh = pollT.C } - localHighestChainInfo := n.getLatestChainInfo() - defer func() { - // reset latest chain info to avoid following race condition: - // 1. Node observes block 100 and becomes unreachable. - // 2. While the node is down, its state is rolled back to block 90. - // 4. Node becomes reachable again. - // 5. Before new head is processed, we report that node is healthy and on block 100. - n.setLatestChainInfo(ChainInfo{}) - }() + localHighestChainInfo, _ := n.rpc.GetInterceptedChainInfo() var pollFailures uint32 for { @@ -211,7 +203,6 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { if outOfSyncT != nil { outOfSyncT.Reset(noNewHeadsTimeoutThreshold) } - n.onNewHead(bh) if !n.chainCfg.FinalityTagEnabled() { latestFinalizedBN := max(bh.BlockNumber()-int64(n.chainCfg.FinalityDepth()), 0) if latestFinalizedBN > localHighestChainInfo.FinalizedBlockNumber { @@ -252,7 +243,6 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { continue } - n.onNewFinalizedHead(latestFinalized) latestFinalizedBN := latestFinalized.BlockNumber() if latestFinalizedBN > localHighestChainInfo.FinalizedBlockNumber { promPoolRPCNodeHighestFinalizedBlock.WithLabelValues(n.chainID.String(), n.name).Set(float64(latestFinalizedBN)) @@ -346,7 +336,6 @@ func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(isOutOfSync func(num int64, td n.declareUnreachable() return } - n.onNewHead(head) if !isOutOfSync(head.BlockNumber(), head.BlockDifficulty()) { // back in-sync! flip back into alive loop lggr.Infow(fmt.Sprintf("%s: %s. Node was out-of-sync for %s", msgInSync, n.String(), time.Since(outOfSyncAt)), "blockNumber", head.BlockNumber(), "blockDifficulty", head.BlockDifficulty(), "nodeState", n.getCachedState()) @@ -525,19 +514,3 @@ func (n *node[CHAIN_ID, HEAD, RPC]) syncingLoop() { } } } - -func (n *node[CHAIN_ID, HEAD, RPC]) onNewHead(head HEAD) { - n.stateMu.Lock() - defer n.stateMu.Unlock() - n.latestChainInfo.BlockNumber = head.BlockNumber() - n.latestChainInfo.TotalDifficulty = head.BlockDifficulty() - if !n.chainCfg.FinalityTagEnabled() { - n.latestChainInfo.FinalizedBlockNumber = max(head.BlockNumber()-int64(n.chainCfg.FinalityDepth()), 0) - } -} - -func (n *node[CHAIN_ID, HEAD, RPC]) onNewFinalizedHead(head HEAD) { - n.stateMu.Lock() - defer n.stateMu.Unlock() - n.latestChainInfo.FinalizedBlockNumber = head.BlockNumber() -} diff --git a/common/client/node_lifecycle_test.go b/common/client/node_lifecycle_test.go index 5e6f81dbba0..ef624f47bce 100644 --- a/common/client/node_lifecycle_test.go +++ b/common/client/node_lifecycle_test.go @@ -15,7 +15,6 @@ import ( "go.uber.org/zap" "github.com/smartcontractkit/chainlink-common/pkg/logger" - bigmath "github.com/smartcontractkit/chainlink-common/pkg/utils/big_math" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" clientMocks "github.com/smartcontractkit/chainlink/v2/common/client/mocks" @@ -67,6 +66,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { rpc: rpc, lggr: lggr, }) + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() defer func() { assert.NoError(t, node.close()) }() sub := mocks.NewSubscription(t) @@ -96,6 +96,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { t.Run("Stays alive and waits for signal", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newSubscribedNode(t, testNodeOpts{ config: testNodeConfig{}, @@ -111,6 +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.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}) lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) const pollFailureThreshold = 3 node := newSubscribedNode(t, testNodeOpts{ @@ -152,6 +154,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { t.Run("with threshold poll failures, transitions to unreachable", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}) lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) const pollFailureThreshold = 3 node := newSubscribedNode(t, testNodeOpts{ @@ -194,7 +197,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { BlockNumber: 20, }).Once() node.SetPoolChainInfoProvider(poolInfo) - node.setLatestChainInfo(ChainInfo{BlockNumber: 20}) + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: 20}, ChainInfo{BlockNumber: 20}) pollError := errors.New("failed to get ClientVersion") rpc.On("ClientVersion", mock.Anything).Return("", pollError) node.declareAlive() @@ -216,10 +219,11 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() - node.latestChainInfo.BlockNumber = 20 + const mostRecentBlock = 20 + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: mostRecentBlock}, ChainInfo{BlockNumber: 30}) poolInfo := newMockPoolChainInfoProvider(t) poolInfo.On("LatestChainInfo").Return(10, ChainInfo{ - BlockNumber: syncThreshold + node.latestChainInfo.BlockNumber + 1, + BlockNumber: syncThreshold + mostRecentBlock + 1, TotalDifficulty: big.NewInt(10), }).Once() node.SetPoolChainInfoProvider(poolInfo) @@ -252,10 +256,11 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() - node.latestChainInfo.BlockNumber = 20 + const mostRecentBlock = 20 + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: mostRecentBlock}, ChainInfo{BlockNumber: 30}) poolInfo := newMockPoolChainInfoProvider(t) poolInfo.On("LatestChainInfo").Return(1, ChainInfo{ - BlockNumber: syncThreshold + node.latestChainInfo.BlockNumber + 1, + BlockNumber: syncThreshold + mostRecentBlock + 1, TotalDifficulty: big.NewInt(10), }).Once() node.SetPoolChainInfoProvider(poolInfo) @@ -277,16 +282,17 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() - node.latestChainInfo.BlockNumber = 20 + 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) 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.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() node := newSubscribedNode(t, testNodeOpts{ config: testNodeConfig{}, chainConfig: clientMocks.ChainConfig{ @@ -313,6 +319,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.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newSubscribedNode(t, testNodeOpts{ config: testNodeConfig{}, @@ -333,10 +340,10 @@ 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()) }) - t.Run("rpc closed head channel", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() sub := mocks.NewSubscription(t) sub.On("Err").Return((<-chan error)(nil)) sub.On("Unsubscribe").Once() @@ -363,33 +370,6 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { tests.AssertLogEventually(t, observedLogs, "Subscription channel unexpectedly closed") assert.Equal(t, nodeStateUnreachable, node.State()) }) - t.Run("updates block number and difficulty on new 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() - expectedBlockNumber := rand.Int63() - expectedDiff := big.NewInt(rand.Int63()) - rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { - ch := args.Get(1).(chan<- Head) - go writeHeads(t, ch, head{BlockNumber: expectedBlockNumber, BlockDifficulty: expectedDiff}) - }).Return(sub, nil).Once() - rpc.On("SetAliveLoopSub", sub).Once() - node := newDialedNode(t, testNodeOpts{ - config: testNodeConfig{}, - chainConfig: clientMocks.ChainConfig{IsFinalityTagEnabled: true}, - rpc: rpc, - }) - defer func() { assert.NoError(t, node.close()) }() - node.declareAlive() - tests.AssertEventually(t, func() bool { - state, chainInfo := node.StateAndLatest() - return state == nodeStateAlive && chainInfo.BlockNumber == expectedBlockNumber && bigmath.Equal(chainInfo.TotalDifficulty, expectedDiff) && - // finality tag is enabled, so must not update finalized block number - chainInfo.FinalizedBlockNumber == 0 - }) - }) 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) @@ -399,6 +379,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { const blockNumber = 1000 const finalityDepth = 10 const expectedBlock = 990 + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { ch := args.Get(1).(chan<- Head) go writeHeads(t, ch, head{BlockNumber: blockNumber - 1}, head{BlockNumber: blockNumber}, head{BlockNumber: blockNumber - 1}) @@ -425,6 +406,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { t.Run("Logs warning if failed to get finalized block", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() rpc.On("LatestFinalizedBlock", mock.Anything).Return(newMockHead(t), errors.New("failed to get finalized block")) sub := mocks.NewSubscription(t) sub.On("Err").Return((<-chan error)(nil)) @@ -452,6 +434,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { head := newMockHead(t) head.On("IsValid").Return(false) rpc.On("LatestFinalizedBlock", mock.Anything).Return(head, nil) + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() sub := mocks.NewSubscription(t) sub.On("Err").Return((<-chan error)(nil)) sub.On("Unsubscribe").Once() @@ -472,7 +455,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { node.declareAlive() tests.AssertLogEventually(t, observedLogs, "Latest finalized block is not valid") }) - t.Run("If finality tag and finalized block polling are enabled updates latest finalized block metric & LatestChainInfo", func(t *testing.T) { + t.Run("If finality tag and finalized block polling are enabled updates latest finalized block metric", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) const expectedBlock = 1101 @@ -488,6 +471,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { // the metric go writeHeads(t, ch, head{BlockNumber: expectedBlock*2 + finalityDepth}) }).Return(sub, nil).Once() + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() rpc.On("SetAliveLoopSub", sub).Once() name := "node-" + rand.Str(5) node := newDialedNode(t, testNodeOpts{ @@ -509,7 +493,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { require.NoError(t, err) var m = &prom.Metric{} require.NoError(t, metric.Write(m)) - return float64(expectedBlock) == m.Gauge.GetValue() && node.getLatestChainInfo().FinalizedBlockNumber == expectedBlock + return float64(expectedBlock) == m.Gauge.GetValue() }) }) } @@ -545,6 +529,7 @@ func setupRPCForAliveLoop(t *testing.T, rpc *mockNodeClient[types.ID, Head]) { aliveSubscription.On("Unsubscribe").Maybe() rpc.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(aliveSubscription, nil).Maybe() rpc.On("SetAliveLoopSub", mock.Anything).Maybe() + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Maybe() } func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { @@ -801,6 +786,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { ch := args.Get(1).(chan<- Head) go writeHeads(t, ch, head{BlockNumber: highestBlock - 1}, head{BlockNumber: highestBlock}) }).Return(outOfSyncSubscription, nil).Once() + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: highestBlock}, ChainInfo{BlockNumber: highestBlock}) setupRPCForAliveLoop(t, rpc) @@ -833,6 +819,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { TotalDifficulty: big.NewInt(200), }).Once() node.SetPoolChainInfoProvider(poolInfo) + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: 0}, ChainInfo{BlockNumber: 0}) rpc.On("Dial", mock.Anything).Return(nil).Once() rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() @@ -1669,6 +1656,8 @@ 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.On("GetInterceptedChainInfo").Return(tc.NodeChainInfo, tc.PoolChainInfo).Once() node := newTestNode(t, testNodeOpts{ config: testNodeConfig{ enforceRepeatableRead: true, @@ -1677,11 +1666,11 @@ func TestNode_State(t *testing.T) { FinalizedBlockOffsetVal: tc.FinalizedBlockOffsetVal, IsFinalityTagEnabled: tc.IsFinalityTagEnabled, }, + rpc: rpc, }) poolInfo := newMockPoolChainInfoProvider(t) poolInfo.On("HighestChainInfo").Return(tc.PoolChainInfo).Once() node.SetPoolChainInfoProvider(poolInfo) - node.setLatestChainInfo(tc.NodeChainInfo) node.setState(nodeStateAlive) assert.Equal(t, tc.ExpectedState, node.State()) }) diff --git a/common/client/types.go b/common/client/types.go index 30473d83ecb..56a56a07c6e 100644 --- a/common/client/types.go +++ b/common/client/types.go @@ -66,6 +66,7 @@ type NodeClient[ 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) @@ -74,7 +75,17 @@ type NodeClient[ UnsubscribeAllExceptAliveLoop() IsSyncing(ctx context.Context) (bool, error) LatestFinalizedBlock(ctx context.Context) (HEAD, error) - GetInterceptedChainInfo() ChainInfo + // GetInterceptedChainInfo - returns latest and highest ChainInfo. + // Latest ChainInfo is the most recent value received within a NodeClient's current lifecycle between Dial and DisconnectAll. + // Highest ChainInfo is the highest ChainInfo observed by the NodeClient since the instance was created. + // Its values must not be reset. + // The results of corresponding calls, to get the most recent head and the latest finalized head, must be + // intercepted and reflected in ChainInfo before being returned to a caller. Otherwise, MultiNode is not able to + // provide repeatable read guarantee. + // DisconnectAll must reset latest ChainInfo to default value. + // Ensure implementation does not have a race condition when values are reset before request completion and as + // a result latest ChainInfo contains information from the previous cycle. + GetInterceptedChainInfo() (latest, highest ChainInfo) } // clientAPI includes all the direct RPC methods required by the generalized common client to implement its own. diff --git a/core/chains/evm/client/chain_client_test.go b/core/chains/evm/client/chain_client_test.go index 51693fecf3f..e69725dc657 100644 --- a/core/chains/evm/client/chain_client_test.go +++ b/core/chains/evm/client/chain_client_test.go @@ -50,6 +50,7 @@ func TestChainClient_BatchCallContext(t *testing.T) { } mockRpc := newMockRpc(t) + mockRpc.On("GetInterceptedChainInfo").Return(commonclient.ChainInfo{}, commonclient.ChainInfo{}) mockRpc.On("BatchCallContext", mock.Anything, b).Run(func(args mock.Arguments) { reqs := args.Get(1).([]rpc.BatchElem) for i := 0; i < len(reqs); i++ { diff --git a/core/chains/evm/client/mocks/rpc_client.go b/core/chains/evm/client/mocks/rpc_client.go index 8fbb4f169b2..0220ac4d030 100644 --- a/core/chains/evm/client/mocks/rpc_client.go +++ b/core/chains/evm/client/mocks/rpc_client.go @@ -445,7 +445,7 @@ func (_m *RPCClient) FilterEvents(ctx context.Context, query ethereum.FilterQuer } // GetInterceptedChainInfo provides a mock function with given fields: -func (_m *RPCClient) GetInterceptedChainInfo() commonclient.ChainInfo { +func (_m *RPCClient) GetInterceptedChainInfo() (commonclient.ChainInfo, commonclient.ChainInfo) { ret := _m.Called() if len(ret) == 0 { @@ -453,13 +453,23 @@ func (_m *RPCClient) GetInterceptedChainInfo() commonclient.ChainInfo { } 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) } - return r0 + if rf, ok := ret.Get(1).(func() commonclient.ChainInfo); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(commonclient.ChainInfo) + } + + return r0, r1 } // HeaderByHash provides a mock function with given fields: ctx, h diff --git a/core/chains/evm/client/node_lifecycle_test.go b/core/chains/evm/client/node_lifecycle_test.go index 878ecabe600..dfda7c7f3b5 100644 --- a/core/chains/evm/client/node_lifecycle_test.go +++ b/core/chains/evm/client/node_lifecycle_test.go @@ -13,6 +13,7 @@ import ( "go.uber.org/zap" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index 6b192e15614..fceefbe0d1f 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -56,7 +56,7 @@ type RPCClient interface { 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() commonclient.ChainInfo + GetInterceptedChainInfo() (latest, highest commonclient.ChainInfo) } type rpcClient struct { @@ -84,7 +84,9 @@ type rpcClient struct { chStopInFlight chan struct{} // intercepted values seen by callers of the rpcClient. Need to ensure MultiNode provides repeatable read guarantee - chainInfo commonclient.ChainInfo + highestChainInfo commonclient.ChainInfo + // most recent chain info observed during current lifecycle (reseted on DisconnectAll) + latestChainInfo commonclient.ChainInfo } // NewRPCCLient returns a new *rpcClient as commonclient.RPC @@ -248,15 +250,16 @@ func (r *rpcClient) registerSub(sub ethereum.Subscription) { r.subs = append(r.subs, sub) } -// disconnectAll disconnects all clients connected to the rpcClient -// WARNING: NOT THREAD-SAFE -// This must be called from within the r.stateMu lock +// DisconnectAll disconnects all clients connected to the rpcClient func (r *rpcClient) DisconnectAll() { + r.stateMu.Lock() + defer r.stateMu.Unlock() if r.ws.rpc != nil { r.ws.rpc.Close() } r.cancelInflightRequests() r.unsubscribeAll() + r.latestChainInfo = commonclient.ChainInfo{} } // unsubscribeAll unsubscribes all subscriptions @@ -349,9 +352,10 @@ func (r *rpcClient) SubscribeNewHead(ctx context.Context, channel chan<- *evmtyp lggr.Debug("RPC call: evmclient.Client#EthSubscribe") start := time.Now() + subscriptionCtxCh := r.getChStopInflight() subForwarder := newSubForwarder(channel, func(head *evmtypes.Head) *evmtypes.Head { head.EVMChainID = ubig.New(r.chainID) - r.oneNewHead(head) + r.oneNewHead(subscriptionCtxCh, head) return head }, func(err error) error { return fmt.Errorf("%s: %w", r.rpcClientErrorPrefix(), err) @@ -472,13 +476,14 @@ 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) (*evmtypes.Head, error) { + requestCtxCh := r.getChStopInflight() block, err := r.blockByNumber(ctx, rpc.FinalizedBlockNumber.String()) if err != nil { return nil, err } - r.oneNewFinalizedHead(block) + r.oneNewFinalizedHead(requestCtxCh, block) return block, nil } @@ -1077,28 +1082,41 @@ func Name(r *rpcClient) string { return r.name } -func (r *rpcClient) oneNewHead(head *evmtypes.Head) { +func (r *rpcClient) oneNewHead(requestCh <-chan struct{}, head *evmtypes.Head) { if head == nil { return } r.stateMu.Lock() defer r.stateMu.Unlock() - r.chainInfo.BlockNumber = max(r.chainInfo.BlockNumber, head.Number) - r.chainInfo.SetTotalDifficultyIfGt(head.TotalDifficulty) + r.highestChainInfo.BlockNumber = max(r.highestChainInfo.BlockNumber, head.Number) + r.highestChainInfo.SetTotalDifficultyIfGt(head.TotalDifficulty) + select { + case <-requestCh: // no need to update latestChainInfo, as rpcClient already started new life cycle + return + default: + r.latestChainInfo.BlockNumber = head.Number + r.latestChainInfo.TotalDifficulty = head.TotalDifficulty + } } -func (r *rpcClient) oneNewFinalizedHead(head *evmtypes.Head) { +func (r *rpcClient) oneNewFinalizedHead(requestCh <-chan struct{}, head *evmtypes.Head) { if head == nil { return } r.stateMu.Lock() defer r.stateMu.Unlock() - r.chainInfo.FinalizedBlockNumber = max(r.chainInfo.FinalizedBlockNumber, head.Number) + r.highestChainInfo.FinalizedBlockNumber = max(r.highestChainInfo.FinalizedBlockNumber, head.Number) + select { + 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() commonclient.ChainInfo { +func (r *rpcClient) GetInterceptedChainInfo() (latest, highest commonclient.ChainInfo) { r.stateMu.RLock() defer r.stateMu.RUnlock() - return r.chainInfo + return r.latestChainInfo, r.highestChainInfo } diff --git a/core/chains/evm/client/rpc_client_test.go b/core/chains/evm/client/rpc_client_test.go index 4566f3943e7..fa4f936b7ff 100644 --- a/core/chains/evm/client/rpc_client_test.go +++ b/core/chains/evm/client/rpc_client_test.go @@ -24,6 +24,14 @@ import ( evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ) +func makeNewHeadWSMessage(head *evmtypes.Head) string { + asJSON, err := json.Marshal(head) + if err != nil { + panic(fmt.Errorf("failed to marshal head: %w", err)) + } + return fmt.Sprintf(`{"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x00","result":%s}}`, string(asJSON)) +} + func TestRPCClient_SubscribeNewHead(t *testing.T) { t.Parallel() ctx, cancel := context.WithTimeout(tests.Context(t), tests.WaitTimeout(t)) @@ -32,71 +40,79 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { chainId := big.NewInt(123456) lggr := logger.Test(t) - type rpcServer struct { - Head *evmtypes.Head - URL *url.URL - } - createRPCServer := func() *rpcServer { - server := &rpcServer{} - server.URL = testutils.NewWSServer(t, chainId, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { - if method == "eth_unsubscribe" { - resp.Result = "true" - return - } - assert.Equal(t, "eth_subscribe", method) - if assert.True(t, params.IsArray()) && assert.Equal(t, "newHeads", params.Array()[0].String()) { - resp.Result = `"0x00"` - head := server.Head - jsonHead, err := json.Marshal(head) - if err != nil { - panic(fmt.Errorf("failed to marshal head: %w", err)) - } - resp.Notify = string(jsonHead) - } + serverCallBack := func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + if method == "eth_unsubscribe" { + resp.Result = "true" return - }).WSURL() - - return server + } + assert.Equal(t, "eth_subscribe", method) + if assert.True(t, params.IsArray()) && assert.Equal(t, "newHeads", params.Array()[0].String()) { + resp.Result = `"0x00"` + } + return } - receiveNewHead := func(rpc client.RPCClient) *evmtypes.Head { + t.Run("Updates chain info on new blocks", func(t *testing.T) { + server := testutils.NewWSServer(t, chainId, serverCallBack) + wsURL := server.WSURL() + + rpc := client.NewRPCClient(lggr, *wsURL, nil, "rpc", 1, chainId, commonclient.Primary) + defer rpc.Close() + require.NoError(t, rpc.Dial(ctx)) + // set to default values + latest, highest := rpc.GetInterceptedChainInfo() + assert.Equal(t, int64(0), latest.BlockNumber) + assert.Equal(t, int64(0), latest.FinalizedBlockNumber) + assert.Nil(t, latest.TotalDifficulty) + assert.Equal(t, int64(0), highest.BlockNumber) + assert.Equal(t, int64(0), highest.FinalizedBlockNumber) + assert.Nil(t, highest.TotalDifficulty) + ch := make(chan *evmtypes.Head) sub, err := rpc.SubscribeNewHead(tests.Context(t), ch) require.NoError(t, err) - result := <-ch - sub.Unsubscribe() - return result - } - t.Run("Updates latest block info in InterceptedChainInfo", func(t *testing.T) { - server := createRPCServer() - rpc := client.NewRPCClient(lggr, *server.URL, nil, "rpc", 1, chainId, commonclient.Primary) - defer rpc.Close() - require.NoError(t, rpc.Dial(ctx)) - chainInfo := rpc.GetInterceptedChainInfo() - require.Equal(t, int64(0), chainInfo.BlockNumber) - require.Equal(t, int64(0), chainInfo.FinalizedBlockNumber) - server.Head = &evmtypes.Head{ - Number: 256, - TotalDifficulty: big.NewInt(1000), - } - _ = receiveNewHead(rpc) - server.Head = &evmtypes.Head{ - Number: 128, - TotalDifficulty: big.NewInt(1000), + 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 + + latest, highest = rpc.GetInterceptedChainInfo() + assert.Equal(t, int64(128), latest.BlockNumber) + assert.Equal(t, int64(0), latest.FinalizedBlockNumber) + assert.Equal(t, big.NewInt(500), latest.TotalDifficulty) + + assertHighest := func(highest commonclient.ChainInfo) { + assert.Equal(t, int64(256), highest.BlockNumber) + assert.Equal(t, int64(0), highest.FinalizedBlockNumber) + assert.Equal(t, big.NewInt(1000), highest.TotalDifficulty) } - _ = receiveNewHead(rpc) - chainInfo = rpc.GetInterceptedChainInfo() - assert.Equal(t, int64(256), chainInfo.BlockNumber) - assert.Equal(t, int64(0), chainInfo.FinalizedBlockNumber) + + assertHighest(highest) + + // DisconnectAll resets latest + rpc.DisconnectAll() + + latest, highest = rpc.GetInterceptedChainInfo() + assert.Equal(t, int64(0), latest.BlockNumber) + assert.Equal(t, int64(0), latest.FinalizedBlockNumber) + assert.Nil(t, latest.TotalDifficulty) + + assertHighest(highest) }) t.Run("Block's chain ID matched configured", func(t *testing.T) { - server := createRPCServer() - rpc := client.NewRPCClient(lggr, *server.URL, nil, "rpc", 1, chainId, commonclient.Primary) + server := testutils.NewWSServer(t, chainId, serverCallBack) + wsURL := server.WSURL() + rpc := client.NewRPCClient(lggr, *wsURL, nil, "rpc", 1, chainId, commonclient.Primary) defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) - server.Head = &evmtypes.Head{ - Number: 256, - } - head := receiveNewHead(rpc) + ch := make(chan *evmtypes.Head) + sub, err := rpc.SubscribeNewHead(tests.Context(t), ch) + 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 proper error", func(t *testing.T) { @@ -111,21 +127,14 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { require.ErrorContains(t, err, "RPCClient returned error (rpc)") }) t.Run("Subscription error is properly wrapper", func(t *testing.T) { - server := testutils.NewWSServer(t, chainId, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { - assert.Equal(t, "eth_subscribe", method) - if assert.True(t, params.IsArray()) && assert.Equal(t, "newHeads", params.Array()[0].String()) { - resp.Result = `"0x00"` - resp.Notify = "{}" - } - return resp - }) + server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() rpc := client.NewRPCClient(lggr, *wsURL, nil, "rpc", 1, chainId, commonclient.Primary) defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) sub, err := rpc.SubscribeNewHead(ctx, make(chan *evmtypes.Head)) require.NoError(t, err) - server.MustWriteBinaryMessageSync(t, "invalid msg") + go server.MustWriteBinaryMessageSync(t, "invalid msg") select { case err = <-sub.Err(): require.ErrorContains(t, err, "RPCClient returned error (rpc): invalid character") @@ -168,7 +177,7 @@ func TestRPCClient_SubscribeFilterLogs(t *testing.T) { require.NoError(t, rpc.Dial(ctx)) sub, err := rpc.SubscribeFilterLogs(ctx, ethereum.FilterQuery{}, make(chan types.Log)) require.NoError(t, err) - server.MustWriteBinaryMessageSync(t, "invalid msg") + go server.MustWriteBinaryMessageSync(t, "invalid msg") errorCtx, cancel := context.WithTimeout(ctx, tests.DefaultWaitTimeout) defer cancel() select { @@ -204,6 +213,7 @@ func TestRPCClient_LatestFinalizedBlock(t *testing.T) { } resp.Result = string(jsonHead) } + return }).WSURL() @@ -218,15 +228,32 @@ func TestRPCClient_LatestFinalizedBlock(t *testing.T) { // updates chain info _, err := rpc.LatestFinalizedBlock(ctx) require.NoError(t, err) - chainInfo := rpc.GetInterceptedChainInfo() - assert.Equal(t, int64(0), chainInfo.BlockNumber) - assert.Equal(t, int64(128), chainInfo.FinalizedBlockNumber) + latest, highest := rpc.GetInterceptedChainInfo() + + assert.Equal(t, int64(0), highest.BlockNumber) + assert.Equal(t, int64(128), highest.FinalizedBlockNumber) + + assert.Equal(t, int64(0), latest.BlockNumber) + assert.Equal(t, int64(128), latest.FinalizedBlockNumber) // lower block number does not update Highest server.Head = &evmtypes.Head{Number: 127} _, err = rpc.LatestFinalizedBlock(ctx) require.NoError(t, err) - chainInfo = rpc.GetInterceptedChainInfo() - assert.Equal(t, int64(0), chainInfo.BlockNumber) - assert.Equal(t, int64(128), chainInfo.FinalizedBlockNumber) + latest, highest = rpc.GetInterceptedChainInfo() + + assert.Equal(t, int64(0), highest.BlockNumber) + assert.Equal(t, int64(128), highest.FinalizedBlockNumber) + + assert.Equal(t, int64(0), latest.BlockNumber) + assert.Equal(t, int64(127), latest.FinalizedBlockNumber) + + // DisconnectAll resets latest ChainInfo + rpc.DisconnectAll() + latest, highest = rpc.GetInterceptedChainInfo() + assert.Equal(t, int64(0), highest.BlockNumber) + assert.Equal(t, int64(128), highest.FinalizedBlockNumber) + + assert.Equal(t, int64(0), latest.BlockNumber) + assert.Equal(t, int64(0), latest.FinalizedBlockNumber) } From bfe7a5408bb024a9e7c674bbabc82f454ce3b1d2 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Mon, 13 May 2024 18:55:32 +0200 Subject: [PATCH 15/53] EnforceRepeatableRead config option --- core/chains/evm/client/config_builder.go | 24 ++++--- core/chains/evm/client/config_builder_test.go | 7 +- core/chains/evm/client/evm_client_test.go | 5 +- .../evm/config/chain_scoped_node_pool.go | 3 +- core/chains/evm/config/config_test.go | 1 + core/chains/evm/config/toml/config.go | 5 ++ .../evm/config/toml/defaults/fallback.toml | 1 + core/config/docs/chains-evm.toml | 6 ++ core/services/chainlink/config_test.go | 2 + .../chainlink/testdata/config-full.toml | 1 + .../config-multi-chain-effective.toml | 3 + core/web/resolver/testdata/config-full.toml | 1 + .../config-multi-chain-effective.toml | 3 + docs/CONFIG.md | 70 +++++++++++++++++++ .../disk-based-logging-disabled.txtar | 1 + .../validate/disk-based-logging-no-dir.txtar | 1 + .../node/validate/disk-based-logging.txtar | 1 + testdata/scripts/node/validate/invalid.txtar | 1 + testdata/scripts/node/validate/valid.txtar | 1 + 19 files changed, 123 insertions(+), 14 deletions(-) diff --git a/core/chains/evm/client/config_builder.go b/core/chains/evm/client/config_builder.go index d78a981b881..8a863ae463b 100644 --- a/core/chains/evm/client/config_builder.go +++ b/core/chains/evm/client/config_builder.go @@ -37,6 +37,8 @@ func NewClientConfigs( noNewHeadsThreshold time.Duration, finalityDepth *uint32, finalityTagEnabled *bool, + finalityBlockOffset *uint32, + enforceRepeatableRead *bool, ) (commonclient.ChainConfig, evmconfig.NodePool, []*toml.Node, error) { nodes, err := parseNodeConfigs(nodeCfgs) @@ -44,21 +46,23 @@ func NewClientConfigs( return nil, nil, nil, err } nodePool := toml.NodePool{ - SelectionMode: selectionMode, - LeaseDuration: commonconfig.MustNewDuration(leaseDuration), - PollFailureThreshold: pollFailureThreshold, - PollInterval: commonconfig.MustNewDuration(pollInterval), - SyncThreshold: syncThreshold, - NodeIsSyncingEnabled: nodeIsSyncingEnabled, + SelectionMode: selectionMode, + LeaseDuration: commonconfig.MustNewDuration(leaseDuration), + PollFailureThreshold: pollFailureThreshold, + PollInterval: commonconfig.MustNewDuration(pollInterval), + SyncThreshold: syncThreshold, + NodeIsSyncingEnabled: nodeIsSyncingEnabled, + EnforceRepeatableRead: enforceRepeatableRead, } nodePoolCfg := &evmconfig.NodePoolConfig{C: nodePool} chainConfig := &evmconfig.EVMConfig{ C: &toml.EVMConfig{ Chain: toml.Chain{ - ChainType: &chainType, - FinalityDepth: finalityDepth, - FinalityTagEnabled: finalityTagEnabled, - NoNewHeadsThreshold: commonconfig.MustNewDuration(noNewHeadsThreshold), + ChainType: &chainType, + FinalityDepth: finalityDepth, + FinalityTagEnabled: finalityTagEnabled, + NoNewHeadsThreshold: commonconfig.MustNewDuration(noNewHeadsThreshold), + FinalizedBlockOffset: finalityBlockOffset, }, }, } diff --git a/core/chains/evm/client/config_builder_test.go b/core/chains/evm/client/config_builder_test.go index 963cb855023..d40e2c1b02f 100644 --- a/core/chains/evm/client/config_builder_test.go +++ b/core/chains/evm/client/config_builder_test.go @@ -22,6 +22,8 @@ func TestClientConfigBuilder(t *testing.T) { syncThreshold := ptr(uint32(5)) nodeIsSyncingEnabled := ptr(false) chainTypeStr := "" + finalizedBlockOffset := ptr[uint32](16) + enforceRepeatableRead := ptr(true) nodeConfigs := []client.NodeConfig{ { Name: ptr("foo"), @@ -33,7 +35,8 @@ func TestClientConfigBuilder(t *testing.T) { finalityTagEnabled := ptr(true) noNewHeadsThreshold := time.Second chainCfg, nodePool, nodes, err := client.NewClientConfigs(selectionMode, leaseDuration, chainTypeStr, nodeConfigs, - pollFailureThreshold, pollInterval, syncThreshold, nodeIsSyncingEnabled, noNewHeadsThreshold, finalityDepth, finalityTagEnabled) + pollFailureThreshold, pollInterval, syncThreshold, nodeIsSyncingEnabled, noNewHeadsThreshold, finalityDepth, + finalityTagEnabled, finalizedBlockOffset, enforceRepeatableRead) require.NoError(t, err) // Validate node pool configs @@ -43,6 +46,7 @@ func TestClientConfigBuilder(t *testing.T) { require.Equal(t, pollInterval, nodePool.PollInterval()) require.Equal(t, *syncThreshold, nodePool.SyncThreshold()) require.Equal(t, *nodeIsSyncingEnabled, nodePool.NodeIsSyncingEnabled()) + require.Equal(t, *enforceRepeatableRead, nodePool.EnforceRepeatableRead()) // Validate node configs require.Equal(t, *nodeConfigs[0].Name, *nodes[0].Name) @@ -54,6 +58,7 @@ func TestClientConfigBuilder(t *testing.T) { require.Equal(t, noNewHeadsThreshold, chainCfg.NodeNoNewHeadsThreshold()) require.Equal(t, *finalityDepth, chainCfg.FinalityDepth()) require.Equal(t, *finalityTagEnabled, chainCfg.FinalityTagEnabled()) + require.Equal(t, *finalizedBlockOffset, chainCfg.FinalizedBlockOffset()) // 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) diff --git a/core/chains/evm/client/evm_client_test.go b/core/chains/evm/client/evm_client_test.go index ea2eed3b240..ea6fcd5144d 100644 --- a/core/chains/evm/client/evm_client_test.go +++ b/core/chains/evm/client/evm_client_test.go @@ -23,6 +23,8 @@ func TestNewEvmClient(t *testing.T) { syncThreshold := ptr(uint32(5)) nodeIsSyncingEnabled := ptr(false) chainTypeStr := "" + finalizedBlockOffset := ptr[uint32](16) + enforceRepeatableRead := ptr(true) nodeConfigs := []client.NodeConfig{ { Name: ptr("foo"), @@ -33,7 +35,8 @@ func TestNewEvmClient(t *testing.T) { finalityDepth := ptr(uint32(10)) finalityTagEnabled := ptr(true) chainCfg, nodePool, nodes, err := client.NewClientConfigs(selectionMode, leaseDuration, chainTypeStr, nodeConfigs, - pollFailureThreshold, pollInterval, syncThreshold, nodeIsSyncingEnabled, noNewHeadsThreshold, finalityDepth, finalityTagEnabled) + pollFailureThreshold, pollInterval, syncThreshold, nodeIsSyncingEnabled, noNewHeadsThreshold, finalityDepth, + finalityTagEnabled, finalizedBlockOffset, enforceRepeatableRead) require.NoError(t, err) client := client.NewEvmClient(nodePool, chainCfg, nil, logger.Test(t), testutils.FixtureChainID, nodes) diff --git a/core/chains/evm/config/chain_scoped_node_pool.go b/core/chains/evm/config/chain_scoped_node_pool.go index 91efbc5ec02..3a6d5602589 100644 --- a/core/chains/evm/config/chain_scoped_node_pool.go +++ b/core/chains/evm/config/chain_scoped_node_pool.go @@ -41,6 +41,5 @@ func (n *NodePoolConfig) FinalizedBlockPollInterval() time.Duration { func (n *NodePoolConfig) Errors() ClientErrors { return &clientErrorsConfig{c: n.C.Errors} } func (n *NodePoolConfig) EnforceRepeatableRead() bool { - // TODO: DH implement me - return false + return *n.C.EnforceRepeatableRead } diff --git a/core/chains/evm/config/config_test.go b/core/chains/evm/config/config_test.go index 9553f59ad61..0d48eabf6ff 100644 --- a/core/chains/evm/config/config_test.go +++ b/core/chains/evm/config/config_test.go @@ -478,6 +478,7 @@ func TestNodePoolConfig(t *testing.T) { require.Equal(t, time.Duration(10000000000), cfg.EVM().NodePool().PollInterval()) require.Equal(t, uint32(5), cfg.EVM().NodePool().PollFailureThreshold()) require.Equal(t, false, cfg.EVM().NodePool().NodeIsSyncingEnabled()) + require.Equal(t, false, cfg.EVM().NodePool().EnforceRepeatableRead()) } func TestClientErrorsConfig(t *testing.T) { diff --git a/core/chains/evm/config/toml/config.go b/core/chains/evm/config/toml/config.go index 23a0c6c166d..e87218294b4 100644 --- a/core/chains/evm/config/toml/config.go +++ b/core/chains/evm/config/toml/config.go @@ -775,6 +775,7 @@ type NodePool struct { NodeIsSyncingEnabled *bool FinalizedBlockPollInterval *commonconfig.Duration Errors ClientErrors `toml:",omitempty"` + EnforceRepeatableRead *bool } func (p *NodePool) setFrom(f *NodePool) { @@ -799,6 +800,10 @@ func (p *NodePool) setFrom(f *NodePool) { if v := f.FinalizedBlockPollInterval; v != nil { p.FinalizedBlockPollInterval = v } + + if v := f.EnforceRepeatableRead; v != nil { + p.EnforceRepeatableRead = v + } p.Errors.setFrom(&f.Errors) } diff --git a/core/chains/evm/config/toml/defaults/fallback.toml b/core/chains/evm/config/toml/defaults/fallback.toml index 8d963e685c4..b3a627fb263 100644 --- a/core/chains/evm/config/toml/defaults/fallback.toml +++ b/core/chains/evm/config/toml/defaults/fallback.toml @@ -64,6 +64,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 diff --git a/core/config/docs/chains-evm.toml b/core/config/docs/chains-evm.toml index 15c1f7d3e6d..809c9554bc2 100644 --- a/core/config/docs/chains-evm.toml +++ b/core/config/docs/chains-evm.toml @@ -356,6 +356,12 @@ NodeIsSyncingEnabled = false # Default # # Set to 0 to disable. FinalizedBlockPollInterval = '5s' # Default +# EnforceRepeatableRead defines if Core should only use RPCs whose most recently finalized block is greater or equal to +# `highest finalized block - FinalizedBlockOffset`. In other words, exclude RPCs lagging on latest finalized +# block. +# +# Set false to disable +EnforceRepeatableRead = false # Default # **ADVANCED** # Errors enable the node to provide custom regex patterns to match against error messages from RPCs. diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 2486ae67b71..c2059d3de3c 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -586,6 +586,7 @@ func TestConfig_Marshal(t *testing.T) { LeaseDuration: &zeroSeconds, NodeIsSyncingEnabled: ptr(true), FinalizedBlockPollInterval: &second, + EnforceRepeatableRead: ptr(true), Errors: evmcfg.ClientErrors{ NonceTooLow: ptr[string]("(: |^)nonce too low"), NonceTooHigh: ptr[string]("(: |^)nonce too high"), @@ -1048,6 +1049,7 @@ SyncThreshold = 13 LeaseDuration = '0s' NodeIsSyncingEnabled = true FinalizedBlockPollInterval = '1s' +EnforceRepeatableRead = true [EVM.NodePool.Errors] NonceTooLow = '(: |^)nonce too low' diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index 8fcc26a121f..29b7d004b68 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -348,6 +348,7 @@ SyncThreshold = 13 LeaseDuration = '0s' NodeIsSyncingEnabled = true FinalizedBlockPollInterval = '1s' +EnforceRepeatableRead = true [EVM.NodePool.Errors] NonceTooLow = '(: |^)nonce too low' diff --git a/core/services/chainlink/testdata/config-multi-chain-effective.toml b/core/services/chainlink/testdata/config-multi-chain-effective.toml index d2e3edc7467..69feab4cd17 100644 --- a/core/services/chainlink/testdata/config-multi-chain-effective.toml +++ b/core/services/chainlink/testdata/config-multi-chain-effective.toml @@ -319,6 +319,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [EVM.OCR] ContractConfirmations = 4 @@ -411,6 +412,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [EVM.OCR] ContractConfirmations = 4 @@ -497,6 +499,7 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [EVM.OCR] ContractConfirmations = 4 diff --git a/core/web/resolver/testdata/config-full.toml b/core/web/resolver/testdata/config-full.toml index 630bdf6d5da..ed44cc48649 100644 --- a/core/web/resolver/testdata/config-full.toml +++ b/core/web/resolver/testdata/config-full.toml @@ -347,6 +347,7 @@ SyncThreshold = 13 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [EVM.NodePool.Errors] NonceTooLow = '(: |^)nonce too low' diff --git a/core/web/resolver/testdata/config-multi-chain-effective.toml b/core/web/resolver/testdata/config-multi-chain-effective.toml index eada9a7a56c..1dcd10b92d1 100644 --- a/core/web/resolver/testdata/config-multi-chain-effective.toml +++ b/core/web/resolver/testdata/config-multi-chain-effective.toml @@ -319,6 +319,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [EVM.OCR] ContractConfirmations = 4 @@ -411,6 +412,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [EVM.OCR] ContractConfirmations = 4 @@ -497,6 +499,7 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [EVM.OCR] ContractConfirmations = 4 diff --git a/docs/CONFIG.md b/docs/CONFIG.md index a75f126dbe8..b67f03e758d 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -1799,6 +1799,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -1885,6 +1886,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -1971,6 +1973,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -2057,6 +2060,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -2144,6 +2148,7 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -2230,6 +2235,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -2316,6 +2322,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -2403,6 +2410,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -2489,6 +2497,7 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -2574,6 +2583,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -2659,6 +2669,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -2745,6 +2756,7 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -2832,6 +2844,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -2918,6 +2931,7 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -3004,6 +3018,7 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -3090,6 +3105,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -3176,6 +3192,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -3262,6 +3279,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -3348,6 +3366,7 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -3434,6 +3453,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -3520,6 +3540,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -3606,6 +3627,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -3693,6 +3715,7 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -3779,6 +3802,7 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -3864,6 +3888,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -3950,6 +3975,7 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -4035,6 +4061,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -4121,6 +4148,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -4207,6 +4235,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -4292,6 +4321,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -4377,6 +4407,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -4463,6 +4494,7 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -4548,6 +4580,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -4634,6 +4667,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -4719,6 +4753,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -4805,6 +4840,7 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -4891,6 +4927,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -4978,6 +5015,7 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -5064,6 +5102,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -5150,6 +5189,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -5236,6 +5276,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -5322,6 +5363,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -5407,6 +5449,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -5492,6 +5535,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -5577,6 +5621,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -5663,6 +5708,7 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -5749,6 +5795,7 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -5834,6 +5881,7 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -5920,6 +5968,7 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -6006,6 +6055,7 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -6093,6 +6143,7 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -6180,6 +6231,7 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -6266,6 +6318,7 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -6352,6 +6405,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -6438,6 +6492,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -6524,6 +6579,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -6610,6 +6666,7 @@ SyncThreshold = 10 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 1 @@ -6696,6 +6753,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -6782,6 +6840,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [OCR] ContractConfirmations = 4 @@ -7435,6 +7494,7 @@ SyncThreshold = 5 # Default LeaseDuration = '0s' # Default NodeIsSyncingEnabled = false # Default FinalizedBlockPollInterval = '5s' # Default +EnforceRepeatableRead = false # Default ``` The node pool manages multiple RPC endpoints. @@ -7508,6 +7568,16 @@ reported based on latest block and finality depth. Set to 0 to disable. +### EnforceRepeatableRead +```toml +EnforceRepeatableRead = false # Default +``` +EnforceRepeatableRead defines if Core should only use RPCs whose most recently finalized block is greater or equal to +`highest finalized block - FinalizedBlockOffset`. In other words, exclude RPCs lagging on latest finalized +block. + +Set false to disable + ## EVM.NodePool.Errors :warning: **_ADVANCED_**: _Do not change these settings unless you know what you are doing._ ```toml diff --git a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar index e84155a399d..f5088ccc99b 100644 --- a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar @@ -375,6 +375,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [EVM.OCR] ContractConfirmations = 4 diff --git a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar index 95f42dc810d..ad081c4a2a0 100644 --- a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar @@ -375,6 +375,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [EVM.OCR] ContractConfirmations = 4 diff --git a/testdata/scripts/node/validate/disk-based-logging.txtar b/testdata/scripts/node/validate/disk-based-logging.txtar index 333250ad217..783aa8fed04 100644 --- a/testdata/scripts/node/validate/disk-based-logging.txtar +++ b/testdata/scripts/node/validate/disk-based-logging.txtar @@ -375,6 +375,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [EVM.OCR] ContractConfirmations = 4 diff --git a/testdata/scripts/node/validate/invalid.txtar b/testdata/scripts/node/validate/invalid.txtar index 96f45f5769a..e46b73dbe27 100644 --- a/testdata/scripts/node/validate/invalid.txtar +++ b/testdata/scripts/node/validate/invalid.txtar @@ -365,6 +365,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [EVM.OCR] ContractConfirmations = 4 diff --git a/testdata/scripts/node/validate/valid.txtar b/testdata/scripts/node/validate/valid.txtar index e7ec931fac0..adf0f8eb6b3 100644 --- a/testdata/scripts/node/validate/valid.txtar +++ b/testdata/scripts/node/validate/valid.txtar @@ -372,6 +372,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [EVM.OCR] ContractConfirmations = 4 From 71117fe031b038b061e3b7c3e00cd4f3321d8be7 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Mon, 13 May 2024 23:23:46 +0200 Subject: [PATCH 16/53] ensure HeadTracked does not miss block if it's committed between initialHead processing and subscription --- common/headtracker/head_listener.go | 6 ++++-- common/headtracker/head_tracker.go | 11 +++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/common/headtracker/head_listener.go b/common/headtracker/head_listener.go index 15977c4dfe4..25715b35280 100644 --- a/common/headtracker/head_listener.go +++ b/common/headtracker/head_listener.go @@ -36,7 +36,7 @@ type headHandler[H types.Head[BLOCK_HASH], BLOCK_HASH types.Hashable] func(ctx c type HeadListener[H types.Head[BLOCK_HASH], BLOCK_HASH types.Hashable] interface { // ListenForNewHeads kicks off the listen loop (not thread safe) // done() must be executed upon leaving ListenForNewHeads() - ListenForNewHeads(handleNewHead headHandler[H, BLOCK_HASH], done func()) + ListenForNewHeads(onSubscribe func(), handleNewHead headHandler[H, BLOCK_HASH], done func()) // ReceivingHeads returns true if the listener is receiving heads (thread safe) ReceivingHeads() bool @@ -88,7 +88,7 @@ func (hl *headListener[HTH, S, ID, BLOCK_HASH]) Name() string { return hl.logger.Name() } -func (hl *headListener[HTH, S, ID, BLOCK_HASH]) ListenForNewHeads(handleNewHead headHandler[HTH, BLOCK_HASH], done func()) { +func (hl *headListener[HTH, S, ID, BLOCK_HASH]) ListenForNewHeads(onSubscription func(), handleNewHead headHandler[HTH, BLOCK_HASH], done func()) { defer done() defer hl.unsubscribe() @@ -99,6 +99,8 @@ func (hl *headListener[HTH, S, ID, BLOCK_HASH]) ListenForNewHeads(handleNewHead if !hl.subscribe(ctx) { break } + + onSubscription() err := hl.receiveHeaders(ctx, handleNewHead) if ctx.Err() != nil { break diff --git a/common/headtracker/head_tracker.go b/common/headtracker/head_tracker.go index 6821f397c7b..ebceba8a233 100644 --- a/common/headtracker/head_tracker.go +++ b/common/headtracker/head_tracker.go @@ -116,16 +116,15 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) Start(ctx context.Context) error // anyway when we connect (but we should not rely on this because it is // not specced). If it happens this is fine, and the head will be // ignored as a duplicate. - err := ht.handleInitialHead(ctx) - if err != nil { - if ctx.Err() != nil { - return ctx.Err() + onSubscribe := func() { + err := ht.handleInitialHead(ctx) + if err != nil { + ht.log.Errorw("Error handling initial head", "err", err) } - ht.log.Errorw("Error handling initial head", "err", err) } ht.wgDone.Add(3) - go ht.headListener.ListenForNewHeads(ht.handleNewHead, ht.wgDone.Done) + go ht.headListener.ListenForNewHeads(onSubscribe, ht.handleNewHead, ht.wgDone.Done) go ht.backfillLoop() go ht.broadcastLoop() From 32511e3cf3ae98726f9ae2e20700e652a0b1973b Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Mon, 13 May 2024 23:24:15 +0200 Subject: [PATCH 17/53] hot fix flakey test --- core/services/ocr2/plugins/llo/integration_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/services/ocr2/plugins/llo/integration_test.go b/core/services/ocr2/plugins/llo/integration_test.go index b6f752541f4..79635ccc8e0 100644 --- a/core/services/ocr2/plugins/llo/integration_test.go +++ b/core/services/ocr2/plugins/llo/integration_test.go @@ -19,6 +19,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + chainselectors "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" @@ -162,6 +164,16 @@ func TestIntegration_LLO(t *testing.T) { addBootstrapJob(t, bootstrapNode, chainID, verifierAddress, "job-1") addOCRJobs(t, streams, serverPubKey, serverURL, verifierAddress, bootstrapPeerID, bootstrapNodePort, nodes, configStoreAddress, clientPubKeys, chainID, fromBlock) + go func() { + // TODO: DH fix me + time.Sleep(10 * time.Second) + for _, node := range append(nodes, bootstrapNode) { + chain, _ := node.App.GetRelayers().LegacyEVMChains().Get(testutils.SimulatedChainID.String()) + err = chain.LogPoller().Replay(tests.Context(t), 1) + require.NoError(t, err) + } + }() + t.Run("receives at least one report per feed from each oracle when EAs are at 100% reliability", func(t *testing.T) { // Expect at least one report per channel from each oracle (keyed by transmitter ID) seen := make(map[ocr2types.Account]map[llotypes.ChannelID]struct{}) From fffaa3bc8c5a1442b82a27af33dc9b28d69aca97 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Mon, 13 May 2024 23:48:00 +0200 Subject: [PATCH 18/53] fix build & changeset --- .changeset/dull-ants-collect.md | 9 +++++++++ core/chains/evm/headtracker/head_listener_test.go | 7 ++++--- core/chains/evm/headtracker/head_tracker_test.go | 7 ++++--- 3 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 .changeset/dull-ants-collect.md diff --git a/.changeset/dull-ants-collect.md b/.changeset/dull-ants-collect.md new file mode 100644 index 00000000000..78f608e3418 --- /dev/null +++ b/.changeset/dull-ants-collect.md @@ -0,0 +1,9 @@ +--- +"chainlink": patch +--- + +Fixed local finality violation caused by an RPC lagging behind on latest finalized block. + +Added `EVM.FinalizedBlockOffset` and `EVM.NodePool.EnforceRepeatableRead` config options. +With `EnforceRepeatableRead = true`, RPC is considered healthy only if its most recent finalized block is larger or equal to the highest finalized block observed by the Node minus `FinalizedBlockOffset`. +#bugfix diff --git a/core/chains/evm/headtracker/head_listener_test.go b/core/chains/evm/headtracker/head_listener_test.go index 4e7efb5e809..e720b76213c 100644 --- a/core/chains/evm/headtracker/head_listener_test.go +++ b/core/chains/evm/headtracker/head_listener_test.go @@ -14,6 +14,7 @@ import ( commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/logger" + commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -73,7 +74,7 @@ func Test_HeadListener_HappyPath(t *testing.T) { done := func() { doneAwaiter.ItHappened() } - go hl.ListenForNewHeads(handler, done) + go hl.ListenForNewHeads(func() {}, handler, done) subscribeAwaiter.AwaitOrFail(t, testutils.WaitTimeout(t)) require.Eventually(t, hl.Connected, testutils.WaitTimeout(t), testutils.TestInterval) @@ -133,7 +134,7 @@ func Test_HeadListener_NotReceivingHeads(t *testing.T) { done := func() { doneAwaiter.ItHappened() } - go hl.ListenForNewHeads(handler, done) + go hl.ListenForNewHeads(func() {}, handler, done) subscribeAwaiter.AwaitOrFail(t, testutils.WaitTimeout(t)) @@ -195,7 +196,7 @@ func Test_HeadListener_SubscriptionErr(t *testing.T) { subscribeAwaiter.ItHappened() }) go func() { - hl.ListenForNewHeads(hnh, done) + hl.ListenForNewHeads(func() {}, hnh, done) }() // Put a head on the channel to ensure we test all code paths diff --git a/core/chains/evm/headtracker/head_tracker_test.go b/core/chains/evm/headtracker/head_tracker_test.go index 587855813d1..2fdf987af23 100644 --- a/core/chains/evm/headtracker/head_tracker_test.go +++ b/core/chains/evm/headtracker/head_tracker_test.go @@ -71,9 +71,10 @@ func TestHeadTracker_New(t *testing.T) { ht := createHeadTracker(t, ethClient, evmcfg.EVM(), evmcfg.EVM().HeadTracker(), orm) ht.Start(t) - latest := ht.headSaver.LatestChain() - require.NotNil(t, latest) - assert.Equal(t, last.Number, latest.Number) + tests.AssertEventually(t, func() bool { + latest := ht.headSaver.LatestChain() + return latest != nil && last.Number == latest.Number + }) } func TestHeadTracker_MarkFinalized_MarksAndTrimsTable(t *testing.T) { From 2bdc0f09442b2564f175e7a95962762e969f7f6a Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Tue, 14 May 2024 00:15:18 +0200 Subject: [PATCH 19/53] remove outdated test --- core/chains/evm/headtracker/head_tracker_test.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/core/chains/evm/headtracker/head_tracker_test.go b/core/chains/evm/headtracker/head_tracker_test.go index 2fdf987af23..881700e6f98 100644 --- a/core/chains/evm/headtracker/head_tracker_test.go +++ b/core/chains/evm/headtracker/head_tracker_test.go @@ -216,16 +216,6 @@ func TestHeadTracker_Start(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) return createHeadTracker(t, ethClient, config.EVM(), config.EVM().HeadTracker(), orm) } - - t.Run("Fail start if context was canceled", func(t *testing.T) { - ctx, cancel := context.WithCancel(testutils.Context(t)) - ht := newHeadTracker(t) - ht.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Run(func(args mock.Arguments) { - cancel() - }).Return(cltest.Head(0), context.Canceled) - err := ht.headTracker.Start(ctx) - require.ErrorIs(t, err, context.Canceled) - }) t.Run("Starts even if failed to get initialHead", func(t *testing.T) { ht := newHeadTracker(t) ht.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(cltest.Head(0), errors.New("failed to get init head")) From 4a0082554a996517303104a11c62669b326e2613 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Tue, 14 May 2024 01:04:54 +0200 Subject: [PATCH 20/53] fix headtracker tests --- .../evm/headtracker/head_tracker_test.go | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/core/chains/evm/headtracker/head_tracker_test.go b/core/chains/evm/headtracker/head_tracker_test.go index 881700e6f98..ef9100ad436 100644 --- a/core/chains/evm/headtracker/head_tracker_test.go +++ b/core/chains/evm/headtracker/head_tracker_test.go @@ -60,6 +60,12 @@ func TestHeadTracker_New(t *testing.T) { ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(cltest.Head(0), nil) // finalized ethClient.On("HeadByNumber", mock.Anything, big.NewInt(0)).Return(cltest.Head(0), nil) + mockEth := &evmtest.MockEth{ + EthClient: ethClient, + } + ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + Maybe(). + Return(mockEth.NewSub(t), nil) orm := headtracker.NewORM(cltest.FixtureChainID, db) assert.Nil(t, orm.IdempotentInsertHead(testutils.Context(t), cltest.Head(1))) @@ -148,9 +154,9 @@ func TestHeadTracker_Get(t *testing.T) { }, func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, ) - ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(cltest.Head(0), nil) + ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(cltest.Head(0), nil).Maybe() - fnCall := ethClient.On("HeadByNumber", mock.Anything, mock.Anything) + fnCall := ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Maybe() fnCall.RunFn = func(args mock.Arguments) { num := args.Get(1).(*big.Int) fnCall.ReturnArguments = mock.Arguments{cltest.Head(num.Int64()), nil} @@ -168,7 +174,10 @@ func TestHeadTracker_Get(t *testing.T) { assert.NoError(t, err) } - assert.Equal(t, test.want, ht.headSaver.LatestChain().ToInt()) + testutils.AssertEventually(t, func() bool { + latest := ht.headSaver.LatestChain().ToInt() + return latest != nil && test.want.Cmp(latest) == 0 + }) }) } } @@ -214,6 +223,9 @@ func TestHeadTracker_Start(t *testing.T) { config := evmtest.NewChainScopedConfig(t, gCfg) orm := headtracker.NewORM(cltest.FixtureChainID, db) ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + mockEth := &evmtest.MockEth{EthClient: ethClient} + sub := mockEth.NewSub(t) + ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(sub, nil).Maybe() return createHeadTracker(t, ethClient, config.EVM(), config.EVM().HeadTracker(), orm) } t.Run("Starts even if failed to get initialHead", func(t *testing.T) { From 3d41882e66cacff8e0d3129af095206ee9f6f103 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Tue, 14 May 2024 12:58:22 +0200 Subject: [PATCH 21/53] use polling in head tracker to get latest finalized block --- common/headtracker/head_saver.go | 2 - common/headtracker/head_tracker.go | 20 +++--- common/headtracker/mocks/head_tracker.go | 63 ++++++++++--------- .../evm/forwarders/forwarder_manager_test.go | 4 +- core/chains/evm/headtracker/head_saver.go | 5 -- core/chains/evm/headtracker/head_tracker.go | 6 +- core/chains/evm/headtracker/heads.go | 23 +------ core/chains/evm/headtracker/heads_test.go | 26 -------- core/chains/evm/headtracker/mocks/config.go | 18 ------ .../evm/headtracker/simulated_head_tracker.go | 28 +++++---- core/chains/evm/logpoller/helper_test.go | 4 +- core/chains/evm/logpoller/log_poller.go | 12 ++-- .../evm/logpoller/log_poller_internal_test.go | 18 +++--- core/chains/evm/logpoller/log_poller_test.go | 19 +++--- core/chains/evm/txmgr/txmgr_test.go | 4 +- .../ocr2/plugins/llo/integration_test.go | 12 ---- ...annel_definition_cache_integration_test.go | 8 +-- .../v21/logprovider/integration_test.go | 7 +-- .../promreporter/prom_reporter_test.go | 4 +- .../vrf/v2/listener_v2_log_listener_test.go | 4 +- 20 files changed, 102 insertions(+), 185 deletions(-) diff --git a/common/headtracker/head_saver.go b/common/headtracker/head_saver.go index 561396a337a..6f461c61225 100644 --- a/common/headtracker/head_saver.go +++ b/common/headtracker/head_saver.go @@ -20,6 +20,4 @@ type HeadSaver[H types.Head[BLOCK_HASH], BLOCK_HASH types.Hashable] interface { Chain(hash BLOCK_HASH) H // MarkFinalized - marks matching block and all it's direct ancestors as finalized MarkFinalized(ctx context.Context, latestFinalized H) error - // ChainWithLatestFinalized - returns highest block, whose ancestor is marked as finalized - ChainWithLatestFinalized() (H, error) } diff --git a/common/headtracker/head_tracker.go b/common/headtracker/head_tracker.go index ebceba8a233..7dda57651d2 100644 --- a/common/headtracker/head_tracker.go +++ b/common/headtracker/head_tracker.go @@ -42,8 +42,8 @@ type HeadTracker[H types.Head[BLOCK_HASH], BLOCK_HASH types.Hashable] interface // Backfill given a head will fill in any missing heads up to latestFinalized Backfill(ctx context.Context, headWithChain H) (err error) LatestChain() H - // ChainWithLatestFinalized - returns highest block, whose ancestor is marked as finalized - ChainWithLatestFinalized() (H, error) + // LatestAndFinalizedBlock - returns latest and finalized blocks + LatestAndFinalizedBlock(ctx context.Context) (latest, finalized H, err error) } type headTracker[ @@ -341,9 +341,18 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) LatestAndFinalizedBlock(ctx conte return } + if !latest.IsValid() { + err = fmt.Errorf("expected latest block to be valid") + return + } + finalized, err = ht.calculateLatestFinalized(ctx, latest) if err != nil { - err = fmt.Errorf("failded to calculate latest finalized block: %w", err) + err = fmt.Errorf("failed to calculate latest finalized block") + return + } + if !finalized.IsValid() { + err = fmt.Errorf("expected finalized block to be valid") return } @@ -459,8 +468,3 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) fetchAndSaveHead(ctx context.Cont } return head, nil } - -// ChainWithLatestFinalized - returns highest block, whose ancestor is marked as finalized -func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) ChainWithLatestFinalized() (HTH, error) { - return ht.headSaver.ChainWithLatestFinalized() -} diff --git a/common/headtracker/mocks/head_tracker.go b/common/headtracker/mocks/head_tracker.go index c467986c946..82005105b77 100644 --- a/common/headtracker/mocks/head_tracker.go +++ b/common/headtracker/mocks/head_tracker.go @@ -33,34 +33,6 @@ func (_m *HeadTracker[H, BLOCK_HASH]) Backfill(ctx context.Context, headWithChai return r0 } -// ChainWithLatestFinalized provides a mock function with given fields: -func (_m *HeadTracker[H, BLOCK_HASH]) ChainWithLatestFinalized() (H, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ChainWithLatestFinalized") - } - - var r0 H - var r1 error - if rf, ok := ret.Get(0).(func() (H, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() H); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(H) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // Close provides a mock function with given fields: func (_m *HeadTracker[H, BLOCK_HASH]) Close() error { ret := _m.Called() @@ -99,6 +71,41 @@ func (_m *HeadTracker[H, BLOCK_HASH]) HealthReport() map[string]error { return r0 } +// LatestAndFinalizedBlock provides a mock function with given fields: ctx +func (_m *HeadTracker[H, BLOCK_HASH]) LatestAndFinalizedBlock(ctx context.Context) (H, H, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for LatestAndFinalizedBlock") + } + + var r0 H + var r1 H + var r2 error + if rf, ok := ret.Get(0).(func(context.Context) (H, H, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) H); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(H) + } + + if rf, ok := ret.Get(1).(func(context.Context) H); ok { + r1 = rf(ctx) + } else { + r1 = ret.Get(1).(H) + } + + if rf, ok := ret.Get(2).(func(context.Context) error); ok { + r2 = rf(ctx) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + // LatestChain provides a mock function with given fields: func (_m *HeadTracker[H, BLOCK_HASH]) LatestChain() H { ret := _m.Called() diff --git a/core/chains/evm/forwarders/forwarder_manager_test.go b/core/chains/evm/forwarders/forwarder_manager_test.go index 99b93aaa0e1..c3b4ff1e08a 100644 --- a/core/chains/evm/forwarders/forwarder_manager_test.go +++ b/core/chains/evm/forwarders/forwarder_manager_test.go @@ -71,7 +71,7 @@ func TestFwdMgr_MaybeForwardTransaction(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - ht := headtracker.NewSimulatedHeadTracker(ctx, evmClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + ht := headtracker.NewSimulatedHeadTracker(evmClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), evmClient, lggr, ht, lpOpts) fwdMgr := forwarders.NewFwdMgr(db, evmClient, lp, lggr, evmcfg.EVM()) fwdMgr.ORM = forwarders.NewORM(db) @@ -133,7 +133,7 @@ func TestFwdMgr_AccountUnauthorizedToForward_SkipsForwarding(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - ht := headtracker.NewSimulatedHeadTracker(ctx, evmClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + ht := headtracker.NewSimulatedHeadTracker(evmClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), evmClient, lggr, ht, lpOpts) fwdMgr := forwarders.NewFwdMgr(db, evmClient, lp, lggr, evmcfg.EVM()) fwdMgr.ORM = forwarders.NewORM(db) diff --git a/core/chains/evm/headtracker/head_saver.go b/core/chains/evm/headtracker/head_saver.go index ca69ab7de96..8913d2dec21 100644 --- a/core/chains/evm/headtracker/head_saver.go +++ b/core/chains/evm/headtracker/head_saver.go @@ -86,10 +86,6 @@ func (hs *headSaver) MarkFinalized(ctx context.Context, finalized *evmtypes.Head return hs.orm.TrimOldHeads(ctx, minBlockToKeep) } -func (hs *headSaver) ChainWithLatestFinalized() (*evmtypes.Head, error) { - return hs.heads.ChainWithLatestFinalized() -} - var NullSaver httypes.HeadSaver = &nullSaver{} type nullSaver struct{} @@ -104,4 +100,3 @@ func (*nullSaver) Chain(hash common.Hash) *evmtypes.Head func (*nullSaver) MarkFinalized(ctx context.Context, latestFinalized *evmtypes.Head) error { return nil } -func (*nullSaver) ChainWithLatestFinalized() (*evmtypes.Head, error) { return nil, nil } diff --git a/core/chains/evm/headtracker/head_tracker.go b/core/chains/evm/headtracker/head_tracker.go index fa69e02b2fb..0e760c74995 100644 --- a/core/chains/evm/headtracker/head_tracker.go +++ b/core/chains/evm/headtracker/head_tracker.go @@ -51,5 +51,7 @@ func (*nullTracker) SetLogLevel(zapcore.Level) {} func (*nullTracker) Backfill(ctx context.Context, headWithChain *evmtypes.Head) (err error) { return nil } -func (*nullTracker) LatestChain() *evmtypes.Head { return nil } -func (*nullTracker) ChainWithLatestFinalized() (*evmtypes.Head, error) { return nil, nil } +func (*nullTracker) LatestChain() *evmtypes.Head { return nil } +func (*nullTracker) LatestAndFinalizedBlock(ctx context.Context) (latest, finalized *evmtypes.Head, err error) { + return nil, nil, nil +} diff --git a/core/chains/evm/headtracker/heads.go b/core/chains/evm/headtracker/heads.go index b847782f4b5..1edfb3e3788 100644 --- a/core/chains/evm/headtracker/heads.go +++ b/core/chains/evm/headtracker/heads.go @@ -1,7 +1,6 @@ package headtracker import ( - "errors" "sort" "sync" @@ -24,15 +23,11 @@ type Heads interface { // MarkFinalized - finds `finalized` in the LatestHead and marks it and all direct ancestors as finalized. // Trims old blocks whose height is smaller than minBlockToKeep MarkFinalized(finalized common.Hash, minBlockToKeep int64) bool - // ChainWithLatestFinalized - returns highest block, whose ancestor is marked as finalized, - // or error if such block is not available - ChainWithLatestFinalized() (*evmtypes.Head, error) } type heads struct { - heads []*evmtypes.Head - headWithLatestFinalized *evmtypes.Head - mu sync.RWMutex + heads []*evmtypes.Head + mu sync.RWMutex } func NewHeads() Heads { @@ -49,16 +44,6 @@ func (h *heads) LatestHead() *evmtypes.Head { return h.heads[0] } -func (h *heads) ChainWithLatestFinalized() (*evmtypes.Head, error) { - h.mu.RLock() - defer h.mu.RUnlock() - - if h.headWithLatestFinalized == nil { - return nil, errors.New("latest finalized block is not available yet") - } - return h.headWithLatestFinalized, nil -} - func (h *heads) HeadByHash(hash common.Hash) *evmtypes.Head { h.mu.RLock() defer h.mu.RUnlock() @@ -104,10 +89,6 @@ func (h *heads) MarkFinalized(finalized common.Hash, minBlockToKeep int64) bool head = head.Parent } - if foundFinalized { - h.headWithLatestFinalized = h.heads[0] - } - return foundFinalized } diff --git a/core/chains/evm/headtracker/heads_test.go b/core/chains/evm/headtracker/heads_test.go index c7677b20823..2f468e0f541 100644 --- a/core/chains/evm/headtracker/heads_test.go +++ b/core/chains/evm/headtracker/heads_test.go @@ -129,8 +129,6 @@ func TestHeads_MarkFinalized(t *testing.T) { allHeads := []*evmtypes.Head{h0, h1, h1Uncle, h2, h2Uncle, h3, h4, h5} heads.AddHeads(allHeads...) - _, err := heads.ChainWithLatestFinalized() - require.NotNil(t, err, "expected error, if there is no finalized block") // mark h3 and all ancestors as finalized require.True(t, heads.MarkFinalized(h3.Hash, h1.BlockNumber()), "expected MarkFinalized succeed") @@ -160,28 +158,4 @@ func TestHeads_MarkFinalized(t *testing.T) { t.Run("blocks were correctly marked as finalized", ensureProperFinalization) heads.AddHeads(h0, h1, h2, h2Uncle, h3, h4, h5) t.Run("blocks remain finalized after re adding them to the Heads", ensureProperFinalization) - // ChainWithLatestFinalized - containsFinalized := func(head *evmtypes.Head) bool { - for ; head != nil; head = head.Parent { - if head.IsFinalized { - return true - } - } - - return false - } - - chainWithFinalized, err := heads.ChainWithLatestFinalized() - require.NoError(t, err) - assert.True(t, containsFinalized(chainWithFinalized), "Expected finalized head to be present in the chain") - // add new head with gap - h6 := newHead(6, h5.Hash) - h7 := newHead(7, h6.Hash) - heads.AddHeads(h7) - assert.Equal(t, int64(7), heads.LatestHead().BlockNumber()) - assert.False(t, containsFinalized(heads.LatestHead()), "Expected latest head not to be connect to the finalized") - chainWithFinalized, err = heads.ChainWithLatestFinalized() - require.NoError(t, err) - assert.Equal(t, int64(5), chainWithFinalized.Number) - assert.True(t, containsFinalized(chainWithFinalized), "Expected finalized head to be still available in the chain") } diff --git a/core/chains/evm/headtracker/mocks/config.go b/core/chains/evm/headtracker/mocks/config.go index 05f923d17a7..fea7d4629b5 100644 --- a/core/chains/evm/headtracker/mocks/config.go +++ b/core/chains/evm/headtracker/mocks/config.go @@ -67,24 +67,6 @@ func (_m *Config) FinalityTagEnabled() bool { return r0 } -// FinalizedBlockOffset provides a mock function with given fields: -func (_m *Config) FinalizedBlockOffset() uint32 { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for FinalizedBlockOffset") - } - - var r0 uint32 - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - return r0 -} - // NewConfig creates a new instance of Config. 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 NewConfig(t interface { diff --git a/core/chains/evm/headtracker/simulated_head_tracker.go b/core/chains/evm/headtracker/simulated_head_tracker.go index 79792d6bd3b..e1e550de992 100644 --- a/core/chains/evm/headtracker/simulated_head_tracker.go +++ b/core/chains/evm/headtracker/simulated_head_tracker.go @@ -14,36 +14,40 @@ type simulatedHeadTracker struct { ec evmclient.Client useFinalityTag bool finalityDepth int64 - ctx context.Context } -func NewSimulatedHeadTracker(ctx context.Context, ec evmclient.Client, useFinalityTag bool, finalityDepth int64) *simulatedHeadTracker { +func NewSimulatedHeadTracker(ec evmclient.Client, useFinalityTag bool, finalityDepth int64) *simulatedHeadTracker { return &simulatedHeadTracker{ ec: ec, useFinalityTag: useFinalityTag, finalityDepth: finalityDepth, - ctx: ctx, } } -func (ht *simulatedHeadTracker) ChainWithLatestFinalized() (*evmtypes.Head, error) { - latestHead, err := ht.ec.HeadByNumber(ht.ctx, nil) +func (ht *simulatedHeadTracker) LatestAndFinalizedBlock(ctx context.Context) (*evmtypes.Head, *evmtypes.Head, error) { + latest, err := ht.ec.HeadByNumber(ctx, nil) if err != nil { - return nil, err + return nil, nil, err + } + + if latest == nil { + return nil, nil, fmt.Errorf("expected latest block to be valid") } var finalizedBlock *evmtypes.Head if ht.useFinalityTag { - finalizedBlock, err = ht.ec.LatestFinalizedBlock(ht.ctx) + finalizedBlock, err = ht.ec.LatestFinalizedBlock(ctx) } else { - finalizedBlock, err = ht.ec.HeadByNumber(ht.ctx, big.NewInt(max(latestHead.Number-ht.finalityDepth, 0))) + finalizedBlock, err = ht.ec.HeadByNumber(ctx, big.NewInt(max(latest.Number-ht.finalityDepth, 0))) } if err != nil { - return nil, fmt.Errorf("simulatedHeadTracker failed to get finalized block") + return nil, nil, fmt.Errorf("simulatedHeadTracker failed to get finalized block") + } + + if finalizedBlock == nil { + return nil, nil, fmt.Errorf("expected finalized block to be valid") } - finalizedBlock.IsFinalized = true - latestHead.Parent = finalizedBlock - return latestHead, nil + return latest, finalizedBlock, nil } diff --git a/core/chains/evm/logpoller/helper_test.go b/core/chains/evm/logpoller/helper_test.go index 2b9a2f1c709..3f589d84d56 100644 --- a/core/chains/evm/logpoller/helper_test.go +++ b/core/chains/evm/logpoller/helper_test.go @@ -10,8 +10,6 @@ import ( pkgerrors "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" @@ -71,7 +69,7 @@ func SetupTH(t testing.TB, opts logpoller.Opts) TestHarness { head := esc.Backend().Blockchain().CurrentHeader() esc.Backend().Blockchain().SetFinalized(head) - headTracker := headtracker.NewSimulatedHeadTracker(tests.Context(t), esc, opts.UseFinalityTag, opts.FinalityDepth) + headTracker := headtracker.NewSimulatedHeadTracker(esc, opts.UseFinalityTag, opts.FinalityDepth) if opts.PollPeriod == 0 { opts.PollPeriod = 1 * time.Hour } diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index fcb94adc384..c3e805ca947 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -89,8 +89,7 @@ type Client interface { } type HeadTracker interface { - // ChainWithLatestFinalized - returns highest block, whose ancestor is marked as finalized - ChainWithLatestFinalized() (*evmtypes.Head, error) + LatestAndFinalizedBlock(ctx context.Context) (latest, finalized *evmtypes.Head, err error) } var ( @@ -1013,14 +1012,13 @@ func (lp *logPoller) PollAndSaveLogs(ctx context.Context, currentBlockNumber int // Returns information about latestBlock, latestFinalizedBlockNumber provided by HeadTracker func (lp *logPoller) latestBlocks() (*evmtypes.Head, int64, error) { - chain, err := lp.headTracker.ChainWithLatestFinalized() + latest, finalized, err := lp.headTracker.LatestAndFinalizedBlock(lp.ctx) if err != nil { - return nil, 0, fmt.Errorf("failed to get chain with latest finalized block from HeadTracker: %w", err) + return nil, 0, fmt.Errorf("failed to get latest and latest finalized block from HeadTracker: %w", err) } - finalized := chain.LatestFinalizedHead() - lp.lggr.Debugw("Latest blocks read from chain", "latest", chain.Number, "finalized", finalized.BlockNumber()) - return chain, finalized.BlockNumber(), nil + lp.lggr.Debugw("Latest blocks read from chain", "latest", latest.Number, "finalized", finalized.BlockNumber()) + return latest, finalized.BlockNumber(), nil } // Find the first place where our chain and their chain have the same block, diff --git a/core/chains/evm/logpoller/log_poller_internal_test.go b/core/chains/evm/logpoller/log_poller_internal_test.go index 632f302866d..4e72bdf9a78 100644 --- a/core/chains/evm/logpoller/log_poller_internal_test.go +++ b/core/chains/evm/logpoller/log_poller_internal_test.go @@ -210,7 +210,8 @@ func TestLogPoller_BackupPollerStartup(t *testing.T) { latestBlock := int64(4) const finalityDepth = 2 - head := evmtypes.Head{Number: latestBlock, Parent: &evmtypes.Head{Number: latestBlock - finalityDepth, IsFinalized: true}} + head := &evmtypes.Head{Number: latestBlock} + finalizedHead := &evmtypes.Head{Number: latestBlock - finalityDepth} events := []common.Hash{EmitterABI.Events["Log1"].ID} log1 := types.Log{ Index: 0, @@ -227,7 +228,7 @@ func TestLogPoller_BackupPollerStartup(t *testing.T) { ec.On("ConfiguredChainID").Return(chainID, nil) headTracker := htMocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) - headTracker.On("ChainWithLatestFinalized").Return(&head, nil) + headTracker.On("LatestAndFinalizedBlock", mock.Anything).Return(head, finalizedHead, nil) ctx := testutils.Context(t) lpOpts := Opts{ @@ -315,11 +316,10 @@ func TestLogPoller_Replay(t *testing.T) { } headTracker := htMocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) - headTracker.On("ChainWithLatestFinalized").Return(func() (*evmtypes.Head, error) { + headTracker.On("LatestAndFinalizedBlock", mock.Anything).Return(func(ctx context.Context) (*evmtypes.Head, *evmtypes.Head, error) { headCopy := head - finalized := &evmtypes.Head{Number: headCopy.Number - lpOpts.FinalityDepth, IsFinalized: true} - headCopy.Parent = finalized - return &headCopy, nil + finalized := &evmtypes.Head{Number: headCopy.Number - lpOpts.FinalityDepth} + return &headCopy, finalized, nil }) lp := NewLogPoller(orm, ec, lggr, headTracker, lpOpts) @@ -540,7 +540,7 @@ func Test_latestBlockAndFinalityDepth(t *testing.T) { t.Run("headTracker returns an error", func(t *testing.T) { headTracker := htMocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) const expectedError = "finalized block is not available yet" - headTracker.On("ChainWithLatestFinalized").Return(&evmtypes.Head{}, fmt.Errorf(expectedError)) + headTracker.On("LatestAndFinalizedBlock", mock.Anything).Return(&evmtypes.Head{}, &evmtypes.Head{}, fmt.Errorf(expectedError)) lp := NewLogPoller(nil, nil, lggr, headTracker, lpOpts) _, _, err := lp.latestBlocks() @@ -549,8 +549,8 @@ func Test_latestBlockAndFinalityDepth(t *testing.T) { t.Run("headTracker returns valid chain", func(t *testing.T) { headTracker := htMocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) finalizedBlock := &evmtypes.Head{Number: 2, IsFinalized: true} - head := &evmtypes.Head{Number: 10, Parent: &evmtypes.Head{Number: 9, Parent: finalizedBlock}} - headTracker.On("ChainWithLatestFinalized").Return(head, nil) + head := &evmtypes.Head{Number: 10} + headTracker.On("LatestAndFinalizedBlock", mock.Anything).Return(head, finalizedBlock, nil) lp := NewLogPoller(nil, nil, lggr, headTracker, lpOpts) latestBlock, finalizedBlockNumber, err := lp.latestBlocks() diff --git a/core/chains/evm/logpoller/log_poller_test.go b/core/chains/evm/logpoller/log_poller_test.go index 4cfcafcd8b5..a7fb1deb1f1 100644 --- a/core/chains/evm/logpoller/log_poller_test.go +++ b/core/chains/evm/logpoller/log_poller_test.go @@ -25,8 +25,6 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - "github.com/smartcontractkit/chainlink-common/pkg/logger" commonutils "github.com/smartcontractkit/chainlink-common/pkg/utils" @@ -722,7 +720,7 @@ func TestLogPoller_SynchronizedWithGeth(t *testing.T) { KeepFinalizedBlocksDepth: 1000, } simulatedClient := client.NewSimulatedBackendClient(t, ec, chainID) - ht := headtracker.NewSimulatedHeadTracker(tests.Context(t), simulatedClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + ht := headtracker.NewSimulatedHeadTracker(simulatedClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) lp := logpoller.NewLogPoller(orm, simulatedClient, lggr, ht, lpOpts) for i := 0; i < finalityDepth; i++ { // Have enough blocks that we could reorg the full finalityDepth-1. ec.Commit() @@ -1564,15 +1562,10 @@ func TestTooManyLogResults(t *testing.T) { Message: "query returned more than 10000 results. Try with this block range [0x100E698, 0x100E6D4].", } - newHeadWithFinalized := func(blockNumber int64) *evmtypes.Head { - head := &evmtypes.Head{Number: blockNumber} - finalized := &evmtypes.Head{Number: blockNumber - lpOpts.FinalityDepth, IsFinalized: true} - head.Parent = finalized - return head - } - // Simulate currentBlock = 300 - headTracker.On("ChainWithLatestFinalized").Return(newHeadWithFinalized(300), nil).Once() + head := &evmtypes.Head{Number: 300} + finalized := &evmtypes.Head{Number: head.Number - lpOpts.FinalityDepth} + headTracker.On("LatestAndFinalizedBlock", mock.Anything).Return(head, finalized, nil).Once() call1 := ec.On("HeadByNumber", mock.Anything, mock.Anything).Return(func(ctx context.Context, blockNumber *big.Int) (*evmtypes.Head, error) { if blockNumber == nil { panic("unexpected call to get current head") @@ -1617,7 +1610,9 @@ func TestTooManyLogResults(t *testing.T) { // Now jump to block 500, but return error no matter how small the block range gets. // Should exit the loop with a critical error instead of hanging. - headTracker.On("ChainWithLatestFinalized").Return(newHeadWithFinalized(500), nil).Once() + head = &evmtypes.Head{Number: 500} + finalized = &evmtypes.Head{Number: head.Number - lpOpts.FinalityDepth} + headTracker.On("LatestAndFinalizedBlock", mock.Anything).Return(head, finalized, nil).Once() call1.On("HeadByNumber", mock.Anything, mock.Anything).Return(func(ctx context.Context, blockNumber *big.Int) (*evmtypes.Head, error) { if blockNumber == nil { panic("unexpected call to get current head") diff --git a/core/chains/evm/txmgr/txmgr_test.go b/core/chains/evm/txmgr/txmgr_test.go index cd6958452fd..abf8f99cbbb 100644 --- a/core/chains/evm/txmgr/txmgr_test.go +++ b/core/chains/evm/txmgr/txmgr_test.go @@ -18,8 +18,6 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -60,7 +58,7 @@ func makeTestEvmTxm( KeepFinalizedBlocksDepth: 1000, } - ht := headtracker.NewSimulatedHeadTracker(tests.Context(t), ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + ht := headtracker.NewSimulatedHeadTracker(ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), ethClient, lggr, ht, lpOpts) // logic for building components (from evm/evm_txm.go) ------- diff --git a/core/services/ocr2/plugins/llo/integration_test.go b/core/services/ocr2/plugins/llo/integration_test.go index 79635ccc8e0..b6f752541f4 100644 --- a/core/services/ocr2/plugins/llo/integration_test.go +++ b/core/services/ocr2/plugins/llo/integration_test.go @@ -19,8 +19,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - chainselectors "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" @@ -164,16 +162,6 @@ func TestIntegration_LLO(t *testing.T) { addBootstrapJob(t, bootstrapNode, chainID, verifierAddress, "job-1") addOCRJobs(t, streams, serverPubKey, serverURL, verifierAddress, bootstrapPeerID, bootstrapNodePort, nodes, configStoreAddress, clientPubKeys, chainID, fromBlock) - go func() { - // TODO: DH fix me - time.Sleep(10 * time.Second) - for _, node := range append(nodes, bootstrapNode) { - chain, _ := node.App.GetRelayers().LegacyEVMChains().Get(testutils.SimulatedChainID.String()) - err = chain.LogPoller().Replay(tests.Context(t), 1) - require.NoError(t, err) - } - }() - t.Run("receives at least one report per feed from each oracle when EAs are at 100% reliability", func(t *testing.T) { // Expect at least one report per channel from each oracle (keyed by transmitter ID) seen := make(map[ocr2types.Account]map[llotypes.ChannelID]struct{}) diff --git a/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go b/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go index b1dd596085e..8529ad89450 100644 --- a/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go +++ b/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go @@ -14,8 +14,6 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" "github.com/smartcontractkit/chainlink-common/pkg/utils" @@ -87,7 +85,7 @@ func Test_ChannelDefinitionCache_Integration(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - ht := headtracker.NewSimulatedHeadTracker(tests.Context(t), ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + ht := headtracker.NewSimulatedHeadTracker(ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) lp := logpoller.NewLogPoller( logpoller.NewORM(testutils.SimulatedChainID, db, lggr), ethClient, lggr, ht, lpOpts) servicetest.Run(t, lp) @@ -160,7 +158,7 @@ func Test_ChannelDefinitionCache_Integration(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - ht := headtracker.NewSimulatedHeadTracker(tests.Context(t), ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + ht := headtracker.NewSimulatedHeadTracker(ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) lp := &mockLogPoller{ LogPoller: logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr), ethClient, lggr, ht, lpOpts), LatestBlockFn: func(ctx context.Context) (int64, error) { @@ -203,7 +201,7 @@ func Test_ChannelDefinitionCache_Integration(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - ht := headtracker.NewSimulatedHeadTracker(tests.Context(t), ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + ht := headtracker.NewSimulatedHeadTracker(ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr), ethClient, lggr, ht, lpOpts) servicetest.Run(t, lp) cdc := llo.NewChannelDefinitionCache(lggr, orm, lp, configStoreAddress, channel2Block.Number().Int64()+1) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/integration_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/integration_test.go index e7ad1a8c0e3..c47bb0b8e71 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/integration_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/integration_test.go @@ -6,9 +6,6 @@ import ( "testing" "time" - "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/common" @@ -18,6 +15,8 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" @@ -508,7 +507,7 @@ func setupDependencies(t *testing.T, db *sqlx.DB, backend *backends.SimulatedBac RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - ht := headtracker.NewSimulatedHeadTracker(tests.Context(t), ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + ht := headtracker.NewSimulatedHeadTracker(ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) lp := logpoller.NewLogPoller(lorm, ethClient, pollerLggr, ht, lpOpts) return lp, ethClient } diff --git a/core/services/promreporter/prom_reporter_test.go b/core/services/promreporter/prom_reporter_test.go index 8439039e829..95164ecc9a6 100644 --- a/core/services/promreporter/prom_reporter_test.go +++ b/core/services/promreporter/prom_reporter_test.go @@ -10,8 +10,6 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" @@ -48,7 +46,7 @@ func newLegacyChainContainer(t *testing.T, db *sqlx.DB) legacyevm.LegacyChainCon RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - ht := headtracker.NewSimulatedHeadTracker(tests.Context(t), ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + ht := headtracker.NewSimulatedHeadTracker(ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), ethClient, lggr, ht, lpOpts) txm, err := txmgr.NewTxm( diff --git a/core/services/vrf/v2/listener_v2_log_listener_test.go b/core/services/vrf/v2/listener_v2_log_listener_test.go index 1f121cd57be..4193700e1ca 100644 --- a/core/services/vrf/v2/listener_v2_log_listener_test.go +++ b/core/services/vrf/v2/listener_v2_log_listener_test.go @@ -20,8 +20,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" @@ -106,7 +104,7 @@ func setupVRFLogPollerListenerTH(t *testing.T, RpcBatchSize: rpcBatchSize, KeepFinalizedBlocksDepth: keepFinalizedBlocksDepth, } - ht := headtracker.NewSimulatedHeadTracker(tests.Context(t), esc, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + ht := headtracker.NewSimulatedHeadTracker(esc, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) lp := logpoller.NewLogPoller(o, esc, lggr, ht, lpOpts) emitterAddress1, _, emitter1, err := log_emitter.DeployLogEmitter(owner, ec) From 2078ea6694a55376721af6df6c76fb99fce7e2a2 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Tue, 14 May 2024 13:06:13 +0200 Subject: [PATCH 22/53] fix relay --- core/services/relay/evm/chain_reader_test.go | 4 +--- core/services/relay/evm/config_poller_test.go | 4 +--- core/services/relay/evm/functions/config_poller_test.go | 4 +--- core/services/relay/evm/mercury/helpers_test.go | 4 +--- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/core/services/relay/evm/chain_reader_test.go b/core/services/relay/evm/chain_reader_test.go index 1db0ce9fb1f..df9de9d4fba 100644 --- a/core/services/relay/evm/chain_reader_test.go +++ b/core/services/relay/evm/chain_reader_test.go @@ -23,8 +23,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - "github.com/smartcontractkit/chainlink-common/pkg/codec" clcommontypes "github.com/smartcontractkit/chainlink-common/pkg/types" @@ -271,7 +269,7 @@ func (it *chainReaderInterfaceTester) GetChainReader(t *testing.T) clcommontypes RpcBatchSize: 1, KeepFinalizedBlocksDepth: 10000, } - ht := headtracker.NewSimulatedHeadTracker(tests.Context(t), it.client, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + ht := headtracker.NewSimulatedHeadTracker(it.client, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr), it.client, lggr, ht, lpOpts) require.NoError(t, lp.Start(ctx)) cr, err := evm.NewChainReaderService(ctx, lggr, lp, it.client, it.chainConfig) diff --git a/core/services/relay/evm/config_poller_test.go b/core/services/relay/evm/config_poller_test.go index 4c31812baa3..caf48caf490 100644 --- a/core/services/relay/evm/config_poller_test.go +++ b/core/services/relay/evm/config_poller_test.go @@ -19,8 +19,6 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" "github.com/smartcontractkit/libocr/gethwrappers2/ocrconfigurationstoreevmsimple" testoffchainaggregator2 "github.com/smartcontractkit/libocr/gethwrappers2/testocr2aggregator" @@ -102,7 +100,7 @@ func TestConfigPoller(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - ht := headtracker.NewSimulatedHeadTracker(tests.Context(t), ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + ht := headtracker.NewSimulatedHeadTracker(ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) lp = logpoller.NewLogPoller(lorm, ethClient, lggr, ht, lpOpts) servicetest.Run(t, lp) } diff --git a/core/services/relay/evm/functions/config_poller_test.go b/core/services/relay/evm/functions/config_poller_test.go index ad0406519af..c44d64c5ba7 100644 --- a/core/services/relay/evm/functions/config_poller_test.go +++ b/core/services/relay/evm/functions/config_poller_test.go @@ -15,8 +15,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" testoffchainaggregator2 "github.com/smartcontractkit/libocr/gethwrappers2/testocr2aggregator" confighelper2 "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" @@ -91,7 +89,7 @@ func runTest(t *testing.T, pluginType functions.FunctionsPluginType, expectedDig RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - ht := headtracker.NewSimulatedHeadTracker(tests.Context(t), ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + ht := headtracker.NewSimulatedHeadTracker(ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) lp := logpoller.NewLogPoller(lorm, ethClient, lggr, ht, lpOpts) servicetest.Run(t, lp) configPoller, err := functions.NewFunctionsConfigPoller(pluginType, lp, lggr) diff --git a/core/services/relay/evm/mercury/helpers_test.go b/core/services/relay/evm/mercury/helpers_test.go index 39cfa32405d..c7c59bf2e11 100644 --- a/core/services/relay/evm/mercury/helpers_test.go +++ b/core/services/relay/evm/mercury/helpers_test.go @@ -14,8 +14,6 @@ import ( "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - "github.com/smartcontractkit/libocr/offchainreporting2plus/chains/evmutil" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" @@ -177,7 +175,7 @@ func SetupTH(t *testing.T, feedID common.Hash) TestHarness { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - ht := headtracker.NewSimulatedHeadTracker(tests.Context(t), ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + ht := headtracker.NewSimulatedHeadTracker(ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) lp := logpoller.NewLogPoller(lorm, ethClient, lggr, ht, lpOpts) servicetest.Run(t, lp) From e2136565ef0aa5b53b81fccead70881e5c49bae1 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Tue, 14 May 2024 13:24:53 +0200 Subject: [PATCH 23/53] regen mocks --- core/chains/evm/headtracker/mocks/config.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/core/chains/evm/headtracker/mocks/config.go b/core/chains/evm/headtracker/mocks/config.go index fea7d4629b5..05f923d17a7 100644 --- a/core/chains/evm/headtracker/mocks/config.go +++ b/core/chains/evm/headtracker/mocks/config.go @@ -67,6 +67,24 @@ func (_m *Config) FinalityTagEnabled() bool { return r0 } +// FinalizedBlockOffset provides a mock function with given fields: +func (_m *Config) FinalizedBlockOffset() uint32 { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for FinalizedBlockOffset") + } + + var r0 uint32 + if rf, ok := ret.Get(0).(func() uint32); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(uint32) + } + + return r0 +} + // NewConfig creates a new instance of Config. 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 NewConfig(t interface { From 2d0a17d2749a896b6e11272cfdf73180ecd823f1 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Tue, 14 May 2024 14:47:52 +0200 Subject: [PATCH 24/53] use proper context in the LogPoller's latestBlocks --- core/chains/evm/logpoller/log_poller.go | 10 +++++----- core/chains/evm/logpoller/log_poller_internal_test.go | 6 ++++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index c3e805ca947..b21b7903941 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -589,7 +589,7 @@ func (lp *logPoller) run() { } // Otherwise this is the first poll _ever_ on a new chain. // Only safe thing to do is to start at the first finalized block. - latestBlock, latestFinalizedBlockNumber, err := lp.latestBlocks() + latestBlock, latestFinalizedBlockNumber, err := lp.latestBlocks(lp.ctx) if err != nil { lp.lggr.Warnw("Unable to get latest for first poll", "err", err) continue @@ -709,7 +709,7 @@ func (lp *logPoller) BackupPollAndSaveLogs(ctx context.Context) { lp.backupPollerNextBlock = mathutil.Max(backupStartBlock, 0) } - _, latestFinalizedBlockNumber, err := lp.latestBlocks() + _, latestFinalizedBlockNumber, err := lp.latestBlocks(ctx) if err != nil { lp.lggr.Warnw("Backup logpoller failed to get latest block", "err", err) return @@ -926,7 +926,7 @@ func (lp *logPoller) PollAndSaveLogs(ctx context.Context, currentBlockNumber int lp.lggr.Debugw("Polling for logs", "currentBlockNumber", currentBlockNumber) // Intentionally not using logPoller.finalityDepth directly but the latestFinalizedBlockNumber returned from lp.latestBlocks() // latestBlocks knows how to pick a proper latestFinalizedBlockNumber based on the logPoller's configuration - latestBlock, latestFinalizedBlockNumber, err := lp.latestBlocks() + latestBlock, latestFinalizedBlockNumber, err := lp.latestBlocks(ctx) if err != nil { lp.lggr.Warnw("Unable to get latestBlockNumber block", "err", err, "currentBlockNumber", currentBlockNumber) return @@ -1011,8 +1011,8 @@ func (lp *logPoller) PollAndSaveLogs(ctx context.Context, currentBlockNumber int } // Returns information about latestBlock, latestFinalizedBlockNumber provided by HeadTracker -func (lp *logPoller) latestBlocks() (*evmtypes.Head, int64, error) { - latest, finalized, err := lp.headTracker.LatestAndFinalizedBlock(lp.ctx) +func (lp *logPoller) latestBlocks(ctx context.Context) (*evmtypes.Head, int64, error) { + latest, finalized, err := lp.headTracker.LatestAndFinalizedBlock(ctx) if err != nil { return nil, 0, fmt.Errorf("failed to get latest and latest finalized block from HeadTracker: %w", err) } diff --git a/core/chains/evm/logpoller/log_poller_internal_test.go b/core/chains/evm/logpoller/log_poller_internal_test.go index 4e72bdf9a78..c114fbeae90 100644 --- a/core/chains/evm/logpoller/log_poller_internal_test.go +++ b/core/chains/evm/logpoller/log_poller_internal_test.go @@ -21,6 +21,8 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" @@ -543,7 +545,7 @@ func Test_latestBlockAndFinalityDepth(t *testing.T) { headTracker.On("LatestAndFinalizedBlock", mock.Anything).Return(&evmtypes.Head{}, &evmtypes.Head{}, fmt.Errorf(expectedError)) lp := NewLogPoller(nil, nil, lggr, headTracker, lpOpts) - _, _, err := lp.latestBlocks() + _, _, err := lp.latestBlocks(tests.Context(t)) require.ErrorContains(t, err, expectedError) }) t.Run("headTracker returns valid chain", func(t *testing.T) { @@ -553,7 +555,7 @@ func Test_latestBlockAndFinalityDepth(t *testing.T) { headTracker.On("LatestAndFinalizedBlock", mock.Anything).Return(head, finalizedBlock, nil) lp := NewLogPoller(nil, nil, lggr, headTracker, lpOpts) - latestBlock, finalizedBlockNumber, err := lp.latestBlocks() + latestBlock, finalizedBlockNumber, err := lp.latestBlocks(tests.Context(t)) require.NoError(t, err) require.NotNil(t, latestBlock) assert.Equal(t, head.BlockNumber(), latestBlock.BlockNumber()) From fad674c49d79833bdb2695f778926669fbbed6e3 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Wed, 15 May 2024 18:20:15 +0200 Subject: [PATCH 25/53] fix flaky test --- core/chains/evm/client/chain_client_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/chains/evm/client/chain_client_test.go b/core/chains/evm/client/chain_client_test.go index 13e00e40ebd..69a9086c83e 100644 --- a/core/chains/evm/client/chain_client_test.go +++ b/core/chains/evm/client/chain_client_test.go @@ -52,7 +52,7 @@ func TestChainClient_BatchCallContext(t *testing.T) { } mockRpc := newMockRpc(t) - mockRpc.On("GetInterceptedChainInfo").Return(commonclient.ChainInfo{}, commonclient.ChainInfo{}) + 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++ { From 3dd728bb3bb6612e26c8c1e9d707bac3f44f214e Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Mon, 20 May 2024 19:44:32 +0200 Subject: [PATCH 26/53] fixes for changes from develop branch --- core/chains/evm/forwarders/forwarder_manager_test.go | 4 +++- core/chains/evm/headtracker/head_tracker_test.go | 11 +++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/core/chains/evm/forwarders/forwarder_manager_test.go b/core/chains/evm/forwarders/forwarder_manager_test.go index 03acf16b85d..73ce7d59146 100644 --- a/core/chains/evm/forwarders/forwarder_manager_test.go +++ b/core/chains/evm/forwarders/forwarder_manager_test.go @@ -18,6 +18,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" "github.com/smartcontractkit/chainlink-common/pkg/utils" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/testhelpers" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" @@ -203,7 +204,8 @@ func TestFwdMgr_InvalidForwarderForOCR2FeedsStates(t *testing.T) { RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, } - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), evmClient, lggr, lpOpts) + ht := headtracker.NewSimulatedHeadTracker(evmClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), evmClient, lggr, ht, lpOpts) fwdMgr := forwarders.NewFwdMgr(db, evmClient, lp, lggr, evmcfg.EVM()) fwdMgr.ORM = forwarders.NewORM(db) diff --git a/core/chains/evm/headtracker/head_tracker_test.go b/core/chains/evm/headtracker/head_tracker_test.go index d3847a192fd..b0d74a74efb 100644 --- a/core/chains/evm/headtracker/head_tracker_test.go +++ b/core/chains/evm/headtracker/head_tracker_test.go @@ -1078,8 +1078,9 @@ func TestHeadTracker_Backfill(t *testing.T) { // BenchmarkHeadTracker_Backfill - benchmarks HeadTracker's Backfill with focus on efficiency after initial // backfill on start up func BenchmarkHeadTracker_Backfill(b *testing.B) { - cfg := configtest.NewGeneralConfig(b, nil) - + cfg := configtest.NewGeneralConfig(b, func(c *chainlink.Config, _ *chainlink.Secrets) { + c.EVM[0].FinalityTagEnabled = ptr(true) + }) evmcfg := evmtest.NewChainScopedConfig(b, cfg) db := pgtest.NewSqlxDB(b) chainID := big.NewInt(evmclient.NullClientChainID) @@ -1101,15 +1102,17 @@ func BenchmarkHeadTracker_Backfill(b *testing.B) { number := hash.Big().Int64() return makeBlock(number), nil }) + ethClient.On("LatestFinalizedBlock", mock.Anything).Return(finalized, nil).Once() // run initial backfill to populate the database - err := ht.headTracker.Backfill(ctx, latest, finalized) + err := ht.headTracker.Backfill(ctx, latest) require.NoError(b, err) b.ResetTimer() // focus benchmark on processing of a new latest block for i := 0; i < b.N; i++ { latest = makeBlock(int64(finalityDepth + i)) finalized = makeBlock(int64(i + 1)) - err := ht.headTracker.Backfill(ctx, latest, finalized) + ethClient.On("LatestFinalizedBlock", mock.Anything).Return(finalized, nil).Once() + err := ht.headTracker.Backfill(ctx, latest) require.NoError(b, err) } } From a105c9d788bc104c69b5868e589da9977bd82e5f Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Thu, 23 May 2024 14:03:59 +0200 Subject: [PATCH 27/53] replace panic with FailNow --- core/chains/evm/logpoller/log_poller_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/chains/evm/logpoller/log_poller_test.go b/core/chains/evm/logpoller/log_poller_test.go index a7fb1deb1f1..c83efc83b39 100644 --- a/core/chains/evm/logpoller/log_poller_test.go +++ b/core/chains/evm/logpoller/log_poller_test.go @@ -1568,7 +1568,7 @@ func TestTooManyLogResults(t *testing.T) { headTracker.On("LatestAndFinalizedBlock", mock.Anything).Return(head, finalized, nil).Once() call1 := ec.On("HeadByNumber", mock.Anything, mock.Anything).Return(func(ctx context.Context, blockNumber *big.Int) (*evmtypes.Head, error) { if blockNumber == nil { - panic("unexpected call to get current head") + require.FailNow(t, "unexpected call to get current head") } return &evmtypes.Head{Number: blockNumber.Int64()}, nil }) @@ -1615,7 +1615,7 @@ func TestTooManyLogResults(t *testing.T) { headTracker.On("LatestAndFinalizedBlock", mock.Anything).Return(head, finalized, nil).Once() call1.On("HeadByNumber", mock.Anything, mock.Anything).Return(func(ctx context.Context, blockNumber *big.Int) (*evmtypes.Head, error) { if blockNumber == nil { - panic("unexpected call to get current head") + require.FailNow(t, "unexpected call to get current head") } return &evmtypes.Head{Number: blockNumber.Int64()}, nil }) From ee0618342d7635da2eb1b168d9571059716e9192 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Thu, 23 May 2024 19:34:05 +0200 Subject: [PATCH 28/53] report dead node only if it's dead for long enough --- common/client/multi_node.go | 115 +++++++++++++++++++------------ common/client/multi_node_test.go | 10 ++- common/client/node.go | 4 +- 3 files changed, 83 insertions(+), 46 deletions(-) diff --git a/common/client/multi_node.go b/common/client/multi_node.go index 271f9045a5f..05b552c842e 100644 --- a/common/client/multi_node.go +++ b/common/client/multi_node.go @@ -92,19 +92,20 @@ type multiNode[ BATCH_ELEM any, ] struct { services.StateMachine - nodes []Node[CHAIN_ID, HEAD, RPC_CLIENT] - sendonlys []SendOnlyNode[CHAIN_ID, RPC_CLIENT] - chainID CHAIN_ID - chainType config.ChainType - lggr logger.SugaredLogger - selectionMode string - noNewHeadsThreshold time.Duration - nodeSelector NodeSelector[CHAIN_ID, HEAD, RPC_CLIENT] - leaseDuration time.Duration - leaseTicker *time.Ticker - chainFamily string - reportInterval time.Duration - sendTxSoftTimeout time.Duration // defines max waiting time from first response til responses evaluation + nodes []Node[CHAIN_ID, HEAD, RPC_CLIENT] + sendonlys []SendOnlyNode[CHAIN_ID, RPC_CLIENT] + chainID CHAIN_ID + chainType config.ChainType + lggr logger.SugaredLogger + selectionMode string + noNewHeadsThreshold time.Duration + nodeSelector NodeSelector[CHAIN_ID, HEAD, RPC_CLIENT] + 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] @@ -150,20 +151,21 @@ func NewMultiNode[ 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, - chainID: chainID, - chainType: chainType, - 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, - sendTxSoftTimeout: sendTxSoftTimeout, + nodes: nodes, + sendonlys: sendonlys, + chainID: chainID, + chainType: chainType, + 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: reportInterval, + sendTxSoftTimeout: sendTxSoftTimeout, } c.lggr.Debugf("The MultiNode is configured to use NodeSelectionMode: %s", selectionMode) @@ -251,6 +253,9 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return // another goroutine beat us here } + if c.activeNode != nil { + c.activeNode.UnsubscribeAllExceptAliveLoop() + } c.activeNode = c.nodeSelector.Select() if c.activeNode == nil { @@ -308,10 +313,13 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } c.activeMu.Lock() + defer c.activeMu.Unlock() if bestNode != c.activeNode { + if c.activeNode != nil { + c.activeNode.UnsubscribeAllExceptAliveLoop() + } c.activeNode = bestNode } - c.activeMu.Unlock() } func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) checkLeaseLoop() { @@ -332,7 +340,16 @@ 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() { defer c.wg.Done() - c.report() + nodeStates := make([]nodeWithState, len(c.nodes)) + for i, n := range c.nodes { + nodeStates[i] = nodeWithState{ + Node: n.String(), + State: n.State().String(), + DeadSince: nil, + } + } + + c.report(nodeStates) monitor := time.NewTicker(utils.WithJitter(c.reportInterval)) defer monitor.Stop() @@ -340,44 +357,54 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP for { select { case <-monitor.C: - c.report() + c.report(nodeStates) case <-c.chStop: return } } } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) report() { - type nodeWithState struct { - Node string - State string - } +type nodeWithState struct { + Node string + State string + DeadSince *time.Time +} - var total, dead int +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) { + start := time.Now() + var dead int counts := make(map[nodeState]int) - nodeStates := make([]nodeWithState, len(c.nodes)) for i, n := range c.nodes { state := n.State() - nodeStates[i] = nodeWithState{n.String(), state.String()} - total++ - if state != nodeStateAlive { + counts[state]++ + nodesStateInfo[i].State = state.String() + if state == nodeStateAlive { + nodesStateInfo[i].DeadSince = nil + continue + } + + if nodesStateInfo[i].DeadSince == nil { + nodesStateInfo[i].DeadSince = &start + } + + if start.Sub(*nodesStateInfo[i].DeadSince) >= c.deathDeclarationDelay { dead++ } - counts[state]++ } for _, state := range allNodeStates { count := counts[state] PromMultiNodeRPCNodeStates.WithLabelValues(c.chainFamily, c.chainID.String(), state.String()).Set(float64(count)) } + total := len(c.nodes) live := total - dead - c.lggr.Tracew(fmt.Sprintf("MultiNode state: %d/%d nodes are alive", live, total), "nodeStates", nodeStates) + c.lggr.Tracew(fmt.Sprintf("MultiNode state: %d/%d nodes are alive", live, total), "nodeStates", nodesStateInfo) if total == dead { rerr := fmt.Errorf("no primary nodes available: 0/%d nodes are alive", total) - c.lggr.Criticalw(rerr.Error(), "nodeStates", nodeStates) + c.lggr.Criticalw(rerr.Error(), "nodeStates", nodesStateInfo) c.SvcErrBuffer.Append(rerr) } else if dead > 0 { - c.lggr.Errorw(fmt.Sprintf("At least one primary node is dead: %d/%d nodes are alive", live, total), "nodeStates", nodeStates) + c.lggr.Errorw(fmt.Sprintf("At least one primary node is dead: %d/%d nodes are alive", live, total), "nodeStates", nodesStateInfo) } } diff --git a/common/client/multi_node_test.go b/common/client/multi_node_test.go index ec1b90b55e7..3539d0b8aa7 100644 --- a/common/client/multi_node_test.go +++ b/common/client/multi_node_test.go @@ -69,11 +69,16 @@ func newHealthyNode(t *testing.T, chainID types.ID) *mockNode[types.ID, types.He } 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) node.On("ConfiguredChainID").Return(chainID).Once() node.On("Start", mock.Anything).Return(nil).Once() node.On("Close").Return(nil).Once() - node.On("State").Return(state).Maybe() node.On("String").Return(fmt.Sprintf("healthy_node_%d", rand.Int())).Maybe() node.On("SetPoolChainInfoProvider", mock.Anything).Once() return node @@ -225,6 +230,7 @@ func TestMultiNode_Report(t *testing.T) { logger: lggr, }) mn.reportInterval = tests.TestInterval + mn.deathDeclarationDelay = tests.TestInterval defer func() { assert.NoError(t, mn.Close()) }() err := mn.Dial(tests.Context(t)) require.NoError(t, err) @@ -242,6 +248,7 @@ func TestMultiNode_Report(t *testing.T) { logger: lggr, }) mn.reportInterval = tests.TestInterval + mn.deathDeclarationDelay = tests.TestInterval defer func() { assert.NoError(t, mn.Close()) }() err := mn.Dial(tests.Context(t)) require.NoError(t, err) @@ -383,6 +390,7 @@ func TestMultiNode_selectNode(t *testing.T) { chainID := types.RandomID() oldBest := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) oldBest.On("String").Return("oldBest").Maybe() + oldBest.On("UnsubscribeAllExceptAliveLoop").Once() newBest := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) newBest.On("String").Return("newBest").Maybe() mn := newTestMultiNode(t, multiNodeOpts{ diff --git a/common/client/node.go b/common/client/node.go index d0fbce72ff4..d1772efa795 100644 --- a/common/client/node.go +++ b/common/client/node.go @@ -61,7 +61,9 @@ type Node[ HEAD Head, RPC NodeClient[CHAIN_ID, HEAD], ] interface { - // State returns nodeState + // 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 + // `FinalizedBlockOutOfSync`, must be executed upon every call. State() nodeState // StateAndLatest returns nodeState with the latest ChainInfo observed by Node during current lifecycle. StateAndLatest() (nodeState, ChainInfo) From 277facf36c2dc788b6b3b4fb7c70bad4928d6124 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Thu, 23 May 2024 19:58:59 +0200 Subject: [PATCH 29/53] fix racy test --- core/chains/evm/headtracker/head_tracker_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/chains/evm/headtracker/head_tracker_test.go b/core/chains/evm/headtracker/head_tracker_test.go index b0d74a74efb..3d0c3f13317 100644 --- a/core/chains/evm/headtracker/head_tracker_test.go +++ b/core/chains/evm/headtracker/head_tracker_test.go @@ -135,8 +135,9 @@ func TestHeadTracker_Get(t *testing.T) { {"nil no initial", nil, nil, big.NewInt(0)}, } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { + for i := range tests { + t.Run(tests[i].name, func(t *testing.T) { + test := tests[i] db := pgtest.NewSqlxDB(t) config := cltest.NewTestChainScopedConfig(t) orm := headtracker.NewORM(cltest.FixtureChainID, db) From 0c004075e115d707e0c559a1afad3b19625ca521 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Thu, 30 May 2024 19:07:50 +0200 Subject: [PATCH 30/53] rollback foundry change caused by merge with develop --- contracts/foundry-lib/forge-std | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/foundry-lib/forge-std b/contracts/foundry-lib/forge-std index bb4ceea94d6..978ac6fadb6 160000 --- a/contracts/foundry-lib/forge-std +++ b/contracts/foundry-lib/forge-std @@ -1 +1 @@ -Subproject commit bb4ceea94d6f10eeb5b41dc2391c6c8bf8e734ef +Subproject commit 978ac6fadb62f5f0b723c996f64be52eddba6801 From dff61c015ffb612ffba7810802f6c73d93f5c40f Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Tue, 4 Jun 2024 16:27:09 +0200 Subject: [PATCH 31/53] use active node as source of HighestChainInfo to reduce number of transitions to nodeStateFinalizedBlockOutOfSync --- common/client/multi_node.go | 97 +++++++++-- common/client/multi_node_test.go | 68 ++++++-- common/client/node.go | 2 + core/chains/evm/client/rpc_client.go | 154 ++++++++++++------ core/chains/evm/client/rpc_client_test.go | 13 +- testdata/scripts/node/validate/warnings.txtar | 2 + 6 files changed, 252 insertions(+), 84 deletions(-) diff --git a/common/client/multi_node.go b/common/client/multi_node.go index 05b552c842e..556a56d08b4 100644 --- a/common/client/multi_node.go +++ b/common/client/multi_node.go @@ -33,6 +33,21 @@ var ( Name: "multi_node_invariant_violations", Help: "The number of invariant violations", }, []string{"network", "chainId", "invariant"}) + // PromMultiNodeSelectRPCTiming - reports how long it took us to select an RPC + PromMultiNodeSelectRPCTiming = promauto.NewHistogramVec(prometheus.HistogramOpts{ + Name: "multi_node_select_rpc_timing", + Help: "Defines how long it took us to pick an RPC to perform request", + Buckets: []float64{ + float64(50 * time.Millisecond), + float64(100 * time.Millisecond), + float64(200 * time.Millisecond), + float64(500 * time.Millisecond), + float64(1 * time.Second), + float64(2 * time.Second), + float64(4 * time.Second), + float64(8 * time.Second), + }, + }, []string{"network", "chainId"}) ErroringNodeError = fmt.Errorf("no live nodes available") ) @@ -110,6 +125,9 @@ type multiNode[ activeMu sync.RWMutex activeNode Node[CHAIN_ID, HEAD, RPC_CLIENT] + highestChainInfoMu sync.RWMutex + highestChainInfo ChainInfo + chStop services.StopChan wg sync.WaitGroup @@ -166,6 +184,7 @@ func NewMultiNode[ reportInterval: reportInterval, deathDeclarationDelay: reportInterval, sendTxSoftTimeout: sendTxSoftTimeout, + highestChainInfo: ChainInfo{TotalDifficulty: big.NewInt(0)}, } c.lggr.Debugf("The MultiNode is configured to use NodeSelectionMode: %s", selectionMode) @@ -238,6 +257,10 @@ 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) { + start := time.Now() + defer func() { + PromMultiNodeSelectRPCTiming.WithLabelValues(c.chainFamily, c.chainID.String()).Observe(float64(time.Since(start))) + }() c.activeMu.RLock() node = c.activeNode c.activeMu.RUnlock() @@ -253,9 +276,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return // another goroutine beat us here } - if c.activeNode != nil { - c.activeNode.UnsubscribeAllExceptAliveLoop() - } + c.finalizeActiveNodeObservationsUnsafe() c.activeNode = c.nodeSelector.Select() if c.activeNode == nil { @@ -287,18 +308,50 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return nLiveNodes, ch } -// HighestChainInfo - returns highest ChainInfo ever observed by any node in the pool. +// HighestChainInfo - returns highest ChainInfo ever observed by any activeNode func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) HighestChainInfo() ChainInfo { - ch := ChainInfo{ - TotalDifficulty: big.NewInt(0), + // HighestChainInfo provides the chain info observed by the application layer, which we use to invalidate lagging RPCs. + // The method is called in two cases: to report an RPC's state and to reevaluate the state during activeNode selection. + // We should try to report the HighestChainInfo directly from the currently active node. + // If we can't acquire read lock, there is a goroutine that selects a new activeNode, and the HighestChainInfo was called by: + // 1. The same goroutine that selects a new activeNode, so the HighestChainInfo is already up-to-date, so it's OK to return the cached value. + // 2. Separate goroutine that tries to report the state of an RPC, so it's also OK to use cached value. + if c.activeMu.TryRLock() { + c.updateHighestChainInfoUnsafe() + c.activeMu.RUnlock() } - for _, n := range c.nodes { - nodeChainInfo := n.HighestChainInfo() - ch.BlockNumber = max(ch.BlockNumber, nodeChainInfo.BlockNumber) - ch.FinalizedBlockNumber = max(ch.FinalizedBlockNumber, nodeChainInfo.FinalizedBlockNumber) - ch.SetTotalDifficultyIfGt(nodeChainInfo.TotalDifficulty) + + c.highestChainInfoMu.RLock() + defer c.highestChainInfoMu.RUnlock() + return c.highestChainInfo +} + +// finalizeActiveNodeObservationsUnsafe - cancels all activeNode subscriptions and captures HighestChainInfo. +// NOTE: UNSAFE. Must be called with activeMu lock. +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) finalizeActiveNodeObservationsUnsafe() { + if c.activeNode == nil { + return } - return ch + + c.activeNode.UnsubscribeAllExceptAliveLoop() + c.updateHighestChainInfoUnsafe() +} + +// updateHighestChainInfoUnsafe - updates HighestChainInfo. Must be called with activeMu read lock. +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) updateHighestChainInfoUnsafe() { + if c.activeNode == nil { + return + } + nodeChainInfo := c.activeNode.HighestChainInfo() + c.highestChainInfoMu.Lock() + defer c.highestChainInfoMu.Unlock() + highestChainInfo := ChainInfo{ + BlockNumber: max(c.highestChainInfo.BlockNumber, nodeChainInfo.BlockNumber), + FinalizedBlockNumber: max(c.highestChainInfo.FinalizedBlockNumber, nodeChainInfo.FinalizedBlockNumber), + TotalDifficulty: big.NewInt(0), + } + highestChainInfo.SetTotalDifficultyIfGt(nodeChainInfo.TotalDifficulty) + c.highestChainInfo = highestChainInfo } func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) checkLease() { @@ -314,12 +367,22 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP c.activeMu.Lock() defer c.activeMu.Unlock() - if bestNode != c.activeNode { - if c.activeNode != nil { - c.activeNode.UnsubscribeAllExceptAliveLoop() - } - c.activeNode = bestNode + if bestNode == c.activeNode { + return } + + c.finalizeActiveNodeObservationsUnsafe() + // ensure that after updating the highest chain info, the best node is still alive + if bestNode.State() != nodeStateAlive { + // If the final observations invalidated the new best node, we still want to reset activeNode. + // This way, `RoundRobin` and `PriorityLevel` selection modes will get closer to expected behavior, + // as there is a chance that on the next call to `selectNode` or `checkLease`, we'll pick a new RPC instead of + // sticking to the one with the highest finalized block. + c.activeNode = nil + return + } + + c.activeNode = bestNode } func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) checkLeaseLoop() { diff --git a/common/client/multi_node_test.go b/common/client/multi_node_test.go index 3539d0b8aa7..a884f11bb26 100644 --- a/common/client/multi_node_test.go +++ b/common/client/multi_node_test.go @@ -385,12 +385,18 @@ func TestMultiNode_selectNode(t *testing.T) { require.NoError(t, err) require.Equal(t, prevActiveNode.String(), newActiveNode.String()) }) - t.Run("Updates node if active is not healthy", func(t *testing.T) { + t.Run("Updates node if active is not healthy and sets highest chain info", func(t *testing.T) { t.Parallel() chainID := types.RandomID() oldBest := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) oldBest.On("String").Return("oldBest").Maybe() oldBest.On("UnsubscribeAllExceptAliveLoop").Once() + expectedChainInfo := ChainInfo{ + BlockNumber: 100, + FinalizedBlockNumber: 150, + TotalDifficulty: big.NewInt(200), + } + oldBest.On("HighestChainInfo").Return(expectedChainInfo).Once() newBest := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) newBest.On("String").Return("newBest").Maybe() mn := newTestMultiNode(t, multiNodeOpts{ @@ -401,6 +407,11 @@ func TestMultiNode_selectNode(t *testing.T) { nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) nodeSelector.On("Select").Return(oldBest).Once() mn.nodeSelector = nodeSelector + mn.highestChainInfo = ChainInfo{ + BlockNumber: 10, + FinalizedBlockNumber: 15, + TotalDifficulty: big.NewInt(20), + } activeNode, err := mn.selectNode() require.NoError(t, err) require.Equal(t, oldBest.String(), activeNode.String()) @@ -410,6 +421,7 @@ func TestMultiNode_selectNode(t *testing.T) { newActiveNode, err := mn.selectNode() require.NoError(t, err) require.Equal(t, newBest.String(), newActiveNode.String()) + require.Equal(t, expectedChainInfo, mn.highestChainInfo) }) t.Run("No active nodes - reports critical error", func(t *testing.T) { t.Parallel() @@ -438,13 +450,17 @@ func TestMultiNode_ChainInfo(t *testing.T) { HighestChainInfo ChainInfo State nodeState } - testCases := []struct { + + chainID := types.RandomID() + type testCase struct { Name string ExpectedNLiveNodes int ExpectedLatestChainInfo ChainInfo ExpectedHighestChainInfo ChainInfo NodeParams []nodeParams - }{ + ActiveNodeI int + } + testCases := []testCase{ { Name: "no nodes", ExpectedLatestChainInfo: ChainInfo{ @@ -453,9 +469,10 @@ func TestMultiNode_ChainInfo(t *testing.T) { ExpectedHighestChainInfo: ChainInfo{ TotalDifficulty: big.NewInt(0), }, + ActiveNodeI: -1, }, { - Name: "Best node is not healthy", + Name: "Best node is not healthy and not active", ExpectedNLiveNodes: 3, ExpectedLatestChainInfo: ChainInfo{ BlockNumber: 20, @@ -463,10 +480,11 @@ func TestMultiNode_ChainInfo(t *testing.T) { TotalDifficulty: big.NewInt(10), }, ExpectedHighestChainInfo: ChainInfo{ - BlockNumber: 1005, - FinalizedBlockNumber: 995, - TotalDifficulty: big.NewInt(2005), + BlockNumber: 24, + FinalizedBlockNumber: 14, + TotalDifficulty: big.NewInt(15), }, + ActiveNodeI: 2, NodeParams: []nodeParams{ { State: nodeStateOutOfSync, @@ -523,22 +541,24 @@ func TestMultiNode_ChainInfo(t *testing.T) { }, }, } - - chainID := types.RandomID() - mn := newTestMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - }) for i := range testCases { tc := testCases[i] t.Run(tc.Name, func(t *testing.T) { + mn := newTestMultiNode(t, multiNodeOpts{ + selectionMode: NodeSelectionModeRoundRobin, + chainID: chainID, + }) for _, params := range tc.NodeParams { node := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) node.On("StateAndLatest").Return(params.State, params.LatestChainInfo) - node.On("HighestChainInfo").Return(params.HighestChainInfo) + node.On("HighestChainInfo").Return(params.HighestChainInfo).Maybe() mn.nodes = append(mn.nodes, node) } + if tc.ActiveNodeI >= 0 { + mn.activeNode = mn.nodes[tc.ActiveNodeI] + } + nNodes, latestChainInfo := mn.LatestChainInfo() assert.Equal(t, tc.ExpectedNLiveNodes, nNodes) assert.Equal(t, tc.ExpectedLatestChainInfo, latestChainInfo) @@ -547,6 +567,26 @@ func TestMultiNode_ChainInfo(t *testing.T) { assert.Equal(t, tc.ExpectedHighestChainInfo, highestChainInfo) }) } + t.Run("HighestChainInfo returns cached value, If not able to acquire active node lock", func(t *testing.T) { + mn := newTestMultiNode(t, multiNodeOpts{ + selectionMode: NodeSelectionModeRoundRobin, + chainID: chainID, + }) + mn.highestChainInfo = ChainInfo{ + BlockNumber: 10, + FinalizedBlockNumber: 20, + TotalDifficulty: big.NewInt(30), + } + mn.activeNode = newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + mn.activeMu.Lock() + defer mn.activeMu.Unlock() + highestChainInfo := mn.HighestChainInfo() + require.Equal(t, ChainInfo{ + BlockNumber: 10, + FinalizedBlockNumber: 20, + TotalDifficulty: big.NewInt(30), + }, highestChainInfo) + }) } func TestMultiNode_BatchCallContextAll(t *testing.T) { diff --git a/common/client/node.go b/common/client/node.go index d1772efa795..919260e18e3 100644 --- a/common/client/node.go +++ b/common/client/node.go @@ -75,6 +75,8 @@ type Node[ String() string RPC() RPC SubscribersCount() int32 + // UnsubscribeAllExceptAliveLoop - closes all subscriptions except the aliveLoop subscription and waits for all requests + // that might update HighestChainInfo to complete. UnsubscribeAllExceptAliveLoop() ConfiguredChainID() CHAIN_ID Order() int32 diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index fceefbe0d1f..fb2cab3e281 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -75,6 +75,10 @@ type rpcClient struct { // close the underlying subscription subs []ethereum.Subscription + // Need to wait for completion of inflight requests that report highestChainInfo to ensure that + // on completion of DisconnectAll highestChainInfo contains all the info collected in current life cycle of the RPC + wgChainInfoInFlightRequest *sync.WaitGroup + // Need to track the aliveLoop subscription, so we do not cancel it when checking lease on the MultiNode aliveLoopSub ethereum.Subscription @@ -109,6 +113,7 @@ func NewRPCClient( r.http = &rawclient{uri: *httpuri} } r.chStopInFlight = make(chan struct{}) + r.wgChainInfoInFlightRequest = &sync.WaitGroup{} lggr = logger.Named(lggr, "Client") lggr = logger.With(lggr, "clientTier", tier.String(), @@ -177,15 +182,7 @@ func (r *rpcClient) DialHTTP() error { } func (r *rpcClient) Close() { - defer func() { - if r.ws.rpc != nil { - r.ws.rpc.Close() - } - }() - - r.stateMu.Lock() - defer r.stateMu.Unlock() - r.cancelInflightRequests() + r.DisconnectAll() } // cancelInflightRequests closes and replaces the chStopInFlight @@ -244,22 +241,36 @@ func (r *rpcClient) getRPCDomain() string { } // registerSub adds the sub to the rpcClient list -func (r *rpcClient) registerSub(sub ethereum.Subscription) { +func (r *rpcClient) registerSub(sub ethereum.Subscription, stopInFLightCh chan struct{}) error { r.stateMu.Lock() defer r.stateMu.Unlock() + // 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: + sub.Unsubscribe() + return fmt.Errorf("failed to registred subscription - all in flight requests were canceled") + default: + } + // TODO: BCI-3358 - delete sub when caller unsubscribes. r.subs = append(r.subs, sub) + return nil } // DisconnectAll disconnects all clients connected to the rpcClient func (r *rpcClient) DisconnectAll() { r.stateMu.Lock() - defer r.stateMu.Unlock() if r.ws.rpc != nil { r.ws.rpc.Close() } r.cancelInflightRequests() r.unsubscribeAll() r.latestChainInfo = commonclient.ChainInfo{} + wg := r.wgChainInfoInFlightRequest + r.wgChainInfoInFlightRequest = &sync.WaitGroup{} + r.stateMu.Unlock() + // wait for all requests to complete to capture all observed chainInfo + wg.Wait() } // unsubscribeAll unsubscribes all subscriptions @@ -286,16 +297,20 @@ func (r *rpcClient) SubscribersCount() int32 { } // UnsubscribeAllExceptAliveLoop disconnects all subscriptions to the node except the alive loop subscription -// while holding the n.stateMu lock +// while holding the n.stateMu lock and waits for func (r *rpcClient) UnsubscribeAllExceptAliveLoop() { r.stateMu.Lock() - defer r.stateMu.Unlock() for _, s := range r.subs { if s != r.aliveLoopSub { s.Unsubscribe() } } + wg := r.wgChainInfoInFlightRequest + r.wgChainInfoInFlightRequest = &sync.WaitGroup{} + r.stateMu.Unlock() + // wait for all requests to complete to capture all observed chainInfo + wg.Wait() } // RPC wrappers @@ -344,31 +359,37 @@ func (r *rpcClient) BatchCallContext(ctx context.Context, b []rpc.BatchElem) err return err } -func (r *rpcClient) SubscribeNewHead(ctx context.Context, channel chan<- *evmtypes.Head) (commontypes.Subscription, error) { - ctx, cancel, ws, _ := r.makeLiveQueryCtxAndSafeGetClients(ctx) +func (r *rpcClient) SubscribeNewHead(ctx context.Context, channel chan<- *evmtypes.Head) (_ commontypes.Subscription, err error) { + // ensure that DisconnectAll waits for completion of this request to collect all values observed during this life cycle + ctx, cancel, chStopInFlight, wg, ws, _ := r.acquireQueryCtx(ctx, 1) + defer wg.Done() defer cancel() args := []interface{}{"newHeads"} lggr := r.newRqLggr().With("args", args) lggr.Debug("RPC call: evmclient.Client#EthSubscribe") start := time.Now() - subscriptionCtxCh := r.getChStopInflight() + 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.oneNewHead(subscriptionCtxCh, head) + r.oneNewHead(chStopInFlight, head) return head - }, func(err error) error { - return fmt.Errorf("%s: %w", r.rpcClientErrorPrefix(), err) - }) - err := subForwarder.start(ws.rpc.EthSubscribe(ctx, subForwarder.srcCh, args...)) - if err == nil { - r.registerSub(subForwarder) + }, r.wrapRPCClientError) + err = subForwarder.start(ws.rpc.EthSubscribe(ctx, subForwarder.srcCh, args...)) + if err != nil { + return } - duration := time.Since(start) - r.logResult(lggr, err, duration, r.getRPCDomain(), "EthSubscribe") + err = r.registerSub(subForwarder, chStopInFlight) + if err != nil { + return + } - return subForwarder, r.wrapWS(err) + return subForwarder, nil } // GethClient wrappers @@ -477,14 +498,7 @@ func (r *rpcClient) HeaderByHash(ctx context.Context, hash common.Hash) (header } func (r *rpcClient) LatestFinalizedBlock(ctx context.Context) (*evmtypes.Head, error) { - requestCtxCh := r.getChStopInflight() - block, err := r.blockByNumber(ctx, rpc.FinalizedBlockNumber.String()) - if err != nil { - return nil, err - } - - r.oneNewFinalizedHead(requestCtxCh, block) - return block, nil + return r.blockByNumber(ctx, rpc.FinalizedBlockNumber.String()) } func (r *rpcClient) BlockByNumber(ctx context.Context, number *big.Int) (head *evmtypes.Head, err error) { @@ -493,7 +507,27 @@ func (r *rpcClient) BlockByNumber(ctx context.Context, number *big.Int) (head *e } func (r *rpcClient) blockByNumber(ctx context.Context, number string) (head *evmtypes.Head, err error) { - err = r.CallContext(ctx, &head, "eth_getBlockByNumber", number, false) + // ensure that DisconnectAll waits for completion of this request to collect all values observed during this life cycle + ctx, cancel, chStopInFlight, wg, ws, http := r.acquireQueryCtx(ctx, 1) + defer cancel() + defer wg.Done() + const method = "eth_getBlockByNumber" + args := []interface{}{number, false} + lggr := r.newRqLggr().With( + "method", method, + "args", args, + ) + + lggr.Debug("RPC call: evmclient.Client#CallContext") + start := time.Now() + if http != nil { + err = r.wrapHTTP(http.rpc.CallContext(ctx, &head, method, args...)) + } else { + err = r.wrapWS(ws.rpc.CallContext(ctx, &head, method, args...)) + } + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "CallContext") if err != nil { return nil, err } @@ -502,6 +536,14 @@ func (r *rpcClient) blockByNumber(ctx context.Context, number string) (head *evm return } head.EVMChainID = ubig.New(r.chainID) + + switch number { + case rpc.FinalizedBlockNumber.String(): + r.oneNewFinalizedHead(chStopInFlight, head) + case rpc.LatestBlockNumber.String(): + r.oneNewHead(chStopInFlight, head) + } + return } @@ -919,26 +961,30 @@ func (r *rpcClient) ClientVersion(ctx context.Context) (version string, err erro return } -func (r *rpcClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) { - ctx, cancel, ws, _ := r.makeLiveQueryCtxAndSafeGetClients(ctx) +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, 0) defer cancel() lggr := r.newRqLggr().With("q", q) lggr.Debug("RPC call: evmclient.Client#SubscribeFilterLogs") start := time.Now() - sub := newSubForwarder(ch, nil, func(err error) error { - return fmt.Errorf("%s: %w", r.rpcClientErrorPrefix(), err) - }) - err := sub.start(ws.geth.SubscribeFilterLogs(ctx, q, sub.srcCh)) - if err == nil { - r.registerSub(sub) + defer func() { + duration := time.Since(start) + r.logResult(lggr, err, duration, r.getRPCDomain(), "SubscribeFilterLogs") + err = r.wrapWS(err) + }() + sub := newSubForwarder(ch, nil, r.wrapRPCClientError) + err = sub.start(ws.geth.SubscribeFilterLogs(ctx, q, sub.srcCh)) + if err != nil { + return } - err = r.wrapWS(err) - duration := time.Since(start) - r.logResult(lggr, err, duration, r.getRPCDomain(), "SubscribeFilterLogs") + err = r.registerSub(sub, chStopInFlight) + if err != nil { + return + } - return sub, err + return sub, nil } func (r *rpcClient) SuggestGasTipCap(ctx context.Context) (tipCap *big.Int, err error) { @@ -1023,17 +1069,27 @@ func (r *rpcClient) wrapHTTP(err error) error { // makeLiveQueryCtxAndSafeGetClients wraps makeQueryCtx func (r *rpcClient) makeLiveQueryCtxAndSafeGetClients(parentCtx context.Context) (ctx context.Context, cancel context.CancelFunc, ws rawclient, http *rawclient) { + ctx, cancel, _, _, ws, http = r.acquireQueryCtx(parentCtx, 0) + return +} + +func (r *rpcClient) acquireQueryCtx(parentCtx context.Context, wgDelta uint16) (ctx context.Context, cancel context.CancelFunc, + chStopInFlight chan struct{}, wg *sync.WaitGroup, ws rawclient, http *rawclient) { // Need to wrap in mutex because state transition can cancel and replace the // context r.stateMu.RLock() - cancelCh := r.chStopInFlight + chStopInFlight = r.chStopInFlight + wg = r.wgChainInfoInFlightRequest + if wgDelta != 0 { + wg.Add(int(wgDelta)) + } ws = r.ws if r.http != nil { cp := *r.http http = &cp } r.stateMu.RUnlock() - ctx, cancel = makeQueryCtx(parentCtx, cancelCh) + ctx, cancel = makeQueryCtx(parentCtx, chStopInFlight) return } diff --git a/core/chains/evm/client/rpc_client_test.go b/core/chains/evm/client/rpc_client_test.go index fa4f936b7ff..d043f21a06c 100644 --- a/core/chains/evm/client/rpc_client_test.go +++ b/core/chains/evm/client/rpc_client_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tidwall/gjson" + "go.uber.org/zap" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" @@ -115,16 +116,18 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { head := <-ch require.Equal(t, chainId, head.ChainID()) }) - t.Run("Failed SubscribeNewHead returns proper error", func(t *testing.T) { + t.Run("Failed SubscribeNewHead 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() - rpc := client.NewRPCClient(lggr, *wsURL, nil, "rpc", 1, chainId, commonclient.Primary) + observedLggr, observed := logger.TestObserved(t, zap.DebugLevel) + rpc := client.NewRPCClient(observedLggr, *wsURL, nil, "rpc", 1, chainId, commonclient.Primary) require.NoError(t, rpc.Dial(ctx)) server.Close() _, err := rpc.SubscribeNewHead(ctx, make(chan *evmtypes.Head)) require.ErrorContains(t, err, "RPCClient returned error (rpc)") + tests.AssertLogEventually(t, observed, "evmclient.Client#EthSubscribe RPC call failure") }) t.Run("Subscription error is properly wrapper", func(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) @@ -151,16 +154,18 @@ func TestRPCClient_SubscribeFilterLogs(t *testing.T) { lggr := logger.Test(t) ctx, cancel := context.WithTimeout(tests.Context(t), tests.WaitTimeout(t)) defer cancel() - t.Run("Failed SubscribeFilterLogs returns proper error", func(t *testing.T) { + t.Run("Failed SubscribeFilterLogs logs and returns 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() - rpc := client.NewRPCClient(lggr, *wsURL, nil, "rpc", 1, chainId, commonclient.Primary) + observedLggr, observed := logger.TestObserved(t, zap.DebugLevel) + rpc := client.NewRPCClient(observedLggr, *wsURL, nil, "rpc", 1, chainId, commonclient.Primary) require.NoError(t, rpc.Dial(ctx)) server.Close() _, err := rpc.SubscribeFilterLogs(ctx, ethereum.FilterQuery{}, make(chan types.Log)) require.ErrorContains(t, err, "RPCClient returned error (rpc)") + tests.AssertLogEventually(t, observed, "evmclient.Client#SubscribeFilterLogs RPC call failure") }) t.Run("Subscription error is properly wrapper", func(t *testing.T) { server := testutils.NewWSServer(t, chainId, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { diff --git a/testdata/scripts/node/validate/warnings.txtar b/testdata/scripts/node/validate/warnings.txtar index 7d45a97cc40..e43c7cfc8bb 100644 --- a/testdata/scripts/node/validate/warnings.txtar +++ b/testdata/scripts/node/validate/warnings.txtar @@ -321,6 +321,7 @@ NonceAutoSync = true NoNewHeadsThreshold = '3m0s' RPCDefaultBatchSize = 250 RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 [EVM.Transactions] ForwardersEnabled = false @@ -373,6 +374,7 @@ SyncThreshold = 5 LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false [EVM.OCR] ContractConfirmations = 4 From 588944c40eab5805cb95d286d42b7281b594cc07 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Tue, 4 Jun 2024 21:04:43 +0200 Subject: [PATCH 32/53] optimised loading of latest finalized block --- common/headtracker/head_tracker.go | 65 ++++---- common/types/head.go | 3 + .../evm/headtracker/head_tracker_test.go | 144 +++++++++++++++++- core/chains/evm/types/models.go | 19 ++- core/chains/evm/types/models_test.go | 20 +++ 5 files changed, 213 insertions(+), 38 deletions(-) diff --git a/common/headtracker/head_tracker.go b/common/headtracker/head_tracker.go index c8aa7c9c15b..ac76407cce8 100644 --- a/common/headtracker/head_tracker.go +++ b/common/headtracker/head_tracker.go @@ -349,7 +349,7 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) LatestAndFinalizedBlock(ctx conte finalized, err = ht.calculateLatestFinalized(ctx, latest) if err != nil { - err = fmt.Errorf("failed to calculate latest finalized block") + err = fmt.Errorf("failed to calculate latest finalized block: %w", err) return } if !finalized.IsValid() { @@ -360,6 +360,24 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) LatestAndFinalizedBlock(ctx conte return } +func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) getHeadAtHeight(ctx context.Context, chainHeadHash BLOCK_HASH, blockHeight int64) (HTH, error) { + chainHead := ht.headSaver.Chain(chainHeadHash) + if chainHead.IsValid() { + // check if provided chain contains a block of specified height + headAtHeight, err := chainHead.HeadAtHeight(blockHeight) + if err == nil { + // we are forced to reload the block due to type mismatched caused by generics + hthAtHeight := ht.headSaver.Chain(headAtHeight.BlockHash()) + // ensure that the block was not removed from the chain by another goroutine + if hthAtHeight.IsValid() { + return hthAtHeight, nil + } + } + } + + return ht.client.HeadByNumber(ctx, big.NewInt(blockHeight)) +} + // calculateLatestFinalized - returns latest finalized block. It's expected that currentHeadNumber - is the head of // canonical chain. There is no guaranties that returned block belongs to the canonical chain. Additional verification // must be performed before usage. @@ -371,8 +389,7 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) calculateLatestFinalized(ctx cont } finalizedBlockNumber := max(latestFinalized.BlockNumber()-int64(ht.config.FinalizedBlockOffset()), 0) - finalizedWithOffset, _, err := ht.fetchAncestorAtHeight(ctx, latestFinalized, finalizedBlockNumber) - return finalizedWithOffset, err + return ht.getHeadAtHeight(ctx, latestFinalized.BlockHash(), finalizedBlockNumber) } // no need to make an additional RPC call on chains with instant finality if ht.config.FinalityDepth() == 0 && ht.config.FinalizedBlockOffset() == 0 { @@ -382,29 +399,7 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) calculateLatestFinalized(ctx cont if finalizedBlockNumber <= 0 { finalizedBlockNumber = 0 } - return ht.client.HeadByNumber(ctx, big.NewInt(finalizedBlockNumber)) -} - -func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) fetchAncestorAtHeight(ctx context.Context, head HTH, baseHeight int64) (ancestor HTH, fetched int, err error) { - for i := head.BlockNumber() - 1; i >= baseHeight; i-- { - // NOTE: Sequential requests here mean it's a potential performance bottleneck, be aware! - existingHead := ht.headSaver.Chain(head.GetParentHash()) - if existingHead.IsValid() { - head = existingHead - continue - } - var err error - head, err = ht.fetchAndSaveHead(ctx, i, head.GetParentHash()) - fetched++ - if ctx.Err() != nil { - ht.log.Debugw("context canceled, aborting backfill", "err", err, "ctx.Err", ctx.Err()) - return ancestor, fetched, fmt.Errorf("fetchAndSaveHead failed: %w", ctx.Err()) - } else if err != nil { - return ancestor, fetched, fmt.Errorf("fetchAndSaveHead failed: %w", err) - } - } - - return head, fetched, nil + return ht.getHeadAtHeight(ctx, currentHead.BlockHash(), finalizedBlockNumber) } // backfill fetches all missing heads up until the latestFinalizedHead @@ -429,9 +424,21 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) backfill(ctx context.Context, hea "err", err) }() - head, fetched, err = ht.fetchAncestorAtHeight(ctx, head, baseHeight) - if err != nil { - return fmt.Errorf("failed to fetch ancestor with height %d: %w", baseHeight, err) + for i := head.BlockNumber() - 1; i >= baseHeight; i-- { + // NOTE: Sequential requests here mean it's a potential performance bottleneck, be aware! + existingHead := ht.headSaver.Chain(head.GetParentHash()) + if existingHead.IsValid() { + head = existingHead + continue + } + head, err = ht.fetchAndSaveHead(ctx, i, head.GetParentHash()) + fetched++ + if ctx.Err() != nil { + ht.log.Debugw("context canceled, aborting backfill", "err", err, "ctx.Err", ctx.Err()) + return fmt.Errorf("fetchAndSaveHead failed: %w", ctx.Err()) + } else if err != nil { + return fmt.Errorf("fetchAndSaveHead failed: %w", err) + } } if head.BlockHash() != latestFinalizedHead.BlockHash() { diff --git a/common/types/head.go b/common/types/head.go index 9d927d4f5e4..77a2a7a0dcf 100644 --- a/common/types/head.go +++ b/common/types/head.go @@ -33,6 +33,9 @@ type Head[BLOCK_HASH Hashable] interface { // If not in chain, returns the zero hash HashAtHeight(blockNum int64) BLOCK_HASH + // HeadAtHeight returns head at specified height or an error, if one does not exist in provided chain. + HeadAtHeight(blockNum int64) (Head[BLOCK_HASH], error) + // Returns the total difficulty of the block. For chains who do not have a concept of block // difficulty, return 0. BlockDifficulty() *big.Int diff --git a/core/chains/evm/headtracker/head_tracker_test.go b/core/chains/evm/headtracker/head_tracker_test.go index 3d0c3f13317..0d0f17c5e6a 100644 --- a/core/chains/evm/headtracker/head_tracker_test.go +++ b/core/chains/evm/headtracker/head_tracker_test.go @@ -3,6 +3,7 @@ package headtracker_test import ( "context" "errors" + "fmt" "math/big" "slices" "sync" @@ -1012,10 +1013,11 @@ func TestHeadTracker_Backfill(t *testing.T) { t.Run("marks block as finalized according to FinalizedBlockOffset (finality tag)", func(t *testing.T) { htu := newHeadTrackerUniverse(t, opts{Heads: []evmtypes.Head{h15}, FinalityTagEnabled: true, FinalizedBlockOffset: 2}) htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&h14, nil).Once() - // calculateLatestFinalizedBlock fetches blocks down to the FinalizedBlockOffset - htu.ethClient.On("HeadByHash", mock.Anything, h13.Hash).Return(&h13, nil).Once() - htu.ethClient.On("HeadByHash", mock.Anything, h12.Hash).Return(&h12, nil).Once() + // calculateLatestFinalizedBlock fetches blocks at LatestFinalized - FinalizedBlockOffset + htu.ethClient.On("HeadByNumber", mock.Anything, big.NewInt(h12.Number)).Return(&h12, nil).Once() // backfill from 15 to 12 + htu.ethClient.On("HeadByHash", mock.Anything, h12.Hash).Return(&h12, nil).Once() + htu.ethClient.On("HeadByHash", mock.Anything, h13.Hash).Return(&h13, nil).Once() htu.ethClient.On("HeadByHash", mock.Anything, h14.Hash).Return(&h14, nil).Once() err := htu.headTracker.Backfill(ctx, &h15) require.NoError(t, err) @@ -1076,6 +1078,142 @@ func TestHeadTracker_Backfill(t *testing.T) { }) } +func TestHeadTracker_LatestAndFinalizedBlock(t *testing.T) { + t.Parallel() + + ctx := testutils.Context(t) + + h11 := cltest.Head(11) + h11.ParentHash = utils.NewHash() + + h12 := cltest.Head(12) + h12.ParentHash = h11.Hash + + h13 := cltest.Head(13) + h13.ParentHash = h12.Hash + + type opts struct { + Heads []evmtypes.Head + FinalityTagEnabled bool + FinalizedBlockOffset uint32 + FinalityDepth uint32 + } + + newHeadTrackerUniverse := func(t *testing.T, opts opts) *headTrackerUniverse { + cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, secrets *chainlink.Secrets) { + c.EVM[0].FinalityTagEnabled = ptr(opts.FinalityTagEnabled) + c.EVM[0].FinalizedBlockOffset = ptr(opts.FinalizedBlockOffset) + c.EVM[0].FinalityDepth = ptr(opts.FinalityDepth) + }) + + evmcfg := evmtest.NewChainScopedConfig(t, cfg) + db := pgtest.NewSqlxDB(t) + orm := headtracker.NewORM(cltest.FixtureChainID, db) + for i := range opts.Heads { + require.NoError(t, orm.IdempotentInsertHead(testutils.Context(t), &opts.Heads[i])) + } + ethClient := evmtest.NewEthClientMock(t) + ethClient.On("ConfiguredChainID", mock.Anything).Return(evmtest.MustGetDefaultChainID(t, cfg.EVMConfigs()), nil) + ht := createHeadTracker(t, ethClient, evmcfg.EVM(), evmcfg.EVM().HeadTracker(), orm) + _, err := ht.headSaver.Load(testutils.Context(t), 0) + require.NoError(t, err) + return ht + } + t.Run("returns error if failed to get latest block", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true}) + const expectedError = "failed to fetch latest block" + htu.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(nil, errors.New(expectedError)).Once() + + _, _, err := htu.headTracker.LatestAndFinalizedBlock(ctx) + require.ErrorContains(t, err, expectedError) + }) + t.Run("returns error if latest block is invalid", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true}) + htu.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(nil, nil).Once() + + _, _, err := htu.headTracker.LatestAndFinalizedBlock(ctx) + require.ErrorContains(t, err, "expected latest block to be valid") + }) + t.Run("returns error if failed to get latest finalized (finality tag)", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true}) + htu.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(h13, nil).Once() + const expectedError = "failed to get latest finalized block" + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(nil, fmt.Errorf(expectedError)).Once() + + _, _, err := htu.headTracker.LatestAndFinalizedBlock(ctx) + require.ErrorContains(t, err, expectedError) + }) + t.Run("returns error if latest finalized block is not valid (finality tag)", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true}) + htu.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(h13, nil).Once() + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(nil, nil).Once() + + _, _, err := htu.headTracker.LatestAndFinalizedBlock(ctx) + require.ErrorContains(t, err, "expected finalized block to be valid") + }) + t.Run("returns latest finalized block as is if FinalizedBlockOffset is 0 (finality tag)", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true}) + htu.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(h13, nil).Once() + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(h11, nil).Once() + + actualL, actualLF, err := htu.headTracker.LatestAndFinalizedBlock(ctx) + require.NoError(t, err) + assert.Equal(t, actualL, h13) + assert.Equal(t, actualLF, h11) + }) + t.Run("returns latest finalized block with offset from cache (finality tag)", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true, FinalizedBlockOffset: 1, Heads: []evmtypes.Head{*h13, *h12, *h11}}) + htu.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(h13, nil).Once() + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(h12, nil).Once() + + actualL, actualLF, err := htu.headTracker.LatestAndFinalizedBlock(ctx) + require.NoError(t, err) + assert.Equal(t, actualL.Number, h13.Number) + assert.Equal(t, actualLF.Number, h11.Number) + }) + t.Run("returns latest finalized block with offset from RPC (finality tag)", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true, FinalizedBlockOffset: 2, Heads: []evmtypes.Head{*h13, *h12, *h11}}) + htu.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(h13, nil).Once() + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(h12, nil).Once() + h10 := cltest.Head(10) + htu.ethClient.On("HeadByNumber", mock.Anything, big.NewInt(10)).Return(h10, nil).Once() + + actualL, actualLF, err := htu.headTracker.LatestAndFinalizedBlock(ctx) + require.NoError(t, err) + assert.Equal(t, actualL.Number, h13.Number) + assert.Equal(t, actualLF.Number, h10.Number) + }) + t.Run("returns current head for both latest and finalized for FD = 0 (finality depth)", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{}) + htu.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(h13, nil).Once() + + actualL, actualLF, err := htu.headTracker.LatestAndFinalizedBlock(ctx) + require.NoError(t, err) + assert.Equal(t, actualL.Number, h13.Number) + assert.Equal(t, actualLF.Number, h13.Number) + }) + t.Run("returns latest finalized block with offset from cache (finality depth)", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{FinalityDepth: 1, FinalizedBlockOffset: 1, Heads: []evmtypes.Head{*h13, *h12, *h11}}) + htu.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(h13, nil).Once() + + actualL, actualLF, err := htu.headTracker.LatestAndFinalizedBlock(ctx) + require.NoError(t, err) + assert.Equal(t, actualL.Number, h13.Number) + assert.Equal(t, actualLF.Number, h11.Number) + }) + t.Run("returns latest finalized block with offset from RPC (finality depth)", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{FinalityDepth: 1, FinalizedBlockOffset: 2, Heads: []evmtypes.Head{*h13, *h12, *h11}}) + htu.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(h13, nil).Once() + h10 := cltest.Head(10) + htu.ethClient.On("HeadByNumber", mock.Anything, big.NewInt(10)).Return(h10, nil).Once() + + actualL, actualLF, err := htu.headTracker.LatestAndFinalizedBlock(ctx) + require.NoError(t, err) + assert.Equal(t, actualL.Number, h13.Number) + assert.Equal(t, actualLF.Number, h10.Number) + }) +} + // BenchmarkHeadTracker_Backfill - benchmarks HeadTracker's Backfill with focus on efficiency after initial // backfill on start up func BenchmarkHeadTracker_Backfill(b *testing.B) { diff --git a/core/chains/evm/types/models.go b/core/chains/evm/types/models.go index 2af5b81ccf8..a9e5cd5841b 100644 --- a/core/chains/evm/types/models.go +++ b/core/chains/evm/types/models.go @@ -118,16 +118,23 @@ func (h *Head) IsInChain(blockHash common.Hash) bool { // HashAtHeight returns the hash of the block at the given height, if it is in the chain. // If not in chain, returns the zero hash func (h *Head) HashAtHeight(blockNum int64) common.Hash { - for { + headAtHeight, err := h.HeadAtHeight(blockNum) + if err != nil { + return common.Hash{} + } + + return headAtHeight.BlockHash() +} + +func (h *Head) HeadAtHeight(blockNum int64) (commontypes.Head[common.Hash], error) { + for h != nil { if h.Number == blockNum { - return h.Hash - } - if h.Parent == nil { - break + return h, nil } + h = h.Parent } - return common.Hash{} + return nil, fmt.Errorf("failed to find head at height %d", blockNum) } // ChainLength returns the length of the chain followed by recursively looking up parents diff --git a/core/chains/evm/types/models_test.go b/core/chains/evm/types/models_test.go index db7b876bb64..6018d68f962 100644 --- a/core/chains/evm/types/models_test.go +++ b/core/chains/evm/types/models_test.go @@ -247,6 +247,26 @@ func TestHead_EarliestInChain(t *testing.T) { assert.Equal(t, int64(1), head.EarliestInChain().BlockNumber()) } +func TestHead_HeadAtHeight(t *testing.T) { + expectedResult := &evmtypes.Head{ + Hash: common.BigToHash(big.NewInt(10)), + Number: 2, + Parent: &evmtypes.Head{ + Number: 1, + }, + } + head := evmtypes.Head{ + Number: 3, + Parent: expectedResult, + } + + headAtHeight, err := head.HeadAtHeight(2) + require.NoError(t, err) + assert.Equal(t, expectedResult, headAtHeight) + _, err = head.HeadAtHeight(0) + assert.Error(t, err, "expected to get an error if head is not in the chain") +} + func TestHead_IsInChain(t *testing.T) { hash1 := utils.NewHash() hash2 := utils.NewHash() From fca61d5dedeb2701c7029fec61cbe2ff6be72ce3 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Wed, 5 Jun 2024 15:41:04 +0200 Subject: [PATCH 33/53] fix merge conflicts --- core/chains/evm/client/node_lifecycle_test.go | 858 ------------------ .../evm/headtracker/head_tracker_test.go | 3 +- 2 files changed, 2 insertions(+), 859 deletions(-) delete mode 100644 core/chains/evm/client/node_lifecycle_test.go diff --git a/core/chains/evm/client/node_lifecycle_test.go b/core/chains/evm/client/node_lifecycle_test.go deleted file mode 100644 index dfda7c7f3b5..00000000000 --- a/core/chains/evm/client/node_lifecycle_test.go +++ /dev/null @@ -1,858 +0,0 @@ -package client - -import ( - "fmt" - "math/big" - "sync/atomic" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/tidwall/gjson" - "go.uber.org/zap" - - "github.com/smartcontractkit/chainlink-common/pkg/logger" - - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" - evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" -) - -func standardHandler(method string, _ gjson.Result) (resp testutils.JSONRPCResponse) { - if method == "eth_subscribe" { - resp.Result = `"0x00"` - resp.Notify = HeadResult - return - } - return -} - -func newTestNode(t *testing.T, cfg config.NodePool, noNewHeadsThresholds time.Duration) *node { - return newTestNodeWithCallback(t, cfg, noNewHeadsThresholds, standardHandler) -} - -func newTestNodeWithCallback(t *testing.T, cfg config.NodePool, noNewHeadsThreshold time.Duration, callback testutils.JSONRPCHandler) *node { - s := testutils.NewWSServer(t, testutils.FixtureChainID, callback) - iN := NewNode(cfg, noNewHeadsThreshold, logger.Test(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) - n := iN.(*node) - return n -} - -// dial sets up the node and puts it into the live state, bypassing the -// normal Start() method which would fire off unwanted goroutines -func dial(t *testing.T, n *node) { - ctx := testutils.Context(t) - require.NoError(t, n.dial(ctx)) - n.setState(NodeStateAlive) - start(t, n) -} - -func start(t *testing.T, n *node) { - // must start to allow closing - err := n.StartOnce("test node", func() error { return nil }) - assert.NoError(t, err) -} - -func makeHeadResult(n int) string { - return fmt.Sprintf( - `{"difficulty":"0xf3a00","extraData":"0xd883010503846765746887676f312e372e318664617277696e","gasLimit":"0xffc001","gasUsed":"0x0","hash":"0x41800b5c3f1717687d85fc9018faac0a6e90b39deaa0b99e7fe4fe796ddeb26a","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0xd1aeb42885a43b72b518182ef893125814811048","mixHash":"0x0f98b15f1a4901a7e9204f3c500a7bd527b3fb2c3340e12176a44b83e414a69e","nonce":"0x0ece08ea8c49dfd9","number":"%s","parentHash":"0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x218","stateRoot":"0xc7b01007a10da045eacb90385887dd0c38fcb5db7393006bdde24b93873c334b","timestamp":"0x58318da2","totalDifficulty":"0x1f3a00","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}`, - testutils.IntToHex(n), - ) -} - -func makeNewHeadWSMessage(n int) string { - return fmt.Sprintf(`{"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x00","result":%s}}`, makeHeadResult(n)) -} - -func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { - t.Parallel() - - t.Run("with no poll and sync timeouts, exits on close", func(t *testing.T) { - pollAndSyncTimeoutsDisabledCfg := TestNodePoolConfig{} - n := newTestNode(t, pollAndSyncTimeoutsDisabledCfg, 0*time.Second) - dial(t, n) - - ch := make(chan struct{}) - n.wg.Add(1) - go func() { - defer close(ch) - n.aliveLoop() - }() - assert.NoError(t, n.Close()) - testutils.WaitWithTimeout(t, ch, "expected aliveLoop to exit") - }) - - t.Run("with no poll failures past threshold, stays alive", func(t *testing.T) { - threshold := 5 - cfg := TestNodePoolConfig{NodePollFailureThreshold: uint32(threshold), NodePollInterval: testutils.TestInterval} - var calls atomic.Int32 - n := newTestNodeWithCallback(t, cfg, time.Second*0, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { - switch method { - case "eth_subscribe": - resp.Result = `"0x00"` - resp.Notify = makeHeadResult(0) - return - case "eth_unsubscribe": - resp.Result = "true" - return - case "web3_clientVersion": - defer calls.Add(1) - // It starts working right before it hits threshold - if int(calls.Load())+1 >= threshold { - resp.Result = `"test client version"` - return - } - resp.Result = "this will error" - return - default: - t.Errorf("unexpected RPC method: %s", method) - } - return - }) - dial(t, n) - defer func() { assert.NoError(t, n.Close()) }() - - n.wg.Add(1) - go n.aliveLoop() - - testutils.AssertEventually(t, func() bool { - // Need to wait for one complete cycle before checking state so add - // 1 to threshold - return int(calls.Load()) > threshold+1 - }) - - assert.Equal(t, NodeStateAlive, n.State()) - }) - - t.Run("with threshold poll failures, transitions to unreachable", func(t *testing.T) { - syncTimeoutsDisabledCfg := TestNodePoolConfig{NodePollFailureThreshold: 3, NodePollInterval: testutils.TestInterval} - n := newTestNode(t, syncTimeoutsDisabledCfg, time.Second*0) - dial(t, n) - defer func() { assert.NoError(t, n.Close()) }() - - n.wg.Add(1) - go n.aliveLoop() - - testutils.AssertEventually(t, func() bool { - return n.State() == NodeStateUnreachable - }) - }) - - t.Run("with threshold poll failures, but we are the last node alive, forcibly keeps it alive", func(t *testing.T) { - threshold := 3 - cfg := TestNodePoolConfig{NodePollFailureThreshold: uint32(threshold), NodePollInterval: testutils.TestInterval} - var calls atomic.Int32 - n := newTestNodeWithCallback(t, cfg, time.Second*0, 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 - case "web3_clientVersion": - defer calls.Add(1) - resp.Error.Message = "this will error" - return - default: - t.Errorf("unexpected RPC method: %s", method) - } - return - }) - n.nLiveNodes = func() (int, int64, *big.Int) { return 1, 0, nil } - dial(t, n) - defer func() { assert.NoError(t, n.Close()) }() - - n.wg.Add(1) - go n.aliveLoop() - - testutils.AssertEventually(t, func() bool { - // Need to wait for one complete cycle before checking state so add - // 1 to threshold - return int(calls.Load()) > threshold+1 - }) - - assert.Equal(t, NodeStateAlive, n.State()) - }) - - t.Run("if initial subscribe fails, transitions to unreachable", func(t *testing.T) { - pollDisabledCfg := TestNodePoolConfig{} - n := newTestNodeWithCallback(t, pollDisabledCfg, testutils.TestInterval, func(string, gjson.Result) (resp testutils.JSONRPCResponse) { return }) - dial(t, n) - defer func() { assert.NoError(t, n.Close()) }() - - _, err := n.EthSubscribe(testutils.Context(t), make(chan *evmtypes.Head)) - assert.Error(t, err) - - n.wg.Add(1) - n.aliveLoop() - - assert.Equal(t, NodeStateUnreachable, n.State()) - // sc-39341: ensure failed EthSubscribe didn't register a (*rpc.ClientSubscription)(nil) which would lead to a panic on Unsubscribe - assert.Len(t, n.subs, 0) - }) - - t.Run("if remote RPC connection is closed transitions to unreachable", func(t *testing.T) { - // NoNewHeadsThreshold needs to be positive but must be very large so - // we don't time out waiting for a new head before we have a chance to - // handle the server disconnect - cfg := TestNodePoolConfig{NodePollInterval: 1 * time.Second} - chSubbed := make(chan struct{}, 1) - chPolled := make(chan struct{}) - s := testutils.NewWSServer(t, testutils.FixtureChainID, - func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { - switch method { - case "eth_subscribe": - chSubbed <- struct{}{} - resp.Result = `"0x00"` - resp.Notify = makeHeadResult(0) - return - case "web3_clientVersion": - select { - case chPolled <- struct{}{}: - default: - } - resp.Result = `"test client version 2"` - return - default: - t.Errorf("unexpected RPC method: %s", method) - } - return - }) - - iN := NewNode(cfg, testutils.WaitTimeout(t), logger.Test(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) - n := iN.(*node) - - dial(t, n) - defer func() { assert.NoError(t, n.Close()) }() - - n.wg.Add(1) - go n.aliveLoop() - - testutils.WaitWithTimeout(t, chSubbed, "timed out waiting for initial subscription") - testutils.WaitWithTimeout(t, chPolled, "timed out waiting for initial poll") - - assert.Equal(t, NodeStateAlive, n.State()) - - // Simulate remote websocket disconnect - // This causes sub.Err() to close - s.Close() - - testutils.AssertEventually(t, func() bool { - return n.State() == NodeStateUnreachable - }) - }) - - t.Run("when no new heads received for threshold, transitions to out of sync", func(t *testing.T) { - cfg := TestNodePoolConfig{} - chSubbed := make(chan struct{}, 2) - s := testutils.NewWSServer(t, testutils.FixtureChainID, - func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { - switch method { - case "eth_subscribe": - chSubbed <- struct{}{} - resp.Result = `"0x00"` - resp.Notify = makeHeadResult(0) - return - case "eth_unsubscribe": - resp.Result = "true" - return - case "web3_clientVersion": - resp.Result = `"test client version 2"` - return - default: - t.Errorf("unexpected RPC method: %s", method) - } - return - }) - - iN := NewNode(cfg, 1*time.Second, logger.Test(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) - n := iN.(*node) - - dial(t, n) - defer func() { assert.NoError(t, n.Close()) }() - - n.wg.Add(1) - go n.aliveLoop() - - testutils.WaitWithTimeout(t, chSubbed, "timed out waiting for initial subscription for InSync") - - testutils.AssertEventually(t, func() bool { - return n.State() == NodeStateOutOfSync - }) - - // Otherwise, there may be data race on dial() vs Close() (accessing ws.rpc) - testutils.WaitWithTimeout(t, chSubbed, "timed out waiting for initial subscription for OutOfSync") - }) - - t.Run("when no new heads received for threshold but we are the last live node, forcibly stays alive", func(t *testing.T) { - lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel) - pollDisabledCfg := TestNodePoolConfig{} - s := testutils.NewWSServer(t, testutils.FixtureChainID, - func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { - switch method { - case "eth_subscribe": - resp.Result = `"0x00"` - resp.Notify = makeHeadResult(0) - return - case "eth_unsubscribe": - resp.Result = "true" - return - default: - t.Errorf("unexpected RPC method: %s", method) - } - return - }) - - iN := NewNode(pollDisabledCfg, testutils.TestInterval, lggr, *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) - n := iN.(*node) - n.nLiveNodes = func() (int, int64, *big.Int) { return 1, 0, nil } - dial(t, n) - defer func() { assert.NoError(t, n.Close()) }() - - n.wg.Add(1) - go n.aliveLoop() - - // to avoid timing-dependent tests, simply wait for the log message instead - // wait for the log twice to be sure we have fully completed the code path and gone around the loop - testutils.WaitForLogMessageCount(t, observedLogs, msgCannotDisable, 2) - - assert.Equal(t, NodeStateAlive, n.State()) - }) - - t.Run("when behind more than SyncThreshold, transitions to out of sync", func(t *testing.T) { - cfg := TestNodePoolConfig{NodeSyncThreshold: 10, NodePollFailureThreshold: 2, NodePollInterval: 100 * time.Millisecond, NodeSelectionMode: NodeSelectionMode_HighestHead} - chSubbed := make(chan struct{}, 2) - var highestHead atomic.Int64 - const stall = 10 - s := testutils.NewWSServer(t, testutils.FixtureChainID, - func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { - switch method { - case "eth_subscribe": - chSubbed <- struct{}{} - resp.Result = `"0x00"` - resp.Notify = makeHeadResult(int(highestHead.Load())) - return - case "eth_unsubscribe": - resp.Result = "true" - return - case "web3_clientVersion": - resp.Result = `"test client version 2"` - // always tick each poll, but only signal back up to stall - if n := highestHead.Add(1); n <= stall { - resp.Notify = makeHeadResult(int(n)) - } - return - default: - t.Errorf("unexpected RPC method: %s", method) - } - return - }) - - iN := NewNode(cfg, 0*time.Second, logger.Test(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) - n := iN.(*node) - n.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { - return 2, highestHead.Load(), nil - } - - dial(t, n) - defer func() { assert.NoError(t, n.Close()) }() - - n.wg.Add(1) - go n.aliveLoop() - - testutils.WaitWithTimeout(t, chSubbed, "timed out waiting for initial subscription for InSync") - - // ensure alive up to stall - testutils.AssertEventually(t, func() bool { - state, num, _ := n.StateAndLatest() - if num < stall { - require.Equal(t, NodeStateAlive, state) - } - return num == stall - }) - - testutils.AssertEventually(t, func() bool { - state, num, _ := n.StateAndLatest() - return state == NodeStateOutOfSync && num == stall - }) - assert.GreaterOrEqual(t, highestHead.Load(), int64(stall+cfg.SyncThreshold())) - - // Otherwise, there may be data race on dial() vs Close() (accessing ws.rpc) - testutils.WaitWithTimeout(t, chSubbed, "timed out waiting for initial subscription for OutOfSync") - }) - - t.Run("when behind but SyncThreshold=0, stay alive", func(t *testing.T) { - cfg := TestNodePoolConfig{NodeSyncThreshold: 0, NodePollFailureThreshold: 2, NodePollInterval: 100 * time.Millisecond, NodeSelectionMode: NodeSelectionMode_HighestHead} - chSubbed := make(chan struct{}, 1) - var highestHead atomic.Int64 - const stall = 10 - s := testutils.NewWSServer(t, testutils.FixtureChainID, - func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { - switch method { - case "eth_subscribe": - chSubbed <- struct{}{} - resp.Result = `"0x00"` - resp.Notify = makeHeadResult(int(highestHead.Load())) - return - case "eth_unsubscribe": - resp.Result = "true" - return - case "web3_clientVersion": - resp.Result = `"test client version 2"` - // always tick each poll, but only signal back up to stall - if n := highestHead.Add(1); n <= stall { - resp.Notify = makeHeadResult(int(n)) - } - return - default: - t.Errorf("unexpected RPC method: %s", method) - } - return - }) - - iN := NewNode(cfg, 0*time.Second, logger.Test(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) - n := iN.(*node) - n.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { - return 2, highestHead.Load(), nil - } - - dial(t, n) - defer func() { assert.NoError(t, n.Close()) }() - - n.wg.Add(1) - go n.aliveLoop() - - testutils.WaitWithTimeout(t, chSubbed, "timed out waiting for initial subscription for InSync") - - // ensure alive up to stall - testutils.AssertEventually(t, func() bool { - state, num, _ := n.StateAndLatest() - require.Equal(t, NodeStateAlive, state) - return num == stall - }) - - assert.Equal(t, NodeStateAlive, n.state) - assert.GreaterOrEqual(t, highestHead.Load(), int64(stall+cfg.SyncThreshold())) - }) - - t.Run("when behind more than SyncThreshold but we are the last live node, forcibly stays alive", func(t *testing.T) { - lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel) - cfg := TestNodePoolConfig{NodeSyncThreshold: 5, NodePollFailureThreshold: 2, NodePollInterval: 100 * time.Millisecond, NodeSelectionMode: NodeSelectionMode_HighestHead} - chSubbed := make(chan struct{}, 1) - var highestHead atomic.Int64 - const stall = 10 - s := testutils.NewWSServer(t, testutils.FixtureChainID, - func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { - switch method { - case "eth_subscribe": - chSubbed <- struct{}{} - resp.Result = `"0x00"` - n := highestHead.Load() - if n > stall { - n = stall - } - resp.Notify = makeHeadResult(int(n)) - return - case "eth_unsubscribe": - resp.Result = "true" - return - case "web3_clientVersion": - resp.Result = `"test client version 2"` - // always tick each poll, but only signal back up to stall - if n := highestHead.Add(1); n <= stall { - resp.Notify = makeHeadResult(int(n)) - } - return - default: - t.Errorf("unexpected RPC method: %s", method) - } - return - }) - - iN := NewNode(cfg, 0*time.Second, lggr, *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) - n := iN.(*node) - n.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { - return 1, highestHead.Load(), nil - } - - dial(t, n) - defer func() { assert.NoError(t, n.Close()) }() - - n.wg.Add(1) - go n.aliveLoop() - - testutils.WaitWithTimeout(t, chSubbed, "timed out waiting for initial subscription for InSync") - - // ensure alive up to stall - testutils.AssertEventually(t, func() bool { - state, num, _ := n.StateAndLatest() - require.Equal(t, NodeStateAlive, state) - return num == stall - }) - - assert.Equal(t, NodeStateAlive, n.state) - testutils.AssertEventually(t, func() bool { - return highestHead.Load() >= int64(stall+cfg.SyncThreshold()) - }) - - testutils.WaitForLogMessageCount(t, observedLogs, msgCannotDisable, 1) - - state, num, _ := n.StateAndLatest() - assert.Equal(t, NodeStateAlive, state) - assert.Equal(t, int64(stall), num) - }) -} - -func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { - t.Parallel() - - t.Run("exits on close", func(t *testing.T) { - cfg := TestNodePoolConfig{} - n := newTestNode(t, cfg, time.Second*0) - dial(t, n) - n.setState(NodeStateOutOfSync) - - ch := make(chan struct{}) - - n.wg.Add(1) - go func() { - defer close(ch) - n.outOfSyncLoop(func(num int64, td *big.Int) bool { return false }) - }() - assert.NoError(t, n.Close()) - testutils.WaitWithTimeout(t, ch, "expected outOfSyncLoop to exit") - }) - - t.Run("if initial subscribe fails, transitions to unreachable", func(t *testing.T) { - cfg := TestNodePoolConfig{} - n := newTestNodeWithCallback(t, cfg, time.Second*0, func(string, gjson.Result) (resp testutils.JSONRPCResponse) { return }) - dial(t, n) - n.setState(NodeStateOutOfSync) - defer func() { assert.NoError(t, n.Close()) }() - - n.wg.Add(1) - - n.outOfSyncLoop(func(num int64, td *big.Int) bool { return num == 0 }) - assert.Equal(t, NodeStateUnreachable, n.State()) - }) - - t.Run("transitions to unreachable if remote RPC subscription channel closed", func(t *testing.T) { - cfg := TestNodePoolConfig{} - chSubbed := make(chan struct{}, 1) - s := testutils.NewWSServer(t, testutils.FixtureChainID, - func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { - switch method { - case "eth_subscribe": - chSubbed <- struct{}{} - resp.Result = `"0x00"` - resp.Notify = makeHeadResult(0) - return - default: - t.Errorf("unexpected RPC method: %s", method) - } - return - }) - - iN := NewNode(cfg, time.Second, logger.Test(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) - n := iN.(*node) - - dial(t, n) - n.setState(NodeStateOutOfSync) - defer func() { assert.NoError(t, n.Close()) }() - - n.wg.Add(1) - go n.outOfSyncLoop(func(num int64, td *big.Int) bool { return num == 0 }) - - testutils.WaitWithTimeout(t, chSubbed, "timed out waiting for initial subscription") - - assert.Equal(t, NodeStateOutOfSync, n.State()) - - // Simulate remote websocket disconnect - // This causes sub.Err() to close - s.Close() - - testutils.AssertEventually(t, func() bool { - return n.State() == NodeStateUnreachable - }) - }) - - t.Run("transitions to alive if it receives a newer head", func(t *testing.T) { - // NoNewHeadsThreshold needs to be positive but must be very large so - // we don't time out waiting for a new head before we have a chance to - // handle the server disconnect - lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - cfg := TestNodePoolConfig{} - chSubbed := make(chan struct{}, 1) - s := testutils.NewWSServer(t, testutils.FixtureChainID, - func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { - switch method { - case "eth_subscribe": - chSubbed <- struct{}{} - resp.Result = `"0x00"` - resp.Notify = makeNewHeadWSMessage(42) - return - case "eth_unsubscribe": - resp.Result = "true" - return - default: - t.Errorf("unexpected RPC method: %s", method) - } - return - }) - - iN := NewNode(cfg, time.Second*0, lggr, *s.WSURL(), nil, "test node", 0, testutils.FixtureChainID, 1) - n := iN.(*node) - - start(t, n) - n.setState(NodeStateOutOfSync) - defer func() { assert.NoError(t, n.Close()) }() - - n.wg.Add(1) - go n.outOfSyncLoop(func(num int64, td *big.Int) bool { return num < 43 }) - - testutils.WaitWithTimeout(t, chSubbed, "timed out waiting for initial subscription") - - assert.Equal(t, NodeStateOutOfSync, n.State()) - - // heads less than latest seen head are ignored; they do not make the node live - for i := 0; i < 43; i++ { - msg := makeNewHeadWSMessage(i) - s.MustWriteBinaryMessageSync(t, msg) - testutils.WaitForLogMessageCount(t, observedLogs, msgReceivedBlock, i+1) - assert.Equal(t, NodeStateOutOfSync, n.State()) - } - - msg := makeNewHeadWSMessage(43) - s.MustWriteBinaryMessageSync(t, msg) - - testutils.AssertEventually(t, func() bool { - s, n, td := n.StateAndLatest() - return s == NodeStateAlive && n != -1 && td != nil - }) - - testutils.WaitForLogMessage(t, observedLogs, msgInSync) - }) - - t.Run("transitions to alive if back in-sync", func(t *testing.T) { - lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - cfg := TestNodePoolConfig{NodeSyncThreshold: 5, NodeSelectionMode: NodeSelectionMode_HighestHead} - chSubbed := make(chan struct{}, 1) - const stall = 42 - s := testutils.NewWSServer(t, testutils.FixtureChainID, - func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { - switch method { - case "eth_subscribe": - chSubbed <- struct{}{} - resp.Result = `"0x00"` - resp.Notify = makeNewHeadWSMessage(stall) - return - case "eth_unsubscribe": - resp.Result = "true" - return - default: - t.Errorf("unexpected RPC method: %s", method) - } - return - }) - - iN := NewNode(cfg, time.Second*0, lggr, *s.WSURL(), nil, "test node", 0, testutils.FixtureChainID, 1) - n := iN.(*node) - n.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { - return 2, stall + int64(cfg.SyncThreshold()), nil - } - - start(t, n) - n.setState(NodeStateOutOfSync) - defer func() { assert.NoError(t, n.Close()) }() - - n.wg.Add(1) - go n.outOfSyncLoop(n.isOutOfSync) - - testutils.WaitWithTimeout(t, chSubbed, "timed out waiting for initial subscription") - - assert.Equal(t, NodeStateOutOfSync, n.State()) - - // heads less than stall (latest seen head - SyncThreshold) are ignored; they do not make the node live - for i := 0; i < stall; i++ { - msg := makeNewHeadWSMessage(i) - s.MustWriteBinaryMessageSync(t, msg) - testutils.WaitForLogMessageCount(t, observedLogs, msgReceivedBlock, i+1) - assert.Equal(t, NodeStateOutOfSync, n.State()) - } - - msg := makeNewHeadWSMessage(stall) - s.MustWriteBinaryMessageSync(t, msg) - - testutils.AssertEventually(t, func() bool { - s, n, td := n.StateAndLatest() - return s == NodeStateAlive && n != -1 && td != nil - }) - - testutils.WaitForLogMessage(t, observedLogs, msgInSync) - }) - - t.Run("if no live nodes are available, forcibly marks this one alive again", func(t *testing.T) { - cfg := TestNodePoolConfig{} - chSubbed := make(chan struct{}, 1) - s := testutils.NewWSServer(t, testutils.FixtureChainID, - func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { - switch method { - case "eth_subscribe": - chSubbed <- struct{}{} - resp.Result = `"0x00"` - resp.Notify = makeHeadResult(0) - return - case "eth_unsubscribe": - resp.Result = "true" - return - default: - t.Errorf("unexpected RPC method: %s", method) - } - return - }) - - iN := NewNode(cfg, testutils.TestInterval, logger.Test(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) - n := iN.(*node) - n.nLiveNodes = func() (int, int64, *big.Int) { return 0, 0, nil } - - dial(t, n) - n.setState(NodeStateOutOfSync) - defer func() { assert.NoError(t, n.Close()) }() - - n.wg.Add(1) - go n.outOfSyncLoop(func(num int64, td *big.Int) bool { return num == 0 }) - - testutils.WaitWithTimeout(t, chSubbed, "timed out waiting for initial subscription") - - testutils.AssertEventually(t, func() bool { - return n.State() == NodeStateAlive - }) - }) -} - -func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { - t.Parallel() - - t.Run("exits on close", func(t *testing.T) { - cfg := TestNodePoolConfig{} - n := newTestNode(t, cfg, time.Second*0) - start(t, n) - n.setState(NodeStateUnreachable) - - ch := make(chan struct{}) - n.wg.Add(1) - go func() { - n.unreachableLoop() - close(ch) - }() - assert.NoError(t, n.Close()) - testutils.WaitWithTimeout(t, ch, "expected unreachableLoop to exit") - }) - - t.Run("on successful redial and verify, transitions to alive", func(t *testing.T) { - cfg := TestNodePoolConfig{} - n := newTestNode(t, cfg, time.Second*0) - start(t, n) - defer func() { assert.NoError(t, n.Close()) }() - n.setState(NodeStateUnreachable) - n.wg.Add(1) - - go n.unreachableLoop() - - testutils.AssertEventually(t, func() bool { - return n.State() == NodeStateAlive - }) - }) - - t.Run("on successful redial but failed verify, transitions to invalid chain ID", func(t *testing.T) { - cfg := TestNodePoolConfig{} - s := testutils.NewWSServer(t, testutils.FixtureChainID, standardHandler) - lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel) - iN := NewNode(cfg, time.Second*0, lggr, *s.WSURL(), nil, "test node", 0, big.NewInt(42), 1) - n := iN.(*node) - defer func() { assert.NoError(t, n.Close()) }() - start(t, n) - n.setState(NodeStateUnreachable) - n.wg.Add(1) - - go n.unreachableLoop() - - testutils.WaitForLogMessage(t, observedLogs, "Failed to redial RPC node; remote endpoint returned the wrong chain ID") - - testutils.AssertEventually(t, func() bool { - return n.State() == NodeStateInvalidChainID - }) - }) - - t.Run("on failed redial, keeps trying to redial", func(t *testing.T) { - cfg := TestNodePoolConfig{} - lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - iN := NewNode(cfg, time.Second*0, lggr, *testutils.MustParseURL(t, "ws://test.invalid"), nil, "test node", 0, big.NewInt(42), 1) - n := iN.(*node) - defer func() { assert.NoError(t, n.Close()) }() - start(t, n) - n.setState(NodeStateUnreachable) - n.wg.Add(1) - - go n.unreachableLoop() - - testutils.WaitForLogMessageCount(t, observedLogs, "Failed to redial RPC node", 3) - - assert.Equal(t, NodeStateUnreachable, n.State()) - }) -} -func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { - t.Parallel() - - t.Run("exits on close", func(t *testing.T) { - cfg := TestNodePoolConfig{} - n := newTestNode(t, cfg, time.Second*0) - start(t, n) - n.setState(NodeStateInvalidChainID) - - ch := make(chan struct{}) - n.wg.Add(1) - go func() { - n.invalidChainIDLoop() - close(ch) - }() - assert.NoError(t, n.Close()) - testutils.WaitWithTimeout(t, ch, "expected invalidChainIDLoop to exit") - }) - - t.Run("on successful verify, transitions to alive", func(t *testing.T) { - cfg := TestNodePoolConfig{} - n := newTestNode(t, cfg, time.Second*0) - dial(t, n) - defer func() { assert.NoError(t, n.Close()) }() - n.setState(NodeStateInvalidChainID) - n.wg.Add(1) - - go n.invalidChainIDLoop() - - testutils.AssertEventually(t, func() bool { - return n.State() == NodeStateAlive - }) - }) - - t.Run("on failed verify, keeps checking", func(t *testing.T) { - cfg := TestNodePoolConfig{} - s := testutils.NewWSServer(t, testutils.FixtureChainID, standardHandler) - lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel) - iN := NewNode(cfg, time.Second*0, lggr, *s.WSURL(), nil, "test node", 0, big.NewInt(42), 1) - n := iN.(*node) - defer func() { assert.NoError(t, n.Close()) }() - dial(t, n) - n.setState(NodeStateUnreachable) - n.wg.Add(1) - - go n.unreachableLoop() - - testutils.WaitForLogMessageCount(t, observedLogs, "Failed to redial RPC node; remote endpoint returned the wrong chain ID", 3) - - assert.Equal(t, NodeStateInvalidChainID, n.State()) - }) -} diff --git a/core/chains/evm/headtracker/head_tracker_test.go b/core/chains/evm/headtracker/head_tracker_test.go index 0dce9509b1a..8e1ff1aa7ba 100644 --- a/core/chains/evm/headtracker/head_tracker_test.go +++ b/core/chains/evm/headtracker/head_tracker_test.go @@ -1173,6 +1173,7 @@ func TestHeadTracker_LatestAndFinalizedBlock(t *testing.T) { c.EVM[0].FinalityTagEnabled = ptr(opts.FinalityTagEnabled) c.EVM[0].FinalizedBlockOffset = ptr(opts.FinalizedBlockOffset) c.EVM[0].FinalityDepth = ptr(opts.FinalityDepth) + c.EVM[0].HeadTracker.FinalityTagBypass = ptr(false) }) evmcfg := evmtest.NewChainScopedConfig(t, cfg) @@ -1218,7 +1219,7 @@ func TestHeadTracker_LatestAndFinalizedBlock(t *testing.T) { htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(nil, nil).Once() _, _, err := htu.headTracker.LatestAndFinalizedBlock(ctx) - require.ErrorContains(t, err, "expected finalized block to be valid") + require.ErrorContains(t, err, "failed to get valid latest finalized block") }) t.Run("returns latest finalized block as is if FinalizedBlockOffset is 0 (finality tag)", func(t *testing.T) { htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true}) From 650e7c56f1615364902d625ea4b27c2d973ae733 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Wed, 5 Jun 2024 16:18:05 +0200 Subject: [PATCH 34/53] make generate --- common/types/mocks/head.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/common/types/mocks/head.go b/common/types/mocks/head.go index 8fcd57a33c9..368253fd5d8 100644 --- a/common/types/mocks/head.go +++ b/common/types/mocks/head.go @@ -184,6 +184,36 @@ func (_m *Head[BLOCK_HASH]) HashAtHeight(blockNum int64) BLOCK_HASH { return r0 } +// HeadAtHeight provides a mock function with given fields: blockNum +func (_m *Head[BLOCK_HASH]) HeadAtHeight(blockNum int64) (types.Head[BLOCK_HASH], error) { + ret := _m.Called(blockNum) + + if len(ret) == 0 { + panic("no return value specified for HeadAtHeight") + } + + var r0 types.Head[BLOCK_HASH] + var r1 error + if rf, ok := ret.Get(0).(func(int64) (types.Head[BLOCK_HASH], error)); ok { + return rf(blockNum) + } + if rf, ok := ret.Get(0).(func(int64) types.Head[BLOCK_HASH]); ok { + r0 = rf(blockNum) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(types.Head[BLOCK_HASH]) + } + } + + if rf, ok := ret.Get(1).(func(int64) error); ok { + r1 = rf(blockNum) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // IsValid provides a mock function with given fields: func (_m *Head[BLOCK_HASH]) IsValid() bool { ret := _m.Called() From 44bd1f52f81100b3f4a60c28171304c610653153 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Wed, 5 Jun 2024 18:27:43 +0200 Subject: [PATCH 35/53] simplify capturing of app layer observations --- common/client/ctx.go | 17 ++ common/client/ctx_test.go | 16 ++ common/client/mock_node_test.go | 36 +-- .../mock_pool_chain_info_provider_test.go | 6 +- common/client/multi_node.go | 99 ++------ common/client/multi_node_test.go | 100 +++----- common/client/node.go | 13 +- common/client/node_fsm.go | 10 +- common/client/node_lifecycle.go | 10 +- common/client/node_lifecycle_test.go | 2 +- common/client/types.go | 4 +- core/chains/evm/client/rpc_client.go | 79 +++--- core/chains/evm/client/rpc_client_test.go | 80 ++++-- .../keystone/generated/forwarder/forwarder.go | 18 +- .../keystone_capability_registry.go | 231 +++++------------- ...rapper-dependency-versions-do-not-edit.txt | 4 +- 16 files changed, 290 insertions(+), 435 deletions(-) create mode 100644 common/client/ctx.go create mode 100644 common/client/ctx_test.go diff --git a/common/client/ctx.go b/common/client/ctx.go new file mode 100644 index 00000000000..57b2fc8a866 --- /dev/null +++ b/common/client/ctx.go @@ -0,0 +1,17 @@ +package client + +import "context" + +type multiNodeContextKey int + +const ( + contextKeyHeathCheckRequest multiNodeContextKey = iota + 1 +) + +func CtxAddHealthCheckFlag(ctx context.Context) context.Context { + return context.WithValue(ctx, contextKeyHeathCheckRequest, struct{}{}) +} + +func CtxIsHeathCheckRequest(ctx context.Context) bool { + return ctx.Value(contextKeyHeathCheckRequest) != nil +} diff --git a/common/client/ctx_test.go b/common/client/ctx_test.go new file mode 100644 index 00000000000..822b36c3f81 --- /dev/null +++ b/common/client/ctx_test.go @@ -0,0 +1,16 @@ +package client + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" +) + +func TestContext(t *testing.T) { + ctx := tests.Context(t) + assert.False(t, CtxIsHeathCheckRequest(ctx), "expected false for test context") + ctx = CtxAddHealthCheckFlag(ctx) + assert.True(t, CtxIsHeathCheckRequest(ctx), "expected context to contain the healthcheck flag") +} diff --git a/common/client/mock_node_test.go b/common/client/mock_node_test.go index e90063b33e8..055fcf84807 100644 --- a/common/client/mock_node_test.go +++ b/common/client/mock_node_test.go @@ -14,55 +14,55 @@ type mockNode[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] stru mock.Mock } -// Close provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Close() error { +// AppLayerObservations provides a mock function with given fields: +func (_m *mockNode[CHAIN_ID, HEAD, RPC]) AppLayerObservations() ChainInfo { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for Close") + panic("no return value specified for AppLayerObservations") } - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { + var r0 ChainInfo + if rf, ok := ret.Get(0).(func() ChainInfo); ok { r0 = rf() } else { - r0 = ret.Error(0) + r0 = ret.Get(0).(ChainInfo) } return r0 } -// ConfiguredChainID provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) ConfiguredChainID() CHAIN_ID { +// Close provides a mock function with given fields: +func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Close() error { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for ConfiguredChainID") + panic("no return value specified for Close") } - var r0 CHAIN_ID - if rf, ok := ret.Get(0).(func() CHAIN_ID); ok { + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() } else { - r0 = ret.Get(0).(CHAIN_ID) + r0 = ret.Error(0) } return r0 } -// HighestChainInfo provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) HighestChainInfo() ChainInfo { +// ConfiguredChainID provides a mock function with given fields: +func (_m *mockNode[CHAIN_ID, HEAD, RPC]) ConfiguredChainID() CHAIN_ID { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for HighestChainInfo") + panic("no return value specified for ConfiguredChainID") } - var r0 ChainInfo - if rf, ok := ret.Get(0).(func() ChainInfo); ok { + var r0 CHAIN_ID + if rf, ok := ret.Get(0).(func() CHAIN_ID); ok { r0 = rf() } else { - r0 = ret.Get(0).(ChainInfo) + r0 = ret.Get(0).(CHAIN_ID) } return r0 diff --git a/common/client/mock_pool_chain_info_provider_test.go b/common/client/mock_pool_chain_info_provider_test.go index 563641f701d..1b250eaea75 100644 --- a/common/client/mock_pool_chain_info_provider_test.go +++ b/common/client/mock_pool_chain_info_provider_test.go @@ -9,12 +9,12 @@ type mockPoolChainInfoProvider struct { mock.Mock } -// HighestChainInfo provides a mock function with given fields: -func (_m *mockPoolChainInfoProvider) HighestChainInfo() ChainInfo { +// AppLayerObservations provides a mock function with given fields: +func (_m *mockPoolChainInfoProvider) AppLayerObservations() ChainInfo { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for HighestChainInfo") + panic("no return value specified for AppLayerObservations") } var r0 ChainInfo diff --git a/common/client/multi_node.go b/common/client/multi_node.go index 556a56d08b4..d60c7071b23 100644 --- a/common/client/multi_node.go +++ b/common/client/multi_node.go @@ -33,21 +33,6 @@ var ( Name: "multi_node_invariant_violations", Help: "The number of invariant violations", }, []string{"network", "chainId", "invariant"}) - // PromMultiNodeSelectRPCTiming - reports how long it took us to select an RPC - PromMultiNodeSelectRPCTiming = promauto.NewHistogramVec(prometheus.HistogramOpts{ - Name: "multi_node_select_rpc_timing", - Help: "Defines how long it took us to pick an RPC to perform request", - Buckets: []float64{ - float64(50 * time.Millisecond), - float64(100 * time.Millisecond), - float64(200 * time.Millisecond), - float64(500 * time.Millisecond), - float64(1 * time.Second), - float64(2 * time.Second), - float64(4 * time.Second), - float64(8 * time.Second), - }, - }, []string{"network", "chainId"}) ErroringNodeError = fmt.Errorf("no live nodes available") ) @@ -125,9 +110,6 @@ type multiNode[ activeMu sync.RWMutex activeNode Node[CHAIN_ID, HEAD, RPC_CLIENT] - highestChainInfoMu sync.RWMutex - highestChainInfo ChainInfo - chStop services.StopChan wg sync.WaitGroup @@ -184,7 +166,6 @@ func NewMultiNode[ reportInterval: reportInterval, deathDeclarationDelay: reportInterval, sendTxSoftTimeout: sendTxSoftTimeout, - highestChainInfo: ChainInfo{TotalDifficulty: big.NewInt(0)}, } c.lggr.Debugf("The MultiNode is configured to use NodeSelectionMode: %s", selectionMode) @@ -257,10 +238,6 @@ 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) { - start := time.Now() - defer func() { - PromMultiNodeSelectRPCTiming.WithLabelValues(c.chainFamily, c.chainID.String()).Observe(float64(time.Since(start))) - }() c.activeMu.RLock() node = c.activeNode c.activeMu.RUnlock() @@ -276,7 +253,9 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return // another goroutine beat us here } - c.finalizeActiveNodeObservationsUnsafe() + if c.activeNode != nil { + c.activeNode.UnsubscribeAllExceptAliveLoop() + } c.activeNode = c.nodeSelector.Select() if c.activeNode == nil { @@ -308,50 +287,18 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return nLiveNodes, ch } -// HighestChainInfo - returns highest ChainInfo ever observed by any activeNode -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) HighestChainInfo() ChainInfo { - // HighestChainInfo provides the chain info observed by the application layer, which we use to invalidate lagging RPCs. - // The method is called in two cases: to report an RPC's state and to reevaluate the state during activeNode selection. - // We should try to report the HighestChainInfo directly from the currently active node. - // If we can't acquire read lock, there is a goroutine that selects a new activeNode, and the HighestChainInfo was called by: - // 1. The same goroutine that selects a new activeNode, so the HighestChainInfo is already up-to-date, so it's OK to return the cached value. - // 2. Separate goroutine that tries to report the state of an RPC, so it's also OK to use cached value. - if c.activeMu.TryRLock() { - c.updateHighestChainInfoUnsafe() - c.activeMu.RUnlock() - } - - c.highestChainInfoMu.RLock() - defer c.highestChainInfoMu.RUnlock() - return c.highestChainInfo -} - -// finalizeActiveNodeObservationsUnsafe - cancels all activeNode subscriptions and captures HighestChainInfo. -// NOTE: UNSAFE. Must be called with activeMu lock. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) finalizeActiveNodeObservationsUnsafe() { - if c.activeNode == nil { - return - } - - c.activeNode.UnsubscribeAllExceptAliveLoop() - c.updateHighestChainInfoUnsafe() -} - -// updateHighestChainInfoUnsafe - updates HighestChainInfo. Must be called with activeMu read lock. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) updateHighestChainInfoUnsafe() { - if c.activeNode == nil { - return +// AppLayerObservations - 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]) AppLayerObservations() ChainInfo { + ch := ChainInfo{ + TotalDifficulty: big.NewInt(0), } - nodeChainInfo := c.activeNode.HighestChainInfo() - c.highestChainInfoMu.Lock() - defer c.highestChainInfoMu.Unlock() - highestChainInfo := ChainInfo{ - BlockNumber: max(c.highestChainInfo.BlockNumber, nodeChainInfo.BlockNumber), - FinalizedBlockNumber: max(c.highestChainInfo.FinalizedBlockNumber, nodeChainInfo.FinalizedBlockNumber), - TotalDifficulty: big.NewInt(0), + for _, n := range c.nodes { + nodeChainInfo := n.AppLayerObservations() + ch.BlockNumber = max(ch.BlockNumber, nodeChainInfo.BlockNumber) + ch.FinalizedBlockNumber = max(ch.FinalizedBlockNumber, nodeChainInfo.FinalizedBlockNumber) + ch.SetTotalDifficultyIfGt(nodeChainInfo.TotalDifficulty) } - highestChainInfo.SetTotalDifficultyIfGt(nodeChainInfo.TotalDifficulty) - c.highestChainInfo = highestChainInfo + 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() { @@ -367,22 +314,12 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP c.activeMu.Lock() defer c.activeMu.Unlock() - if bestNode == c.activeNode { - return - } - - c.finalizeActiveNodeObservationsUnsafe() - // ensure that after updating the highest chain info, the best node is still alive - if bestNode.State() != nodeStateAlive { - // If the final observations invalidated the new best node, we still want to reset activeNode. - // This way, `RoundRobin` and `PriorityLevel` selection modes will get closer to expected behavior, - // as there is a chance that on the next call to `selectNode` or `checkLease`, we'll pick a new RPC instead of - // sticking to the one with the highest finalized block. - c.activeNode = nil - return + if bestNode != c.activeNode { + if c.activeNode != nil { + c.activeNode.UnsubscribeAllExceptAliveLoop() + } + c.activeNode = bestNode } - - c.activeNode = bestNode } func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) checkLeaseLoop() { diff --git a/common/client/multi_node_test.go b/common/client/multi_node_test.go index a884f11bb26..589a3a7e24b 100644 --- a/common/client/multi_node_test.go +++ b/common/client/multi_node_test.go @@ -385,18 +385,12 @@ func TestMultiNode_selectNode(t *testing.T) { require.NoError(t, err) require.Equal(t, prevActiveNode.String(), newActiveNode.String()) }) - t.Run("Updates node if active is not healthy and sets highest chain info", func(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.On("String").Return("oldBest").Maybe() oldBest.On("UnsubscribeAllExceptAliveLoop").Once() - expectedChainInfo := ChainInfo{ - BlockNumber: 100, - FinalizedBlockNumber: 150, - TotalDifficulty: big.NewInt(200), - } - oldBest.On("HighestChainInfo").Return(expectedChainInfo).Once() newBest := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) newBest.On("String").Return("newBest").Maybe() mn := newTestMultiNode(t, multiNodeOpts{ @@ -407,11 +401,6 @@ func TestMultiNode_selectNode(t *testing.T) { nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) nodeSelector.On("Select").Return(oldBest).Once() mn.nodeSelector = nodeSelector - mn.highestChainInfo = ChainInfo{ - BlockNumber: 10, - FinalizedBlockNumber: 15, - TotalDifficulty: big.NewInt(20), - } activeNode, err := mn.selectNode() require.NoError(t, err) require.Equal(t, oldBest.String(), activeNode.String()) @@ -421,7 +410,6 @@ func TestMultiNode_selectNode(t *testing.T) { newActiveNode, err := mn.selectNode() require.NoError(t, err) require.Equal(t, newBest.String(), newActiveNode.String()) - require.Equal(t, expectedChainInfo, mn.highestChainInfo) }) t.Run("No active nodes - reports critical error", func(t *testing.T) { t.Parallel() @@ -446,45 +434,39 @@ func TestMultiNode_selectNode(t *testing.T) { func TestMultiNode_ChainInfo(t *testing.T) { t.Parallel() type nodeParams struct { - LatestChainInfo ChainInfo - HighestChainInfo ChainInfo - State nodeState - } - - chainID := types.RandomID() - type testCase struct { - Name string - ExpectedNLiveNodes int - ExpectedLatestChainInfo ChainInfo - ExpectedHighestChainInfo ChainInfo - NodeParams []nodeParams - ActiveNodeI int + LatestChainInfo ChainInfo + AppLayerObservations ChainInfo + State nodeState } - testCases := []testCase{ + testCases := []struct { + Name string + ExpectedNLiveNodes int + ExpectedLatestChainInfo ChainInfo + ExpectedAppLayerObservations ChainInfo + NodeParams []nodeParams + }{ { Name: "no nodes", ExpectedLatestChainInfo: ChainInfo{ TotalDifficulty: big.NewInt(0), }, - ExpectedHighestChainInfo: ChainInfo{ + ExpectedAppLayerObservations: ChainInfo{ TotalDifficulty: big.NewInt(0), }, - ActiveNodeI: -1, }, { - Name: "Best node is not healthy and not active", + Name: "Best node is not healthy", ExpectedNLiveNodes: 3, ExpectedLatestChainInfo: ChainInfo{ BlockNumber: 20, FinalizedBlockNumber: 10, TotalDifficulty: big.NewInt(10), }, - ExpectedHighestChainInfo: ChainInfo{ - BlockNumber: 24, - FinalizedBlockNumber: 14, - TotalDifficulty: big.NewInt(15), + ExpectedAppLayerObservations: ChainInfo{ + BlockNumber: 1005, + FinalizedBlockNumber: 995, + TotalDifficulty: big.NewInt(2005), }, - ActiveNodeI: 2, NodeParams: []nodeParams{ { State: nodeStateOutOfSync, @@ -493,7 +475,7 @@ func TestMultiNode_ChainInfo(t *testing.T) { FinalizedBlockNumber: 990, TotalDifficulty: big.NewInt(2000), }, - HighestChainInfo: ChainInfo{ + AppLayerObservations: ChainInfo{ BlockNumber: 1005, FinalizedBlockNumber: 995, TotalDifficulty: big.NewInt(2005), @@ -506,7 +488,7 @@ func TestMultiNode_ChainInfo(t *testing.T) { FinalizedBlockNumber: 10, TotalDifficulty: big.NewInt(9), }, - HighestChainInfo: ChainInfo{ + AppLayerObservations: ChainInfo{ BlockNumber: 25, FinalizedBlockNumber: 15, TotalDifficulty: big.NewInt(14), @@ -519,7 +501,7 @@ func TestMultiNode_ChainInfo(t *testing.T) { FinalizedBlockNumber: 9, TotalDifficulty: big.NewInt(10), }, - HighestChainInfo: ChainInfo{ + AppLayerObservations: ChainInfo{ BlockNumber: 24, FinalizedBlockNumber: 14, TotalDifficulty: big.NewInt(15), @@ -532,7 +514,7 @@ func TestMultiNode_ChainInfo(t *testing.T) { FinalizedBlockNumber: 1, TotalDifficulty: nil, }, - HighestChainInfo: ChainInfo{ + AppLayerObservations: ChainInfo{ BlockNumber: 16, FinalizedBlockNumber: 6, TotalDifficulty: nil, @@ -541,52 +523,30 @@ func TestMultiNode_ChainInfo(t *testing.T) { }, }, } + + chainID := types.RandomID() + mn := newTestMultiNode(t, multiNodeOpts{ + selectionMode: NodeSelectionModeRoundRobin, + chainID: chainID, + }) for i := range testCases { tc := testCases[i] t.Run(tc.Name, func(t *testing.T) { - mn := newTestMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - }) for _, params := range tc.NodeParams { node := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) node.On("StateAndLatest").Return(params.State, params.LatestChainInfo) - node.On("HighestChainInfo").Return(params.HighestChainInfo).Maybe() + node.On("AppLayerObservations").Return(params.AppLayerObservations) mn.nodes = append(mn.nodes, node) } - if tc.ActiveNodeI >= 0 { - mn.activeNode = mn.nodes[tc.ActiveNodeI] - } - nNodes, latestChainInfo := mn.LatestChainInfo() assert.Equal(t, tc.ExpectedNLiveNodes, nNodes) assert.Equal(t, tc.ExpectedLatestChainInfo, latestChainInfo) - highestChainInfo := mn.HighestChainInfo() - assert.Equal(t, tc.ExpectedHighestChainInfo, highestChainInfo) + highestChainInfo := mn.AppLayerObservations() + assert.Equal(t, tc.ExpectedAppLayerObservations, highestChainInfo) }) } - t.Run("HighestChainInfo returns cached value, If not able to acquire active node lock", func(t *testing.T) { - mn := newTestMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - }) - mn.highestChainInfo = ChainInfo{ - BlockNumber: 10, - FinalizedBlockNumber: 20, - TotalDifficulty: big.NewInt(30), - } - mn.activeNode = newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - mn.activeMu.Lock() - defer mn.activeMu.Unlock() - highestChainInfo := mn.HighestChainInfo() - require.Equal(t, ChainInfo{ - BlockNumber: 10, - FinalizedBlockNumber: 20, - TotalDifficulty: big.NewInt(30), - }, highestChainInfo) - }) } func TestMultiNode_BatchCallContextAll(t *testing.T) { diff --git a/common/client/node.go b/common/client/node.go index 919260e18e3..fdee5ff54a6 100644 --- a/common/client/node.go +++ b/common/client/node.go @@ -67,16 +67,15 @@ type Node[ State() nodeState // StateAndLatest returns nodeState with the latest ChainInfo observed by Node during current lifecycle. StateAndLatest() (nodeState, ChainInfo) - // HighestChainInfo - returns highest ChainInfo ever observed by the Node - HighestChainInfo() ChainInfo + // AppLayerObservations - returns highest ChainInfo ever observed by underlying RPC excluding results of health check requests + AppLayerObservations() ChainInfo SetPoolChainInfoProvider(PoolChainInfoProvider) // Name is a unique identifier for this node. Name() string String() string RPC() RPC SubscribersCount() int32 - // UnsubscribeAllExceptAliveLoop - closes all subscriptions except the aliveLoop subscription and waits for all requests - // that might update HighestChainInfo to complete. + // UnsubscribeAllExceptAliveLoop - closes all subscriptions except the aliveLoop subscription UnsubscribeAllExceptAliveLoop() ConfiguredChainID() CHAIN_ID Order() int32 @@ -324,3 +323,9 @@ func (n *node[CHAIN_ID, HEAD, RPC]) disconnectAll() { func (n *node[CHAIN_ID, HEAD, RPC]) Order() int32 { return n.order } + +func (n *node[CHAIN_ID, HEAD, RPC]) newCtx() (context.Context, context.CancelFunc) { + ctx, cancel := n.stopCh.NewCtx() + ctx = CtxAddHealthCheckFlag(ctx) + return ctx, cancel +} diff --git a/common/client/node_fsm.go b/common/client/node_fsm.go index ba343f40aef..014bfa94259 100644 --- a/common/client/node_fsm.go +++ b/common/client/node_fsm.go @@ -148,7 +148,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) isFinalizedBlockOutOfSync() bool { return false } - highestObservedByCaller := n.poolInfoProvider.HighestChainInfo() + highestObservedByCaller := n.poolInfoProvider.AppLayerObservations() latest, _ := n.rpc.GetInterceptedChainInfo() if n.chainCfg.FinalityTagEnabled() { return latest.FinalizedBlockNumber < highestObservedByCaller.FinalizedBlockNumber-int64(n.chainCfg.FinalizedBlockOffset()) @@ -165,10 +165,10 @@ func (n *node[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, ChainInfo) { return n.recalculateState(), latest } -// HighestChainInfo - returns highest ChainInfo ever observed by the Node -func (n *node[CHAIN_ID, HEAD, RPC]) HighestChainInfo() ChainInfo { - _, highest := n.rpc.GetInterceptedChainInfo() - return highest +// AppLayerObservations - returns highest ChainInfo ever observed by external user of the Node +func (n *node[CHAIN_ID, HEAD, RPC]) AppLayerObservations() ChainInfo { + _, appLayerObservations := n.rpc.GetInterceptedChainInfo() + return appLayerObservations } func (n *node[CHAIN_ID, HEAD, RPC]) SetPoolChainInfoProvider(poolInfoProvider PoolChainInfoProvider) { n.poolInfoProvider = poolInfoProvider diff --git a/common/client/node_lifecycle.go b/common/client/node_lifecycle.go index 13f5de6ed80..39e17bb4972 100644 --- a/common/client/node_lifecycle.go +++ b/common/client/node_lifecycle.go @@ -70,7 +70,7 @@ const ( // Should only be run ONCE per node, after a successful Dial func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { defer n.wg.Done() - ctx, cancel := n.stopCh.NewCtx() + ctx, cancel := n.newCtx() defer cancel() { @@ -295,7 +295,7 @@ const ( // 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(isOutOfSync func(num int64, td *big.Int) bool) { defer n.wg.Done() - ctx, cancel := n.stopCh.NewCtx() + ctx, cancel := n.newCtx() defer cancel() { @@ -368,7 +368,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(isOutOfSync func(num int64, td func (n *node[CHAIN_ID, HEAD, RPC]) unreachableLoop() { defer n.wg.Done() - ctx, cancel := n.stopCh.NewCtx() + ctx, cancel := n.newCtx() defer cancel() { @@ -423,7 +423,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) unreachableLoop() { func (n *node[CHAIN_ID, HEAD, RPC]) invalidChainIDLoop() { defer n.wg.Done() - ctx, cancel := n.stopCh.NewCtx() + ctx, cancel := n.newCtx() defer cancel() { @@ -475,7 +475,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) invalidChainIDLoop() { func (n *node[CHAIN_ID, HEAD, RPC]) syncingLoop() { defer n.wg.Done() - ctx, cancel := n.stopCh.NewCtx() + ctx, cancel := n.newCtx() defer cancel() { diff --git a/common/client/node_lifecycle_test.go b/common/client/node_lifecycle_test.go index ef624f47bce..177f66bc792 100644 --- a/common/client/node_lifecycle_test.go +++ b/common/client/node_lifecycle_test.go @@ -1669,7 +1669,7 @@ func TestNode_State(t *testing.T) { rpc: rpc, }) poolInfo := newMockPoolChainInfoProvider(t) - poolInfo.On("HighestChainInfo").Return(tc.PoolChainInfo).Once() + poolInfo.On("AppLayerObservations").Return(tc.PoolChainInfo).Once() node.SetPoolChainInfoProvider(poolInfo) node.setState(nodeStateAlive) assert.Equal(t, tc.ExpectedState, node.State()) diff --git a/common/client/types.go b/common/client/types.go index 56a56a07c6e..3e630b93dc0 100644 --- a/common/client/types.go +++ b/common/client/types.go @@ -168,8 +168,8 @@ type PoolChainInfoProvider interface { // Return highest latest ChainInfo within the alive nodes. E.g. most recent block number and highest block number // observed by Node A are 10 and 15; Node B - 12 and 14. This method will return 12. LatestChainInfo() (int, ChainInfo) - // HighestChainInfo - returns highest ChainInfo ever observed by any node in the pool. - HighestChainInfo() ChainInfo + // AppLayerObservations - returns highest ChainInfo ever observed by any user of MultiNode. + AppLayerObservations() ChainInfo } // ChainInfo - defines RPC's or MultiNode's view on the chain diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index f0b9697ae13..4848cfdfe10 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -101,7 +101,7 @@ type RPCClient interface { 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, highest commonclient.ChainInfo) + GetInterceptedChainInfo() (latest, appLayerObservations commonclient.ChainInfo) } type rawclient struct { @@ -126,10 +126,6 @@ type rpcClient struct { // close the underlying subscription subs []ethereum.Subscription - // Need to wait for completion of inflight requests that report highestChainInfo to ensure that - // on completion of DisconnectAll highestChainInfo contains all the info collected in current life cycle of the RPC - wgChainInfoInFlightRequest *sync.WaitGroup - // Need to track the aliveLoop subscription, so we do not cancel it when checking lease on the MultiNode aliveLoopSub ethereum.Subscription @@ -138,8 +134,8 @@ type rpcClient struct { // stateMu since it can happen on state transitions as well as rpcClient Close. chStopInFlight chan struct{} - // intercepted values seen by callers of the rpcClient. Need to ensure MultiNode provides repeatable read guarantee - highestChainInfo commonclient.ChainInfo + // intercepted values seen by callers of the rpcClient excluding health check calls. Need to ensure MultiNode provides repeatable read guarantee + appLayerObservations commonclient.ChainInfo // most recent chain info observed during current lifecycle (reseted on DisconnectAll) latestChainInfo commonclient.ChainInfo } @@ -164,7 +160,6 @@ func NewRPCClient( r.http = &rawclient{uri: *httpuri} } r.chStopInFlight = make(chan struct{}) - r.wgChainInfoInFlightRequest = &sync.WaitGroup{} lggr = logger.Named(lggr, "Client") lggr = logger.With(lggr, "clientTier", tier.String(), @@ -233,7 +228,15 @@ func (r *rpcClient) DialHTTP() error { } func (r *rpcClient) Close() { - r.DisconnectAll() + defer func() { + if r.ws.rpc != nil { + r.ws.rpc.Close() + } + }() + + r.stateMu.Lock() + defer r.stateMu.Unlock() + r.cancelInflightRequests() } // cancelInflightRequests closes and replaces the chStopInFlight @@ -311,17 +314,13 @@ func (r *rpcClient) registerSub(sub ethereum.Subscription, stopInFLightCh chan s // DisconnectAll disconnects all clients connected to the rpcClient func (r *rpcClient) DisconnectAll() { r.stateMu.Lock() + defer r.stateMu.Unlock() if r.ws.rpc != nil { r.ws.rpc.Close() } r.cancelInflightRequests() r.unsubscribeAll() r.latestChainInfo = commonclient.ChainInfo{} - wg := r.wgChainInfoInFlightRequest - r.wgChainInfoInFlightRequest = &sync.WaitGroup{} - r.stateMu.Unlock() - // wait for all requests to complete to capture all observed chainInfo - wg.Wait() } // unsubscribeAll unsubscribes all subscriptions @@ -348,20 +347,16 @@ func (r *rpcClient) SubscribersCount() int32 { } // UnsubscribeAllExceptAliveLoop disconnects all subscriptions to the node except the alive loop subscription -// while holding the n.stateMu lock and waits for +// 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() } } - wg := r.wgChainInfoInFlightRequest - r.wgChainInfoInFlightRequest = &sync.WaitGroup{} - r.stateMu.Unlock() - // wait for all requests to complete to capture all observed chainInfo - wg.Wait() } // RPC wrappers @@ -411,9 +406,7 @@ func (r *rpcClient) BatchCallContext(ctx context.Context, b []rpc.BatchElem) err } func (r *rpcClient) SubscribeNewHead(ctx context.Context, channel chan<- *evmtypes.Head) (_ commontypes.Subscription, err error) { - // ensure that DisconnectAll waits for completion of this request to collect all values observed during this life cycle - ctx, cancel, chStopInFlight, wg, ws, _ := r.acquireQueryCtx(ctx, 1) - defer wg.Done() + ctx, cancel, chStopInFlight, ws, _ := r.acquireQueryCtx(ctx) defer cancel() args := []interface{}{"newHeads"} lggr := r.newRqLggr().With("args", args) @@ -427,7 +420,7 @@ func (r *rpcClient) SubscribeNewHead(ctx context.Context, channel chan<- *evmtyp }() subForwarder := newSubForwarder(channel, func(head *evmtypes.Head) *evmtypes.Head { head.EVMChainID = ubig.New(r.chainID) - r.oneNewHead(chStopInFlight, head) + r.oneNewHead(ctx, chStopInFlight, head) return head }, r.wrapRPCClientError) err = subForwarder.start(ws.rpc.EthSubscribe(ctx, subForwarder.srcCh, args...)) @@ -558,10 +551,8 @@ func (r *rpcClient) BlockByNumber(ctx context.Context, number *big.Int) (head *e } func (r *rpcClient) blockByNumber(ctx context.Context, number string) (head *evmtypes.Head, err error) { - // ensure that DisconnectAll waits for completion of this request to collect all values observed during this life cycle - ctx, cancel, chStopInFlight, wg, ws, http := r.acquireQueryCtx(ctx, 1) + ctx, cancel, chStopInFlight, ws, http := r.acquireQueryCtx(ctx) defer cancel() - defer wg.Done() const method = "eth_getBlockByNumber" args := []interface{}{number, false} lggr := r.newRqLggr().With( @@ -590,9 +581,9 @@ func (r *rpcClient) blockByNumber(ctx context.Context, number string) (head *evm switch number { case rpc.FinalizedBlockNumber.String(): - r.oneNewFinalizedHead(chStopInFlight, head) + r.oneNewFinalizedHead(ctx, chStopInFlight, head) case rpc.LatestBlockNumber.String(): - r.oneNewHead(chStopInFlight, head) + r.oneNewHead(ctx, chStopInFlight, head) } return @@ -1022,7 +1013,7 @@ func (r *rpcClient) ClientVersion(ctx context.Context) (version string, err erro } 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, 0) + ctx, cancel, chStopInFlight, ws, _ := r.acquireQueryCtx(ctx) defer cancel() lggr := r.newRqLggr().With("q", q) @@ -1129,20 +1120,16 @@ func (r *rpcClient) wrapHTTP(err error) error { // makeLiveQueryCtxAndSafeGetClients wraps makeQueryCtx func (r *rpcClient) makeLiveQueryCtxAndSafeGetClients(parentCtx context.Context) (ctx context.Context, cancel context.CancelFunc, ws rawclient, http *rawclient) { - ctx, cancel, _, _, ws, http = r.acquireQueryCtx(parentCtx, 0) + ctx, cancel, _, ws, http = r.acquireQueryCtx(parentCtx) return } -func (r *rpcClient) acquireQueryCtx(parentCtx context.Context, wgDelta uint16) (ctx context.Context, cancel context.CancelFunc, - chStopInFlight chan struct{}, wg *sync.WaitGroup, ws rawclient, http *rawclient) { +func (r *rpcClient) acquireQueryCtx(parentCtx context.Context) (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 r.stateMu.RLock() chStopInFlight = r.chStopInFlight - wg = r.wgChainInfoInFlightRequest - if wgDelta != 0 { - wg.Add(int(wgDelta)) - } ws = r.ws if r.http != nil { cp := *r.http @@ -1213,15 +1200,17 @@ func Name(r *rpcClient) string { return r.name } -func (r *rpcClient) oneNewHead(requestCh <-chan struct{}, head *evmtypes.Head) { +func (r *rpcClient) oneNewHead(ctx context.Context, requestCh <-chan struct{}, head *evmtypes.Head) { if head == nil { return } r.stateMu.Lock() defer r.stateMu.Unlock() - r.highestChainInfo.BlockNumber = max(r.highestChainInfo.BlockNumber, head.Number) - r.highestChainInfo.SetTotalDifficultyIfGt(head.TotalDifficulty) + if !commonclient.CtxIsHeathCheckRequest(ctx) { + r.appLayerObservations.BlockNumber = max(r.appLayerObservations.BlockNumber, head.Number) + r.appLayerObservations.SetTotalDifficultyIfGt(head.TotalDifficulty) + } select { case <-requestCh: // no need to update latestChainInfo, as rpcClient already started new life cycle return @@ -1231,13 +1220,15 @@ func (r *rpcClient) oneNewHead(requestCh <-chan struct{}, head *evmtypes.Head) { } } -func (r *rpcClient) oneNewFinalizedHead(requestCh <-chan struct{}, head *evmtypes.Head) { +func (r *rpcClient) oneNewFinalizedHead(ctx context.Context, requestCh <-chan struct{}, head *evmtypes.Head) { if head == nil { return } r.stateMu.Lock() defer r.stateMu.Unlock() - r.highestChainInfo.FinalizedBlockNumber = max(r.highestChainInfo.FinalizedBlockNumber, head.Number) + if !commonclient.CtxIsHeathCheckRequest(ctx) { + r.appLayerObservations.FinalizedBlockNumber = max(r.appLayerObservations.FinalizedBlockNumber, head.Number) + } select { case <-requestCh: // no need to update latestChainInfo, as rpcClient already started new life cycle return @@ -1246,10 +1237,10 @@ func (r *rpcClient) oneNewFinalizedHead(requestCh <-chan struct{}, head *evmtype } } -func (r *rpcClient) GetInterceptedChainInfo() (latest, highest commonclient.ChainInfo) { +func (r *rpcClient) GetInterceptedChainInfo() (latest, appLayerObservations commonclient.ChainInfo) { r.stateMu.RLock() defer r.stateMu.RUnlock() - return r.latestChainInfo, r.highestChainInfo + return r.latestChainInfo, r.appLayerObservations } func ToBlockNumArg(number *big.Int) string { diff --git a/core/chains/evm/client/rpc_client_test.go b/core/chains/evm/client/rpc_client_test.go index d043f21a06c..fbe2336c7d1 100644 --- a/core/chains/evm/client/rpc_client_test.go +++ b/core/chains/evm/client/rpc_client_test.go @@ -60,13 +60,13 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) // set to default values - latest, highest := rpc.GetInterceptedChainInfo() + latest, appLayerObservations := rpc.GetInterceptedChainInfo() assert.Equal(t, int64(0), latest.BlockNumber) assert.Equal(t, int64(0), latest.FinalizedBlockNumber) assert.Nil(t, latest.TotalDifficulty) - assert.Equal(t, int64(0), highest.BlockNumber) - assert.Equal(t, int64(0), highest.FinalizedBlockNumber) - assert.Nil(t, highest.TotalDifficulty) + assert.Equal(t, int64(0), appLayerObservations.BlockNumber) + assert.Equal(t, int64(0), appLayerObservations.FinalizedBlockNumber) + assert.Nil(t, appLayerObservations.TotalDifficulty) ch := make(chan *evmtypes.Head) sub, err := rpc.SubscribeNewHead(tests.Context(t), ch) @@ -79,28 +79,52 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { // received 128 head <-ch - latest, highest = rpc.GetInterceptedChainInfo() + latest, appLayerObservations = rpc.GetInterceptedChainInfo() assert.Equal(t, int64(128), latest.BlockNumber) assert.Equal(t, int64(0), latest.FinalizedBlockNumber) assert.Equal(t, big.NewInt(500), latest.TotalDifficulty) - assertHighest := func(highest commonclient.ChainInfo) { - assert.Equal(t, int64(256), highest.BlockNumber) - assert.Equal(t, int64(0), highest.FinalizedBlockNumber) - assert.Equal(t, big.NewInt(1000), highest.TotalDifficulty) + assertAppLayerObservations := func(appLayerObservations commonclient.ChainInfo) { + assert.Equal(t, int64(256), appLayerObservations.BlockNumber) + assert.Equal(t, int64(0), appLayerObservations.FinalizedBlockNumber) + assert.Equal(t, big.NewInt(1000), appLayerObservations.TotalDifficulty) } - assertHighest(highest) + assertAppLayerObservations(appLayerObservations) // DisconnectAll resets latest rpc.DisconnectAll() - latest, highest = rpc.GetInterceptedChainInfo() + latest, appLayerObservations = rpc.GetInterceptedChainInfo() assert.Equal(t, int64(0), latest.BlockNumber) assert.Equal(t, int64(0), latest.FinalizedBlockNumber) assert.Nil(t, latest.TotalDifficulty) - assertHighest(highest) + assertAppLayerObservations(appLayerObservations) + }) + t.Run("App layer observations are not affected by new block if health check flag is present", func(t *testing.T) { + server := testutils.NewWSServer(t, chainId, serverCallBack) + wsURL := server.WSURL() + + rpc := client.NewRPCClient(lggr, *wsURL, nil, "rpc", 1, chainId, commonclient.Primary) + defer rpc.Close() + require.NoError(t, rpc.Dial(ctx)) + ch := make(chan *evmtypes.Head) + sub, err := rpc.SubscribeNewHead(commonclient.CtxAddHealthCheckFlag(tests.Context(t)), ch) + require.NoError(t, err) + defer sub.Unsubscribe() + go server.MustWriteBinaryMessageSync(t, makeNewHeadWSMessage(&evmtypes.Head{Number: 256, TotalDifficulty: big.NewInt(1000)})) + // received 256 head + <-ch + + latest, appLayerObservations := rpc.GetInterceptedChainInfo() + assert.Equal(t, int64(256), latest.BlockNumber) + assert.Equal(t, int64(0), latest.FinalizedBlockNumber) + assert.Equal(t, big.NewInt(1000), latest.TotalDifficulty) + + assert.Equal(t, int64(0), appLayerObservations.BlockNumber) + assert.Equal(t, int64(0), appLayerObservations.FinalizedBlockNumber) + assert.Equal(t, (*big.Int)(nil), appLayerObservations.TotalDifficulty) }) t.Run("Block's chain ID matched configured", func(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) @@ -233,31 +257,43 @@ func TestRPCClient_LatestFinalizedBlock(t *testing.T) { // updates chain info _, err := rpc.LatestFinalizedBlock(ctx) require.NoError(t, err) - latest, highest := rpc.GetInterceptedChainInfo() + latest, appLayerObservations := rpc.GetInterceptedChainInfo() - assert.Equal(t, int64(0), highest.BlockNumber) - assert.Equal(t, int64(128), highest.FinalizedBlockNumber) + assert.Equal(t, int64(0), appLayerObservations.BlockNumber) + assert.Equal(t, int64(128), appLayerObservations.FinalizedBlockNumber) assert.Equal(t, int64(0), latest.BlockNumber) assert.Equal(t, int64(128), latest.FinalizedBlockNumber) - // lower block number does not update Highest + // lower block number does not update appLayerObservations server.Head = &evmtypes.Head{Number: 127} _, err = rpc.LatestFinalizedBlock(ctx) require.NoError(t, err) - latest, highest = rpc.GetInterceptedChainInfo() + latest, appLayerObservations = rpc.GetInterceptedChainInfo() - assert.Equal(t, int64(0), highest.BlockNumber) - assert.Equal(t, int64(128), highest.FinalizedBlockNumber) + assert.Equal(t, int64(0), appLayerObservations.BlockNumber) + assert.Equal(t, int64(128), appLayerObservations.FinalizedBlockNumber) assert.Equal(t, int64(0), latest.BlockNumber) assert.Equal(t, int64(127), latest.FinalizedBlockNumber) + // health check flg prevents change in appLayerObservations + server.Head = &evmtypes.Head{Number: 256} + _, err = rpc.LatestFinalizedBlock(commonclient.CtxAddHealthCheckFlag(ctx)) + require.NoError(t, err) + latest, appLayerObservations = rpc.GetInterceptedChainInfo() + + assert.Equal(t, int64(0), appLayerObservations.BlockNumber) + assert.Equal(t, int64(128), appLayerObservations.FinalizedBlockNumber) + + assert.Equal(t, int64(0), latest.BlockNumber) + assert.Equal(t, int64(256), latest.FinalizedBlockNumber) + // DisconnectAll resets latest ChainInfo rpc.DisconnectAll() - latest, highest = rpc.GetInterceptedChainInfo() - assert.Equal(t, int64(0), highest.BlockNumber) - assert.Equal(t, int64(128), highest.FinalizedBlockNumber) + latest, appLayerObservations = rpc.GetInterceptedChainInfo() + assert.Equal(t, int64(0), appLayerObservations.BlockNumber) + assert.Equal(t, int64(128), appLayerObservations.FinalizedBlockNumber) assert.Equal(t, int64(0), latest.BlockNumber) assert.Equal(t, int64(0), latest.FinalizedBlockNumber) diff --git a/core/gethwrappers/keystone/generated/forwarder/forwarder.go b/core/gethwrappers/keystone/generated/forwarder/forwarder.go index 951ef047364..fbdd024c64f 100644 --- a/core/gethwrappers/keystone/generated/forwarder/forwarder.go +++ b/core/gethwrappers/keystone/generated/forwarder/forwarder.go @@ -31,8 +31,8 @@ var ( ) var KeystoneForwarderMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"DuplicateSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxSigners\",\"type\":\"uint256\"}],\"name\":\"ExcessSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FaultToleranceMustBePositive\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minSigners\",\"type\":\"uint256\"}],\"name\":\"InsufficientSigners\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"InvalidDonId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"InvalidSignature\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"received\",\"type\":\"uint256\"}],\"name\":\"InvalidSignatureCount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrantCall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"reportId\",\"type\":\"bytes32\"}],\"name\":\"ReportAlreadyProcessed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"workflowOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"result\",\"type\":\"bool\"}],\"name\":\"ReportProcessed\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"}],\"name\":\"getTransmitter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiverAddress\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"rawReport\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"reportContext\",\"type\":\"bytes\"},{\"internalType\":\"bytes[]\",\"name\":\"signatures\",\"type\":\"bytes[]\"}],\"name\":\"report\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", - Bin: "0x608060405234801561001057600080fd5b5033806000816100675760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615610097576100978161009f565b505050610148565b336001600160a01b038216036100f75760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161005e565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b61152c806101576000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c8063390d0b151161005b578063390d0b15146100f257806379ba50971461012a5780638da5cb5b14610132578063f2fde38b1461015057600080fd5b80631128956514610082578063134a46f014610097578063181f5a77146100aa575b600080fd5b610095610090366004611128565b610163565b005b6100956100a53660046111d3565b6108b5565b604080518082018252601781527f4b657973746f6e65466f7277617264657220312e302e30000000000000000000602082015290516100e991906112ab565b60405180910390f35b6101056101003660046112c5565b610c16565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100e9565b610095610ca7565b60005473ffffffffffffffffffffffffffffffffffffffff16610105565b61009561015e3660046112ef565b610da4565b60015474010000000000000000000000000000000000000000900460ff16156101b8576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001790556058851015610232576040517fb55ac75400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000806000806102778a8a8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610db892505050565b63ffffffff8316600090815260026020526040812054949850929650909450925060ff90911690036102e2576040517fea1b312900000000000000000000000000000000000000000000000000000000815263ffffffff841660048201526024015b60405180910390fd5b604080517fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060608e901b1660208083019190915260348083018690528351808403909101815260549092018352815191810191909120600081815260039092529190205473ffffffffffffffffffffffffffffffffffffffff1615610396576040517f1aac3d29000000000000000000000000000000000000000000000000000000008152600481018290526024016102d9565b63ffffffff841660009081526002602052604090205486906103bc9060ff166001611339565b60ff16146104275763ffffffff84166000908152600260205260409020546103e89060ff166001611339565b6040517fd6022e8e00000000000000000000000000000000000000000000000000000000815260ff9091166004820152602481018790526044016102d9565b60008b8b604051610439929190611352565b604051908190038120610452918c908c90602001611362565b604051602081830303815290604052805190602001209050610472610fb5565b6000805b898110156106e05760008060006104e48e8e868181106104985761049861137c565b90506020028101906104aa91906113ab565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610ddd92505050565b9194509250905060006001886104fb84601b611339565b6040805160008152602081018083529390935260ff90911690820152606081018690526080810185905260a0016020604051602081039080840390855afa15801561054a573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015163ffffffff8f1660009081526002602081815284832073ffffffffffffffffffffffffffffffffffffffff851684529091019052918220549850925060ff881690039050610605576040517fbf18af4300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016102d9565b610610600187611410565b955060008760ff8816601f81106106295761062961137c565b602002015173ffffffffffffffffffffffffffffffffffffffff1614610693576040517fe021c4f200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016102d9565b80878760ff16601f81106106a9576106a961137c565b73ffffffffffffffffffffffffffffffffffffffff9092166020929092020152506106d992508391506114299050565b9050610476565b5050505060008c73ffffffffffffffffffffffffffffffffffffffff1663ff5a027087858f8f605890809261071793929190611461565b6040518563ffffffff1660e01b8152600401610736949392919061148b565b600060405180830381600087803b15801561075057600080fd5b505af1925050508015610761575060015b1561076a575060015b60405180604001604052803373ffffffffffffffffffffffffffffffffffffffff1681526020018215158152506003600084815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160000160146101000a81548160ff021916908315150217905550905050838373ffffffffffffffffffffffffffffffffffffffff168e73ffffffffffffffffffffffffffffffffffffffff167fdae8e752043eb5fc7e4a6eced57ceaf159548b630125ece9ffc41cfc952c208184604051610876911515815260200190565b60405180910390a45050600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1690555050505050505050505050565b6108bd610e3d565b8260ff166000036108fa576040517f0743bae600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601f81111561093f576040517f61750f4000000000000000000000000000000000000000000000000000000000815260048101829052601f60248201526044016102d9565b61094a8360036114fc565b60ff1681116109a8578061095f8460036114fc565b61096a906001611339565b6040517f9dd9e6d8000000000000000000000000000000000000000000000000000000008152600481019290925260ff1660248201526044016102d9565b60005b63ffffffff8516600090815260026020526040902060010154811015610a495763ffffffff851660009081526002602052604081206001018054839081106109f5576109f561137c565b600091825260208083209091015463ffffffff891683526002808352604080852073ffffffffffffffffffffffffffffffffffffffff9093168552910190915281205550610a4281611429565b90506109ab565b5063ffffffff84166000908152600260205260409020610a6d906001018383610fd4565b5060005b81811015610bcb576000838383818110610a8d57610a8d61137c565b9050602002016020810190610aa291906112ef565b63ffffffff8716600090815260026020818152604080842073ffffffffffffffffffffffffffffffffffffffff86168552909201905290205490915015610b2d576040517fe021c4f200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016102d9565b610b38826001611339565b63ffffffff8716600090815260026020818152604080842073ffffffffffffffffffffffffffffffffffffffff909616808552868401835290842060ff959095169094559081526001938401805494850181558252902090910180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169091179055610bc481611429565b9050610a71565b50505063ffffffff91909116600090815260026020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055565b600080610c7884846040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084901b1660208201526034810182905260009060540160405160208183030381529060405280519060200120905092915050565b60009081526003602052604090205473ffffffffffffffffffffffffffffffffffffffff169150505b92915050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610d28576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016102d9565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610dac610e3d565b610db581610ec0565b50565b602081015160408201516044830151606490930151919360e09190911c929160601c90565b60008060006041845114610e1f57836040517f2adfdc300000000000000000000000000000000000000000000000000000000081526004016102d991906112ab565b50505060208101516040820151606090920151909260009190911a90565b60005473ffffffffffffffffffffffffffffffffffffffff163314610ebe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016102d9565b565b3373ffffffffffffffffffffffffffffffffffffffff821603610f3f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016102d9565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b604051806103e00160405280601f906020820280368337509192915050565b82805482825590600052602060002090810192821561104c579160200282015b8281111561104c5781547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff843516178255602090920191600190910190610ff4565b5061105892915061105c565b5090565b5b80821115611058576000815560010161105d565b803573ffffffffffffffffffffffffffffffffffffffff8116811461109557600080fd5b919050565b60008083601f8401126110ac57600080fd5b50813567ffffffffffffffff8111156110c457600080fd5b6020830191508360208285010111156110dc57600080fd5b9250929050565b60008083601f8401126110f557600080fd5b50813567ffffffffffffffff81111561110d57600080fd5b6020830191508360208260051b85010111156110dc57600080fd5b60008060008060008060006080888a03121561114357600080fd5b61114c88611071565b9650602088013567ffffffffffffffff8082111561116957600080fd5b6111758b838c0161109a565b909850965060408a013591508082111561118e57600080fd5b61119a8b838c0161109a565b909650945060608a01359150808211156111b357600080fd5b506111c08a828b016110e3565b989b979a50959850939692959293505050565b600080600080606085870312156111e957600080fd5b843563ffffffff811681146111fd57600080fd5b9350602085013560ff8116811461121357600080fd5b9250604085013567ffffffffffffffff81111561122f57600080fd5b61123b878288016110e3565b95989497509550505050565b6000815180845260005b8181101561126d57602081850181015186830182015201611251565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815260006112be6020830184611247565b9392505050565b600080604083850312156112d857600080fd5b6112e183611071565b946020939093013593505050565b60006020828403121561130157600080fd5b6112be82611071565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60ff8181168382160190811115610ca157610ca161130a565b8183823760009101908152919050565b838152818360208301376000910160200190815292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126113e057600080fd5b83018035915067ffffffffffffffff8211156113fb57600080fd5b6020019150368190038213156110dc57600080fd5b60ff8281168282160390811115610ca157610ca161130a565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361145a5761145a61130a565b5060010190565b6000808585111561147157600080fd5b8386111561147e57600080fd5b5050820193919092039150565b84815273ffffffffffffffffffffffffffffffffffffffff8416602082015260606040820152816060820152818360808301376000818301608090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01601019392505050565b60ff81811683821602908116908181146115185761151861130a565b509291505056fea164736f6c6343000813000a", + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"DuplicateSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxSigners\",\"type\":\"uint256\"}],\"name\":\"ExcessSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FaultToleranceMustBePositive\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minSigners\",\"type\":\"uint256\"}],\"name\":\"InsufficientSigners\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"InvalidDonId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"InvalidSignature\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"received\",\"type\":\"uint256\"}],\"name\":\"InvalidSignatureCount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrantCall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"reportId\",\"type\":\"bytes32\"}],\"name\":\"ReportAlreadyProcessed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"workflowOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"result\",\"type\":\"bool\"}],\"name\":\"ReportProcessed\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"}],\"name\":\"getTransmitter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiverAddress\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"rawReport\",\"type\":\"bytes\"},{\"internalType\":\"bytes[]\",\"name\":\"signatures\",\"type\":\"bytes[]\"}],\"name\":\"report\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b5033806000816100675760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615610097576100978161009f565b505050610148565b336001600160a01b038216036100f75760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161005e565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b61151d806101576000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c806379ba50971161005b57806379ba5097146101175780638da5cb5b1461011f578063c0965dc31461013d578063f2fde38b1461015057600080fd5b8063134a46f014610082578063181f5a7714610097578063390d0b15146100df575b600080fd5b610095610090366004611106565b610163565b005b604080518082018252601781527f4b657973746f6e65466f7277617264657220312e302e30000000000000000000602082015290516100d691906111de565b60405180910390f35b6100f26100ed366004611221565b61057d565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100d6565b61009561060e565b60005473ffffffffffffffffffffffffffffffffffffffff166100f2565b61009561014b36600461124b565b61070b565b61009561015e3660046112fa565b610ded565b60015474010000000000000000000000000000000000000000900460ff16156101b8576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000017905560ff8316600003610234576040517f0743bae600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601f81111561027e576040517f61750f4000000000000000000000000000000000000000000000000000000000815260048101829052601f60248201526044015b60405180910390fd5b610289836003611344565b60ff1681116102e7578061029e846003611344565b6102a9906001611367565b6040517f9dd9e6d8000000000000000000000000000000000000000000000000000000008152600481019290925260ff166024820152604401610275565b60005b63ffffffff85166000908152600260205260409020600101548110156103885763ffffffff8516600090815260026020526040812060010180548390811061033457610334611380565b600091825260208083209091015463ffffffff891683526002808352604080852073ffffffffffffffffffffffffffffffffffffffff9093168552910190915281205550610381816113af565b90506102ea565b5063ffffffff841660009081526002602052604090206103ac906001018383610ffe565b5060005b8181101561050a5760008383838181106103cc576103cc611380565b90506020020160208101906103e191906112fa565b63ffffffff8716600090815260026020818152604080842073ffffffffffffffffffffffffffffffffffffffff8616855290920190529020549091501561046c576040517fe021c4f200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610275565b610477826001611367565b63ffffffff8716600090815260026020818152604080842073ffffffffffffffffffffffffffffffffffffffff909616808552868401835290842060ff959095169094559081526001938401805494850181558252902090910180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169091179055610503816113af565b90506103b0565b50505063ffffffff91909116600090815260026020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff169055565b6000806105df84846040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084901b1660208201526034810182905260009060540160405160208183030381529060405280519060200120905092915050565b60009081526003602052604090205473ffffffffffffffffffffffffffffffffffffffff169150505b92915050565b60015473ffffffffffffffffffffffffffffffffffffffff16331461068f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610275565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b60015474010000000000000000000000000000000000000000900460ff1615610760576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000017905560588310156107da576040517fb55ac75400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060008061081f88888080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610e0192505050565b63ffffffff8316600090815260026020526040812054949850929650909450925060ff9091169003610885576040517fea1b312900000000000000000000000000000000000000000000000000000000815263ffffffff84166004820152602401610275565b604080517fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060608c901b1660208083019190915260348083018690528351808403909101815260549092018352815191810191909120600081815260039092529190205473ffffffffffffffffffffffffffffffffffffffff1615610939576040517f1aac3d2900000000000000000000000000000000000000000000000000000000815260048101829052602401610275565b63ffffffff8416600090815260026020526040902054869061095f9060ff166001611367565b60ff16146109ca5763ffffffff841660009081526002602052604090205461098b9060ff166001611367565b6040517fd6022e8e00000000000000000000000000000000000000000000000000000000815260ff909116600482015260248101879052604401610275565b600089896040516109dc9291906113e7565b604051809103902090506109ee611086565b6000805b89811015610c5c576000806000610a608e8e86818110610a1457610a14611380565b9050602002810190610a2691906113f7565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610e2692505050565b925092509250600060018883868660405160008152602001604052604051610aa4949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa158015610ac6573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015163ffffffff8f1660009081526002602081815284832073ffffffffffffffffffffffffffffffffffffffff851684529091019052918220549850925060ff881690039050610b81576040517fbf18af4300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610275565b610b8c60018761145c565b955060008760ff8816601f8110610ba557610ba5611380565b602002015173ffffffffffffffffffffffffffffffffffffffff1614610c0f576040517fe021c4f200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610275565b80878760ff16601f8110610c2557610c25611380565b73ffffffffffffffffffffffffffffffffffffffff909216602092909202015250610c5592508391506113af9050565b90506109f2565b5050505060008a905060008173ffffffffffffffffffffffffffffffffffffffff1663ff5a027088868e8e6058908092610c9893929190611475565b6040518563ffffffff1660e01b8152600401610cb7949392919061149f565b600060405180830381600087803b158015610cd157600080fd5b505af1925050508015610ce2575060015b15610ceb575060015b604080518082018252338152821515602080830191825260008781526003909152839020915182549151151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00000000000000000000000000000000000000000090921673ffffffffffffffffffffffffffffffffffffffff91821617919091179091559051869186811691908f16907fdae8e752043eb5fc7e4a6eced57ceaf159548b630125ece9ffc41cfc952c208190610daf90861515815260200190565b60405180910390a45050600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16905550505050505050505050565b610df5610e86565b610dfe81610f09565b50565b602081015160408201516044830151606490930151919360e09190911c929160601c90565b60008060006041845114610e6857836040517f2adfdc3000000000000000000000000000000000000000000000000000000000815260040161027591906111de565b50505060208101516040820151606090920151909260009190911a90565b60005473ffffffffffffffffffffffffffffffffffffffff163314610f07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610275565b565b3373ffffffffffffffffffffffffffffffffffffffff821603610f88576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610275565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b828054828255906000526020600020908101928215611076579160200282015b828111156110765781547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84351617825560209092019160019091019061101e565b506110829291506110a5565b5090565b604051806103e00160405280601f906020820280368337509192915050565b5b8082111561108257600081556001016110a6565b60008083601f8401126110cc57600080fd5b50813567ffffffffffffffff8111156110e457600080fd5b6020830191508360208260051b85010111156110ff57600080fd5b9250929050565b6000806000806060858703121561111c57600080fd5b843563ffffffff8116811461113057600080fd5b9350602085013560ff8116811461114657600080fd5b9250604085013567ffffffffffffffff81111561116257600080fd5b61116e878288016110ba565b95989497509550505050565b6000815180845260005b818110156111a057602081850181015186830182015201611184565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815260006111f1602083018461117a565b9392505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461121c57600080fd5b919050565b6000806040838503121561123457600080fd5b61123d836111f8565b946020939093013593505050565b60008060008060006060868803121561126357600080fd5b61126c866111f8565b9450602086013567ffffffffffffffff8082111561128957600080fd5b818801915088601f83011261129d57600080fd5b8135818111156112ac57600080fd5b8960208285010111156112be57600080fd5b6020830196508095505060408801359150808211156112dc57600080fd5b506112e9888289016110ba565b969995985093965092949392505050565b60006020828403121561130c57600080fd5b6111f1826111f8565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60ff818116838216029081169081811461136057611360611315565b5092915050565b60ff818116838216019081111561060857610608611315565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036113e0576113e0611315565b5060010190565b8183823760009101908152919050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261142c57600080fd5b83018035915067ffffffffffffffff82111561144757600080fd5b6020019150368190038213156110ff57600080fd5b60ff828116828216039081111561060857610608611315565b6000808585111561148557600080fd5b8386111561149257600080fd5b5050820193919092039150565b84815273ffffffffffffffffffffffffffffffffffffffff8416602082015260606040820152816060820152818360808301376000818301608090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160101939250505056fea164736f6c6343000813000a", } var KeystoneForwarderABI = KeystoneForwarderMetaData.ABI @@ -249,16 +249,16 @@ func (_KeystoneForwarder *KeystoneForwarderTransactorSession) AcceptOwnership() return _KeystoneForwarder.Contract.AcceptOwnership(&_KeystoneForwarder.TransactOpts) } -func (_KeystoneForwarder *KeystoneForwarderTransactor) Report(opts *bind.TransactOpts, receiverAddress common.Address, rawReport []byte, reportContext []byte, signatures [][]byte) (*types.Transaction, error) { - return _KeystoneForwarder.contract.Transact(opts, "report", receiverAddress, rawReport, reportContext, signatures) +func (_KeystoneForwarder *KeystoneForwarderTransactor) Report(opts *bind.TransactOpts, receiverAddress common.Address, rawReport []byte, signatures [][]byte) (*types.Transaction, error) { + return _KeystoneForwarder.contract.Transact(opts, "report", receiverAddress, rawReport, signatures) } -func (_KeystoneForwarder *KeystoneForwarderSession) Report(receiverAddress common.Address, rawReport []byte, reportContext []byte, signatures [][]byte) (*types.Transaction, error) { - return _KeystoneForwarder.Contract.Report(&_KeystoneForwarder.TransactOpts, receiverAddress, rawReport, reportContext, signatures) +func (_KeystoneForwarder *KeystoneForwarderSession) Report(receiverAddress common.Address, rawReport []byte, signatures [][]byte) (*types.Transaction, error) { + return _KeystoneForwarder.Contract.Report(&_KeystoneForwarder.TransactOpts, receiverAddress, rawReport, signatures) } -func (_KeystoneForwarder *KeystoneForwarderTransactorSession) Report(receiverAddress common.Address, rawReport []byte, reportContext []byte, signatures [][]byte) (*types.Transaction, error) { - return _KeystoneForwarder.Contract.Report(&_KeystoneForwarder.TransactOpts, receiverAddress, rawReport, reportContext, signatures) +func (_KeystoneForwarder *KeystoneForwarderTransactorSession) Report(receiverAddress common.Address, rawReport []byte, signatures [][]byte) (*types.Transaction, error) { + return _KeystoneForwarder.Contract.Report(&_KeystoneForwarder.TransactOpts, receiverAddress, rawReport, signatures) } func (_KeystoneForwarder *KeystoneForwarderTransactor) SetConfig(opts *bind.TransactOpts, donId uint32, f uint8, signers []common.Address) (*types.Transaction, error) { @@ -742,7 +742,7 @@ type KeystoneForwarderInterface interface { AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) - Report(opts *bind.TransactOpts, receiverAddress common.Address, rawReport []byte, reportContext []byte, signatures [][]byte) (*types.Transaction, error) + Report(opts *bind.TransactOpts, receiverAddress common.Address, rawReport []byte, signatures [][]byte) (*types.Transaction, error) SetConfig(opts *bind.TransactOpts, donId uint32, f uint8, signers []common.Address) (*types.Transaction, error) diff --git a/core/gethwrappers/keystone/generated/keystone_capability_registry/keystone_capability_registry.go b/core/gethwrappers/keystone/generated/keystone_capability_registry/keystone_capability_registry.go index 1725189ced6..3357bf8f5fe 100644 --- a/core/gethwrappers/keystone/generated/keystone_capability_registry/keystone_capability_registry.go +++ b/core/gethwrappers/keystone/generated/keystone_capability_registry/keystone_capability_registry.go @@ -42,29 +42,21 @@ type CapabilityRegistryCapabilityConfiguration struct { Config []byte } -type CapabilityRegistryDONInfo struct { - Id uint32 - ConfigCount uint32 - IsPublic bool - NodeP2PIds [][32]byte - CapabilityConfigurations []CapabilityRegistryCapabilityConfiguration +type CapabilityRegistryNodeOperator struct { + Admin common.Address + Name string } -type CapabilityRegistryNodeInfo struct { +type CapabilityRegistryNodeParams struct { NodeOperatorId uint32 - Signer [32]byte + Signer common.Address P2pId [32]byte HashedCapabilityIds [][32]byte } -type CapabilityRegistryNodeOperator struct { - Admin common.Address - Name string -} - var CapabilityRegistryMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"name\":\"AccessForbidden\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CapabilityAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityIsDeprecated\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"DONDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONCapability\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONNode\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"proposedConfigurationContract\",\"type\":\"address\"}],\"name\":\"InvalidCapabilityConfigurationContractInterface\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"name\":\"InvalidNodeCapabilities\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeOperatorAdmin\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"InvalidNodeP2PId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"lengthOne\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lengthTwo\",\"type\":\"uint256\"}],\"name\":\"LengthMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"NodeDoesNotSupportCapability\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDeprecated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"}],\"name\":\"NodeAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"}],\"name\":\"NodeOperatorRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"NodeRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"}],\"name\":\"NodeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability\",\"name\":\"capability\",\"type\":\"tuple\"}],\"name\":\"addCapability\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"nodes\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"}],\"name\":\"addDON\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"addNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeInfo[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"addNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"deprecateCapability\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCapabilities\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedId\",\"type\":\"bytes32\"}],\"name\":\"getCapability\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"getDON\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"id\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodeP2PIds\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"}],\"internalType\":\"structCapabilityRegistry.DONInfo\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"getDONCapabilityConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDONs\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"id\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodeP2PIds\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"}],\"internalType\":\"structCapabilityRegistry.DONInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"}],\"name\":\"getHashedCapabilityId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"getNode\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeInfo\",\"name\":\"\",\"type\":\"tuple\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"}],\"name\":\"getNodeOperator\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getNodeOperators\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getNodes\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"},{\"internalType\":\"uint32[]\",\"name\":\"\",\"type\":\"uint32[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"isCapabilityDeprecated\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32[]\",\"name\":\"donIds\",\"type\":\"uint32[]\"}],\"name\":\"removeDONs\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint256[]\"}],\"name\":\"removeNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"removedNodeP2PIds\",\"type\":\"bytes32[]\"}],\"name\":\"removeNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodes\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"}],\"name\":\"updateDON\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"updateNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeInfo[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"updateNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x6080604052601280546001600160401b0319166401000000011790553480156200002857600080fd5b503380600081620000805760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000b357620000b381620000bc565b50505062000167565b336001600160a01b03821603620001165760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000077565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b61437580620001776000396000f3fe608060405234801561001057600080fd5b50600436106101ae5760003560e01c806365c14dc7116100ee5780639cb7c5f411610097578063c63239c511610071578063c63239c514610413578063ddbe4f8214610426578063e29581aa1461043b578063f2fde38b1461045157600080fd5b80639cb7c5f4146103cd578063ae3c241c146103ed578063b06e07a71461040057600080fd5b806373ac22b4116100c857806373ac22b41461038a57806379ba50971461039d5780638da5cb5b146103a557600080fd5b806365c14dc71461034257806366acaa33146103625780636ae5c5911461037757600080fd5b8063214502431161015b57806336b402fb1161013557806336b402fb146102b3578063398f3773146102fb57806350c946fe1461030e5780635e65e3091461032f57600080fd5b8063214502431461026b57806323537405146102805780632c01a1e8146102a057600080fd5b8063181f5a771161018c578063181f5a77146102035780631cdf6343146102455780631d05394c1461025857600080fd5b80630c5801e3146101b3578063117392ce146101c857806312570011146101db575b600080fd5b6101c66101c136600461328f565b610464565b005b6101c66101d63660046132fb565b610775565b6101ee6101e9366004613313565b6109c0565b60405190151581526020015b60405180910390f35b60408051808201909152601881527f4361706162696c697479526567697374727920312e302e30000000000000000060208201525b6040516101fa9190613390565b6101c66102533660046133a3565b6109d3565b6101c66102663660046133a3565b610aa3565b610273610be3565b6040516101fa91906134f5565b61029361028e36600461358e565b610d32565b6040516101fa91906135a9565b6101c66102ae3660046133a3565b610d65565b6102ed6102c13660046135bc565b604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b6040519081526020016101fa565b6101c66103093660046133a3565b611009565b61032161031c366004613313565b6111cc565b6040516101fa92919061361f565b6101c661033d3660046133a3565b611201565b610355610350366004613313565b611704565b6040516101fa919061367c565b61036a6117ea565b6040516101fa919061368f565b6101c6610385366004613710565b6119a8565b6101c66103983660046133a3565b611a4b565b6101c6611eb1565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101fa565b6103e06103db366004613313565b611fae565b6040516101fa9190613833565b6101c66103fb366004613313565b612058565b61023861040e366004613841565b612123565b6101c661042136600461386b565b6121f8565b61042e612287565b6040516101fa91906138fe565b61044361244d565b6040516101fa92919061394c565b6101c661045f366004613a2d565b6125d8565b8281146104ac576040517fab8b67c600000000000000000000000000000000000000000000000000000000815260048101849052602481018290526044015b60405180910390fd5b6000805473ffffffffffffffffffffffffffffffffffffffff16905b8481101561076d5760008686838181106104e4576104e4613a4a565b905060200201359050600085858481811061050157610501613a4a565b90506020028101906105139190613a79565b61051c90613b81565b805190915073ffffffffffffffffffffffffffffffffffffffff1661056d576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805173ffffffffffffffffffffffffffffffffffffffff1633148015906105aa57503373ffffffffffffffffffffffffffffffffffffffff851614155b156105e1576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516000838152600f602052604090205473ffffffffffffffffffffffffffffffffffffffff908116911614158061069357506020808201516040516106279201613390565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201206000868152600f835292909220919261067a926001019101613c9a565b6040516020818303038152906040528051906020012014155b1561075a5780516000838152600f6020908152604090912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9093169290921782558201516001909101906107009082613d89565b50806000015173ffffffffffffffffffffffffffffffffffffffff167f14c8f513e8a6d86d2d16b0cb64976de4e72386c4f8068eca3b7354373f8fe97a838360200151604051610751929190613ea3565b60405180910390a25b50508061076690613eeb565b90506104c8565b505050505050565b61077d6125ec565b6040805182356020828101919091528084013582840152825180830384018152606090920190925280519101206107b560038261266f565b156107ec576040517fe288638f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006107fe6080840160608501613a2d565b73ffffffffffffffffffffffffffffffffffffffff1614610969576108296080830160608401613a2d565b73ffffffffffffffffffffffffffffffffffffffff163b158061090957506108576080830160608401613a2d565b6040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527f73e8b41d00000000000000000000000000000000000000000000000000000000600482015273ffffffffffffffffffffffffffffffffffffffff91909116906301ffc9a790602401602060405180830381865afa1580156108e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109079190613f23565b155b156109695761091e6080830160608401613a2d565b6040517fabb5e3fd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016104a3565b61097460038261268a565b506000818152600260205260409020829061098f8282613f40565b505060405181907f65610e5677eedff94555572640e442f89848a109ef8593fa927ac30b2565ff0690600090a25050565b60006109cd60058361266f565b92915050565b6109db6125ec565b60005b81811015610a9e5760008383838181106109fa576109fa613a4a565b602090810292909201356000818152600f9093526040832080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168155909350919050610a4b60018301826131f5565b50610a59905060078261268a565b506040518181527f1e5877d7b3001d1569bf733b76c7eceda58bd6c031e5b8d0b7042308ba2e9d4f9060200160405180910390a150610a9781613eeb565b90506109de565b505050565b610aab6125ec565b60005b81811015610a9e576000838383818110610aca57610aca613a4a565b9050602002016020810190610adf919061358e565b63ffffffff808216600090815260116020526040812080549394509264010000000090049091169003610b46576040517f2b62be9b00000000000000000000000000000000000000000000000000000000815263ffffffff831660048201526024016104a3565b63ffffffff808316600081815260116020526040902080547fffffffffffffffffffffffffffffffffffffffffffffff000000000000000000169055610b90916009919061268a16565b506040805163ffffffff84168152600060208201527ff264aae70bf6a9d90e68e0f9b393f4e7fbea67b063b0f336e0b36c1581703651910160405180910390a1505080610bdc90613eeb565b9050610aae565b601254606090640100000000900463ffffffff1660006001610c056009612696565b601254610c209190640100000000900463ffffffff16613fc2565b610c2a9190613fc2565b67ffffffffffffffff811115610c4257610c42613ab7565b604051908082528060200260200182016040528015610cb857816020015b6040805160a08101825260008082526020808301829052928201526060808201819052608082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909201910181610c605790505b509050600060015b838163ffffffff161015610d2957610ce2600963ffffffff8084169061266f16565b610d1957610cef816126a0565b838381518110610d0157610d01613a4a565b602002602001018190525081610d1690613eeb565b91505b610d2281613fd5565b9050610cc0565b50909392505050565b6040805160a08101825260008082526020820181905291810191909152606080820181905260808201526109cd826126a0565b6000805473ffffffffffffffffffffffffffffffffffffffff163314905b82811015611003576000848483818110610d9f57610d9f613a4a565b602090810292909201356000818152601090935260409092206001015491925050151580610dfc576040517f64e2ee92000000000000000000000000000000000000000000000000000000008152600481018390526024016104a3565b60008281526010602090815260408083205463ffffffff168352600f82528083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff1682526001810180549293919291840191610e5690613c4d565b80601f0160208091040260200160405190810160405280929190818152602001828054610e8290613c4d565b8015610ecf5780601f10610ea457610100808354040283529160200191610ecf565b820191906000526020600020905b815481529060010190602001808311610eb257829003601f168201915b505050505081525050905084158015610eff5750805173ffffffffffffffffffffffffffffffffffffffff163314155b15610f36576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260106020526040902060010154610f5490600b90612939565b50600083815260106020526040902060020154610f7390600d90612939565b5060008381526010602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001681556001810183905560020191909155517f5254e609a97bab37b7cc79fe128f85c097bd6015c6e1624ae0ba392eb975320590610fe79085815260200190565b60405180910390a150505080610ffc90613eeb565b9050610d83565b50505050565b6110116125ec565b60005b81811015610a9e57600083838381811061103057611030613a4a565b90506020028101906110429190613a79565b61104b90613b81565b805190915073ffffffffffffffffffffffffffffffffffffffff1661109c576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601254604080518082018252835173ffffffffffffffffffffffffffffffffffffffff908116825260208086015181840190815263ffffffff9095166000818152600f909252939020825181547fffffffffffffffffffffffff000000000000000000000000000000000000000016921691909117815592519192909160018201906111289082613d89565b5050601280549091506000906111439063ffffffff16613fd5565b91906101000a81548163ffffffff021916908363ffffffff160217905550816000015173ffffffffffffffffffffffffffffffffffffffff167fda6697b182650034bd205cdc2dbfabb06bdb3a0a83a2b45bfefa3c4881284e0b8284602001516040516111b1929190613ea3565b60405180910390a25050806111c590613eeb565b9050611014565b60408051608081018252600080825260208201819052918101829052606080820152906111f883612945565b91509150915091565b60005b81811015610a9e57600083838381811061122057611220613a4a565b90506020028101906112329190613ff8565b61123b9061402c565b9050600061125e60005473ffffffffffffffffffffffffffffffffffffffff1690565b825163ffffffff166000908152600f602090815260408083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff908116835260018201805496909116331496509394919390928401916112bb90613c4d565b80601f01602080910402602001604051908101604052809291908181526020018280546112e790613c4d565b80156113345780601f1061130957610100808354040283529160200191611334565b820191906000526020600020905b81548152906001019060200180831161131757829003601f168201915b5050505050815250509050811580156113645750805173ffffffffffffffffffffffffffffffffffffffff163314155b1561139b576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040808401516000908152601060205220600101541515806113f15783604001516040517f64e2ee920000000000000000000000000000000000000000000000000000000081526004016104a391815260200190565b6020840151158061143757508360200151601060008660400151815260200190815260200160002060010154141580156114375750602084015161143790600b9061266f565b1561146e576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606084015180516000036114b057806040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016104a391906140ff565b60408581015160009081526010602052208054640100000000900463ffffffff169060046114dd83613fd5565b82546101009290920a63ffffffff8181021990931691831602179091556040878101516000908152601060205290812054640100000000900490911691505b82518110156115e85761155283828151811061153a5761153a613a4a565b6020026020010151600361266f90919063ffffffff16565b61158a57826040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016104a391906140ff565b6115d783828151811061159f5761159f613a4a565b6020908102919091018101516040808b015160009081526010845281812063ffffffff80891683526003909101909452209161268a16565b506115e181613eeb565b905061151c565b5085516040808801805160009081526010602090815283822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff9096169590951790945581518082528382206002015581518152828120600190810154948b015192518252929020909101541461169d5761166c600b82612939565b50602080880180516040808b015160009081526010909452909220600101919091555161169b90600b9061268a565b505b60408781015188516020808b0151845193845263ffffffff909216908301528183015290517ff101cfc54994c31624d25789378d71ec4dbdc533e26a4ecc6b7648f4798d09169181900360600190a150505050505050806116fd90613eeb565b9050611204565b6040805180820190915260008152606060208201526000828152600f60209081526040918290208251808401909352805473ffffffffffffffffffffffffffffffffffffffff168352600181018054919284019161176190613c4d565b80601f016020809104026020016040519081016040528092919081815260200182805461178d90613c4d565b80156117da5780601f106117af576101008083540402835291602001916117da565b820191906000526020600020905b8154815290600101906020018083116117bd57829003601f168201915b5050505050815250509050919050565b60125460609063ffffffff16600060016118046007612696565b601254611817919063ffffffff16613fc2565b6118219190613fc2565b67ffffffffffffffff81111561183957611839613ab7565b60405190808252806020026020018201604052801561187f57816020015b6040805180820190915260008152606060208201528152602001906001900390816118575790505b509050600060015b8363ffffffff16811015610d29576118a060078261266f565b611998576000818152600f60209081526040918290208251808401909352805473ffffffffffffffffffffffffffffffffffffffff16835260018101805491928401916118ec90613c4d565b80601f016020809104026020016040519081016040528092919081815260200182805461191890613c4d565b80156119655780601f1061193a57610100808354040283529160200191611965565b820191906000526020600020905b81548152906001019060200180831161194857829003601f168201915b50505050508152505083838151811061198057611980613a4a565b60200260200101819052508161199590613eeb565b91505b6119a181613eeb565b9050611887565b6119b06125ec565b601254640100000000900463ffffffff16600081815260116020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001682179055611a0781600188888888886129ea565b60128054600490611a2590640100000000900463ffffffff16613fd5565b91906101000a81548163ffffffff021916908363ffffffff160217905550505050505050565b60005b81811015610a9e576000838383818110611a6a57611a6a613a4a565b9050602002810190611a7c9190613ff8565b611a859061402c565b90506000611aa860005473ffffffffffffffffffffffffffffffffffffffff1690565b825163ffffffff166000908152600f602090815260408083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff90811683526001820180549690911633149650939491939092840191611b0590613c4d565b80601f0160208091040260200160405190810160405280929190818152602001828054611b3190613c4d565b8015611b7e5780601f10611b5357610100808354040283529160200191611b7e565b820191906000526020600020905b815481529060010190602001808311611b6157829003601f168201915b505050505081525050905081158015611bae5750805173ffffffffffffffffffffffffffffffffffffffff163314155b15611be5576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408084015160009081526010602052206001015415158080611c0a57506040840151155b15611c495783604001516040517f64e2ee920000000000000000000000000000000000000000000000000000000081526004016104a391815260200190565b60208401511580611c6657506020840151611c6690600b9061266f565b15611c9d576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60608401518051600003611cdf57806040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016104a391906140ff565b60408581015160009081526010602052208054600490611d0c90640100000000900463ffffffff16613fd5565b82546101009290920a63ffffffff81810219909316918316021790915560408681015160009081526010602052908120546401000000009004909116905b8251811015611dc657611d6883828151811061153a5761153a613a4a565b611da057826040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016104a391906140ff565b611db583828151811061159f5761159f613a4a565b50611dbf81613eeb565b9050611d4a565b5085516040808801805160009081526010602090815283822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff9687161790558251808352848320600201558a018051925182529290206001015551611e3891600b919061268a16565b506040860151611e4a90600d9061268a565b5060408681015187516020808a0151845193845263ffffffff909216908301528183015290517fc9296aa9b0951d8000e8ed7f2b5be30c5106de8df3dbedf9a57c93f5f9e4d7da9181900360600190a150505050505080611eaa90613eeb565b9050611a4e565b60015473ffffffffffffffffffffffffffffffffffffffff163314611f32576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016104a3565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b604080516080808201835260008083526020808401829052838501829052606084018290528582526002808252918590208551938401865280548452600180820154928501929092529182015493949293919284019160ff169081111561201757612017613794565b600181111561202857612028613794565b815260029190910154610100900473ffffffffffffffffffffffffffffffffffffffff1660209091015292915050565b6120606125ec565b61206b60038261266f565b6120a4576040517fe181733f000000000000000000000000000000000000000000000000000000008152600481018290526024016104a3565b6120af60058261266f565b156120e9576040517ff7d7a294000000000000000000000000000000000000000000000000000000008152600481018290526024016104a3565b6120f460058261268a565b5060405181907fdcea1b78b6ddc31592a94607d537543fcaafda6cc52d6d5cc7bbfca1422baf2190600090a250565b63ffffffff8083166000908152601160209081526040808320805464010000000090049094168084526001909401825280832085845260030190915290208054606092919061217190613c4d565b80601f016020809104026020016040519081016040528092919081815260200182805461219d90613c4d565b80156121ea5780601f106121bf576101008083540402835291602001916121ea565b820191906000526020600020905b8154815290600101906020018083116121cd57829003601f168201915b505050505091505092915050565b6122006125ec565b63ffffffff808716600090815260116020526040812054640100000000900490911690819003612264576040517f2b62be9b00000000000000000000000000000000000000000000000000000000815263ffffffff881660048201526024016104a3565b61227e8761227183613fd5565b92508288888888886129ea565b50505050505050565b606060006122956003612e76565b905060006122a36005612696565b82516122af9190613fc2565b67ffffffffffffffff8111156122c7576122c7613ab7565b60405190808252806020026020018201604052801561233757816020015b6040805160808101825260008082526020808301829052928201819052606082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816122e55790505b5090506000805b8351811015610d2957600084828151811061235b5761235b613a4a565b6020026020010151905061237981600561266f90919063ffffffff16565b61243c576002600082815260200190815260200160002060405180608001604052908160008201548152602001600182015481526020016002820160009054906101000a900460ff1660018111156123d3576123d3613794565b60018111156123e4576123e4613794565b815260029190910154610100900473ffffffffffffffffffffffffffffffffffffffff16602090910152845185908590811061242257612422613a4a565b6020026020010181905250828061243890613eeb565b9350505b5061244681613eeb565b905061233e565b606080600061245c600d612e76565b90506000815167ffffffffffffffff81111561247a5761247a613ab7565b6040519080825280602002602001820160405280156124e957816020015b60408051608081018252600080825260208083018290529282015260608082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816124985790505b5090506000825167ffffffffffffffff81111561250857612508613ab7565b604051908082528060200260200182016040528015612531578160200160208202803683370190505b50905060005b83518110156125cd57600084828151811061255457612554613a4a565b6020026020010151905060008061256a83612945565b915091508186858151811061258157612581613a4a565b60200260200101819052508085858151811061259f5761259f613a4a565b602002602001019063ffffffff16908163ffffffff1681525050505050806125c690613eeb565b9050612537565b509094909350915050565b6125e06125ec565b6125e981612e83565b50565b60005473ffffffffffffffffffffffffffffffffffffffff16331461266d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016104a3565b565b600081815260018301602052604081205415155b9392505050565b60006126838383612f78565b60006109cd825490565b6040805160a081018252600080825260208083018290528284018290526060808401819052608084015263ffffffff85811683526011825284832080546401000000009004909116808452600190910182528483206002810180548751818602810186019098528088529596929591949390919083018282801561274357602002820191906000526020600020905b81548152602001906001019080831161272f575b505050505090506000815167ffffffffffffffff81111561276657612766613ab7565b6040519080825280602002602001820160405280156127ac57816020015b6040805180820190915260008152606060208201528152602001906001900390816127845790505b50905060005b81518110156128cd5760405180604001604052808483815181106127d8576127d8613a4a565b602002602001015181526020018560030160008685815181106127fd576127fd613a4a565b60200260200101518152602001908152602001600020805461281e90613c4d565b80601f016020809104026020016040519081016040528092919081815260200182805461284a90613c4d565b80156128975780601f1061286c57610100808354040283529160200191612897565b820191906000526020600020905b81548152906001019060200180831161287a57829003601f168201915b50505050508152508282815181106128b1576128b1613a4a565b6020026020010181905250806128c690613eeb565b90506127b2565b506040805160a08101825263ffffffff888116600081815260116020818152868320548086168752948b168187015292909152905268010000000000000000900460ff161515918101919091526060810161292785612e76565b81526020019190915295945050505050565b60006126838383612fc7565b604080516080810182526000808252602082018190529181019190915260608082015260408051608081018252600084815260106020908152838220805463ffffffff8082168652600183015484870152600283015486880152640100000000909104168352600301905291822060608201906129c190612e76565b905260009384526010602052604090932054929364010000000090930463ffffffff1692915050565b63ffffffff8088166000908152601160209081526040808320938a16835260019093019052908120905b85811015612ad857612a41878783818110612a3157612a31613a4a565b859260209091020135905061266f565b15612aa25788878783818110612a5957612a59613a4a565b6040517f636e405700000000000000000000000000000000000000000000000000000000815263ffffffff909416600485015260200291909101356024830152506044016104a3565b612ac7878783818110612ab757612ab7613a4a565b859260209091020135905061268a565b50612ad181613eeb565b9050612a14565b5060005b83811015612db75736858583818110612af757612af7613a4a565b9050602002810190612b099190613a79565b9050612b176003823561266f565b612b50576040517fe181733f000000000000000000000000000000000000000000000000000000008152813560048201526024016104a3565b612b5c6005823561266f565b15612b96576040517ff7d7a294000000000000000000000000000000000000000000000000000000008152813560048201526024016104a3565b8035600090815260038401602052604081208054612bb390613c4d565b90501115612bfc576040517f3927d08000000000000000000000000000000000000000000000000000000000815263ffffffff8b166004820152813560248201526044016104a3565b60005b87811015612d0e57612ca38235601060008c8c86818110612c2257612c22613a4a565b9050602002013581526020019081526020016000206003016000601060008e8e88818110612c5257612c52613a4a565b90506020020135815260200190815260200160002060000160049054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200190815260200160002061266f90919063ffffffff16565b612cfe57888882818110612cb957612cb9613a4a565b6040517fa7e7925000000000000000000000000000000000000000000000000000000000815260209091029290920135600483015250823560248201526044016104a3565b612d0781613eeb565b9050612bff565b5060028301805460018101825560009182526020918290208335910155612d3790820182614137565b82356000908152600386016020526040902091612d5591908361419c565b50612da68a8a83358b8b612d6c6020880188614137565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506130ba92505050565b50612db081613eeb565b9050612adc565b5063ffffffff88811660008181526011602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff1668010000000000000000881515027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff1617640100000000958d1695860217905581519283528201929092527ff264aae70bf6a9d90e68e0f9b393f4e7fbea67b063b0f336e0b36c1581703651910160405180910390a15050505050505050565b6060600061268383613199565b3373ffffffffffffffffffffffffffffffffffffffff821603612f02576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016104a3565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000818152600183016020526040812054612fbf575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556109cd565b5060006109cd565b600081815260018301602052604081205480156130b0576000612feb600183613fc2565b8554909150600090612fff90600190613fc2565b905081811461306457600086600001828154811061301f5761301f613a4a565b906000526020600020015490508087600001848154811061304257613042613a4a565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080613075576130756142b7565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506109cd565b60009150506109cd565b60008481526002602081905260409091200154610100900473ffffffffffffffffffffffffffffffffffffffff161561076d57600084815260026020819052604091829020015490517ffba64a7c00000000000000000000000000000000000000000000000000000000815261010090910473ffffffffffffffffffffffffffffffffffffffff169063fba64a7c9061315f908690869086908b908d906004016142e6565b600060405180830381600087803b15801561317957600080fd5b505af115801561318d573d6000803e3d6000fd5b50505050505050505050565b6060816000018054806020026020016040519081016040528092919081815260200182805480156131e957602002820191906000526020600020905b8154815260200190600101908083116131d5575b50505050509050919050565b50805461320190613c4d565b6000825580601f10613211575050565b601f0160209004906000526020600020908101906125e991905b8082111561323f576000815560010161322b565b5090565b60008083601f84011261325557600080fd5b50813567ffffffffffffffff81111561326d57600080fd5b6020830191508360208260051b850101111561328857600080fd5b9250929050565b600080600080604085870312156132a557600080fd5b843567ffffffffffffffff808211156132bd57600080fd5b6132c988838901613243565b909650945060208701359150808211156132e257600080fd5b506132ef87828801613243565b95989497509550505050565b60006080828403121561330d57600080fd5b50919050565b60006020828403121561332557600080fd5b5035919050565b6000815180845260005b8181101561335257602081850181015186830182015201613336565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081526000612683602083018461332c565b600080602083850312156133b657600080fd5b823567ffffffffffffffff8111156133cd57600080fd5b6133d985828601613243565b90969095509350505050565b600081518084526020808501945080840160005b83811015613415578151875295820195908201906001016133f9565b509495945050505050565b600063ffffffff8083511684526020818185015116818601526040915081840151151582860152606084015160a0606087015261346060a08701826133e5565b9050608085015186820360808801528181518084528484019150848160051b850101858401935060005b828110156134e7578582037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00184528451805183528701518783018990526134d48984018261332c565b958801959488019492505060010161348a565b509998505050505050505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015613568577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452613556858351613420565b9450928501929085019060010161351c565b5092979650505050505050565b803563ffffffff8116811461358957600080fd5b919050565b6000602082840312156135a057600080fd5b61268382613575565b6020815260006126836020830184613420565b600080604083850312156135cf57600080fd5b50508035926020909101359150565b63ffffffff81511682526020810151602083015260408101516040830152600060608201516080606085015261361760808501826133e5565b949350505050565b60408152600061363260408301856135de565b905063ffffffff831660208301529392505050565b73ffffffffffffffffffffffffffffffffffffffff81511682526000602082015160406020850152613617604085018261332c565b6020815260006126836020830184613647565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015613568577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526136f0858351613647565b945092850192908501906001016136b6565b80151581146125e957600080fd5b60008060008060006060868803121561372857600080fd5b853567ffffffffffffffff8082111561374057600080fd5b61374c89838a01613243565b9097509550602088013591508082111561376557600080fd5b5061377288828901613243565b909450925050604086013561378681613702565b809150509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b805182526020810151602083015260408101516002811061380d577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b604083015260609081015173ffffffffffffffffffffffffffffffffffffffff16910152565b608081016109cd82846137c3565b6000806040838503121561385457600080fd5b61385d83613575565b946020939093013593505050565b6000806000806000806080878903121561388457600080fd5b61388d87613575565b9550602087013567ffffffffffffffff808211156138aa57600080fd5b6138b68a838b01613243565b909750955060408901359150808211156138cf57600080fd5b506138dc89828a01613243565b90945092505060608701356138f081613702565b809150509295509295509295565b6020808252825182820181905260009190848201906040850190845b818110156139405761392d8385516137c3565b928401926080929092019160010161391a565b50909695505050505050565b6000604082016040835280855180835260608501915060608160051b8601019250602080880160005b838110156139c1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08887030185526139af8683516135de565b95509382019390820190600101613975565b50508584038187015286518085528782019482019350915060005b828110156139fe57845163ffffffff16845293810193928101926001016139dc565b5091979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff811681146125e957600080fd5b600060208284031215613a3f57600080fd5b813561268381613a0b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112613aad57600080fd5b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715613b0957613b09613ab7565b60405290565b6040516080810167ffffffffffffffff81118282101715613b0957613b09613ab7565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613b7957613b79613ab7565b604052919050565b600060408236031215613b9357600080fd5b613b9b613ae6565b8235613ba681613a0b565b815260208381013567ffffffffffffffff80821115613bc457600080fd5b9085019036601f830112613bd757600080fd5b813581811115613be957613be9613ab7565b613c19847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613b32565b91508082523684828501011115613c2f57600080fd5b80848401858401376000908201840152918301919091525092915050565b600181811c90821680613c6157607f821691505b60208210810361330d577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000602080835260008454613cae81613c4d565b80848701526040600180841660008114613ccf5760018114613d0757613d35565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008516838a01528284151560051b8a01019550613d35565b896000528660002060005b85811015613d2d5781548b8201860152908301908801613d12565b8a0184019650505b509398975050505050505050565b601f821115610a9e57600081815260208120601f850160051c81016020861015613d6a5750805b601f850160051c820191505b8181101561076d57828155600101613d76565b815167ffffffffffffffff811115613da357613da3613ab7565b613db781613db18454613c4d565b84613d43565b602080601f831160018114613e0a5760008415613dd45750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b17855561076d565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015613e5757888601518255948401946001909101908401613e38565b5085821015613e9357878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b828152604060208201526000613617604083018461332c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613f1c57613f1c613ebc565b5060010190565b600060208284031215613f3557600080fd5b815161268381613702565b813581556020820135600182015560028101604083013560028110613f6457600080fd5b81546060850135613f7481613a0b565b74ffffffffffffffffffffffffffffffffffffffff008160081b1660ff84167fffffffffffffffffffffff000000000000000000000000000000000000000000841617178455505050505050565b818103818111156109cd576109cd613ebc565b600063ffffffff808316818103613fee57613fee613ebc565b6001019392505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112613aad57600080fd5b60006080823603121561403e57600080fd5b614046613b0f565b61404f83613575565b81526020808401358183015260408401356040830152606084013567ffffffffffffffff8082111561408057600080fd5b9085019036601f83011261409357600080fd5b8135818111156140a5576140a5613ab7565b8060051b91506140b6848301613b32565b81815291830184019184810190368411156140d057600080fd5b938501935b838510156140ee578435825293850193908501906140d5565b606087015250939695505050505050565b6020808252825182820181905260009190848201906040850190845b818110156139405783518352928401929184019160010161411b565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261416c57600080fd5b83018035915067ffffffffffffffff82111561418757600080fd5b60200191503681900382131561328857600080fd5b67ffffffffffffffff8311156141b4576141b4613ab7565b6141c8836141c28354613c4d565b83613d43565b6000601f84116001811461421a57600085156141e45750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b1783556142b0565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156142695786850135825560209485019460019092019101614249565b50868210156142a4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b6080815284608082015260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86111561431f57600080fd5b8560051b808860a0850137820182810360a090810160208501526143459082018761332c565b91505063ffffffff8085166040840152808416606084015250969550505050505056fea164736f6c6343000813000a", + ABI: "[{\"inputs\":[],\"name\":\"AccessForbidden\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CapabilityAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityIsDeprecated\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONCapability\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONNode\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"proposedConfigurationContract\",\"type\":\"address\"}],\"name\":\"InvalidCapabilityConfigurationContractInterface\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"name\":\"InvalidNodeCapabilities\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeOperatorAdmin\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"InvalidNodeP2PId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"lengthOne\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lengthTwo\",\"type\":\"uint256\"}],\"name\":\"LengthMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"NodeDoesNotSupportCapability\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDeprecated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"donId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"}],\"name\":\"DONAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"}],\"name\":\"NodeAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"}],\"name\":\"NodeOperatorRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"NodeRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"NodeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability\",\"name\":\"capability\",\"type\":\"tuple\"}],\"name\":\"addCapability\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"nodes\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"}],\"name\":\"addDON\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"addNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeParams[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"addNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"deprecateCapability\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCapabilities\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedId\",\"type\":\"bytes32\"}],\"name\":\"getCapability\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"getDON\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"},{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"getDONCapabilityConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"}],\"name\":\"getHashedCapabilityId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"getNode\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeParams\",\"name\":\"\",\"type\":\"tuple\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"}],\"name\":\"getNodeOperator\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"isCapabilityDeprecated\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint256[]\"}],\"name\":\"removeNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"removedNodeP2PIds\",\"type\":\"bytes32[]\"}],\"name\":\"removeNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"updateNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeParams[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"updateNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x6080604052600b805463ffffffff191660011790553480156200002157600080fd5b503380600081620000795760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000ac57620000ac81620000b5565b50505062000160565b336001600160a01b038216036200010f5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000070565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6134b380620001706000396000f3fe608060405234801561001057600080fd5b50600436106101775760003560e01c806350e03b16116100d85780638da5cb5b1161008c578063b06e07a711610066578063b06e07a71461038f578063ddbe4f82146103a2578063f2fde38b146103b757600080fd5b80638da5cb5b146103345780639cb7c5f41461035c578063ae3c241c1461037c57600080fd5b806365c14dc7116100bd57806365c14dc7146102f95780636ae5c5911461031957806379ba50971461032c57600080fd5b806350e03b16146102d35780635840cd45146102e657600080fd5b8063235374051161012f57806336b402fb1161011457806336b402fb14610257578063398f37731461029f57806350c946fe146102b257600080fd5b806323537405146102215780632c01a1e81461024457600080fd5b8063125700111161016057806312570011146101a4578063181f5a77146101cc5780631cdf63431461020e57600080fd5b80630c5801e31461017c578063117392ce14610191575b600080fd5b61018f61018a3660046126db565b6103ca565b005b61018f61019f366004612747565b6106db565b6101b76101b236600461275f565b610926565b60405190151581526020015b60405180910390f35b60408051808201909152601881527f4361706162696c697479526567697374727920312e302e30000000000000000060208201525b6040516101c391906127dc565b61018f61021c3660046127ef565b610939565b61023461022f36600461284a565b6109fc565b6040516101c394939291906128a0565b61018f6102523660046127ef565b610c0f565b610291610265366004612958565b604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b6040519081526020016101c3565b61018f6102ad3660046127ef565b610e8d565b6102c56102c036600461275f565b611026565b6040516101c392919061297a565b61018f6102e13660046127ef565b6110e8565b61018f6102f43660046127ef565b6115c9565b61030c61030736600461275f565b611a63565b6040516101c39190612a1c565b61018f610327366004612a6d565b611b49565b61018f611f5d565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101c3565b61036f61036a36600461275f565b61205a565b6040516101c39190612b90565b61018f61038a36600461275f565b612104565b61020161039d366004612b9e565b6121cf565b6103aa612287565b6040516101c39190612bc8565b61018f6103c5366004612c38565b6123cc565b828114610412576040517fab8b67c600000000000000000000000000000000000000000000000000000000815260048101849052602481018290526044015b60405180910390fd5b6000805473ffffffffffffffffffffffffffffffffffffffff16905b848110156106d357600086868381811061044a5761044a612c55565b905060200201359050600085858481811061046757610467612c55565b90506020028101906104799190612c84565b61048290612d8c565b805190915073ffffffffffffffffffffffffffffffffffffffff166104d3576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805173ffffffffffffffffffffffffffffffffffffffff16331480159061051057503373ffffffffffffffffffffffffffffffffffffffff851614155b15610547576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805160008381526007602052604090205473ffffffffffffffffffffffffffffffffffffffff90811691161415806105f9575060208082015160405161058d92016127dc565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201206000868152600783529290922091926105e0926001019101612ea5565b6040516020818303038152906040528051906020012014155b156106c0578051600083815260076020908152604090912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9093169290921782558201516001909101906106669082612f94565b50806000015173ffffffffffffffffffffffffffffffffffffffff167f14c8f513e8a6d86d2d16b0cb64976de4e72386c4f8068eca3b7354373f8fe97a8383602001516040516106b79291906130ae565b60405180910390a25b5050806106cc906130f6565b905061042e565b505050505050565b6106e36123e0565b60408051823560208281019190915280840135828401528251808303840181526060909201909252805191012061071b600382612463565b15610752576040517fe288638f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006107646080840160608501612c38565b73ffffffffffffffffffffffffffffffffffffffff16146108cf5761078f6080830160608401612c38565b73ffffffffffffffffffffffffffffffffffffffff163b158061086f57506107bd6080830160608401612c38565b6040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527f884efe6100000000000000000000000000000000000000000000000000000000600482015273ffffffffffffffffffffffffffffffffffffffff91909116906301ffc9a790602401602060405180830381865afa158015610849573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061086d919061312e565b155b156108cf576108846080830160608401612c38565b6040517fabb5e3fd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401610409565b6108da60038261247e565b50600081815260026020526040902082906108f5828261314b565b505060405181907f65610e5677eedff94555572640e442f89848a109ef8593fa927ac30b2565ff0690600090a25050565b6000610933600583612463565b92915050565b6109416123e0565b60005b818110156109f757600083838381811061096057610960612c55565b60209081029290920135600081815260079093526040832080547fffffffffffffffffffffffff00000000000000000000000000000000000000001681559093509190506109b16001830182612641565b50506040518181527f1e5877d7b3001d1569bf733b76c7eceda58bd6c031e5b8d0b7042308ba2e9d4f9060200160405180910390a1506109f0816130f6565b9050610944565b505050565b63ffffffff81166000908152600960205260408120819060609081908390610a269060030161248a565b90506000815167ffffffffffffffff811115610a4457610a44612cc2565b604051908082528060200260200182016040528015610a8a57816020015b604080518082019091526000815260606020820152815260200190600190039081610a625790505b50905060005b8151811015610bc9576040518060400160405280848381518110610ab657610ab6612c55565b60200260200101518152602001600960008b63ffffffff1663ffffffff1681526020019081526020016000206005016000868581518110610af957610af9612c55565b602002602001015181526020019081526020016000208054610b1a90612e58565b80601f0160208091040260200160405190810160405280929190818152602001828054610b4690612e58565b8015610b935780601f10610b6857610100808354040283529160200191610b93565b820191906000526020600020905b815481529060010190602001808311610b7657829003601f168201915b5050505050815250828281518110610bad57610bad612c55565b602002602001018190525080610bc2906130f6565b9050610a90565b5063ffffffff8781166000908152600960205260409020805491821691640100000000900460ff1690610bfe9060010161248a565b919750955093509150509193509193565b6000805473ffffffffffffffffffffffffffffffffffffffff163314905b82811015610e87576000848483818110610c4957610c49612c55565b60209081029290920135600081815260089093526040909220549192505068010000000000000000900473ffffffffffffffffffffffffffffffffffffffff16151580610cc5576040517f64e2ee9200000000000000000000000000000000000000000000000000000000815260048101839052602401610409565b60008281526008602090815260408083205463ffffffff168352600782528083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff1682526001810180549293919291840191610d1f90612e58565b80601f0160208091040260200160405190810160405280929190818152602001828054610d4b90612e58565b8015610d985780601f10610d6d57610100808354040283529160200191610d98565b820191906000526020600020905b815481529060010190602001808311610d7b57829003601f168201915b505050505081525050905084158015610dc85750805173ffffffffffffffffffffffffffffffffffffffff163314155b15610dff576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526008602052604080822080547fffffffff0000000000000000000000000000000000000000000000000000000016815560010191909155517f5254e609a97bab37b7cc79fe128f85c097bd6015c6e1624ae0ba392eb975320590610e6b9085815260200190565b60405180910390a150505080610e80906130f6565b9050610c2d565b50505050565b610e956123e0565b60005b818110156109f7576000838383818110610eb457610eb4612c55565b9050602002810190610ec69190612c84565b610ecf90612d8c565b805190915073ffffffffffffffffffffffffffffffffffffffff16610f20576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600a54604080518082018252835173ffffffffffffffffffffffffffffffffffffffff908116825260208086015181840190815260008681526007909252939020825181547fffffffffffffffffffffffff000000000000000000000000000000000000000016921691909117815591519091906001820190610fa39082612f94565b50905050600a60008154610fb6906130f6565b909155508151602083015160405173ffffffffffffffffffffffffffffffffffffffff909216917fda6697b182650034bd205cdc2dbfabb06bdb3a0a83a2b45bfefa3c4881284e0b9161100b918591906130ae565b60405180910390a250508061101f906130f6565b9050610e98565b604080516080810182526000808252602082018190529181019190915260608082015260408051608081018252600084815260086020908152838220805463ffffffff808216865273ffffffffffffffffffffffffffffffffffffffff6801000000000000000083041684870152600183015486880152640100000000909104168352600201905291822060608201906110bf9061248a565b905260009384526008602052604090932054929364010000000090930463ffffffff1692915050565b60005b818110156109f757600083838381811061110757611107612c55565b905060200281019061111991906131cd565b61112290613201565b9050600061114560005473ffffffffffffffffffffffffffffffffffffffff1690565b825163ffffffff1660009081526007602090815260408083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff908116835260018201805496909116331496509394919390928401916111a290612e58565b80601f01602080910402602001604051908101604052809291908181526020018280546111ce90612e58565b801561121b5780601f106111f05761010080835404028352916020019161121b565b820191906000526020600020905b8154815290600101906020018083116111fe57829003601f168201915b50505050508152505090508115801561124b5750805173ffffffffffffffffffffffffffffffffffffffff163314155b15611282576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408084015160009081526008602052205468010000000000000000900473ffffffffffffffffffffffffffffffffffffffff161515806112f75783604001516040517f64e2ee9200000000000000000000000000000000000000000000000000000000815260040161040991815260200190565b602084015173ffffffffffffffffffffffffffffffffffffffff16611348576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060840151805160000361138a57806040517f3748d4c600000000000000000000000000000000000000000000000000000000815260040161040991906132dd565b60408581015160009081526008602052208054640100000000900463ffffffff169060046113b7836132f0565b82546101009290920a63ffffffff8181021990931691831602179091556040878101516000908152600860205290812054640100000000900490911691505b82518110156114c25761142c83828151811061141457611414612c55565b6020026020010151600361246390919063ffffffff16565b61146457826040517f3748d4c600000000000000000000000000000000000000000000000000000000815260040161040991906132dd565b6114b183828151811061147957611479612c55565b6020908102919091018101516040808b015160009081526008845281812063ffffffff80891683526002909101909452209161247e16565b506114bb816130f6565b90506113f6565b5085516040808801805160009081526008602090815283822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff968716179055825180835284832060010155808b018051845184529285902080547fffffffff0000000000000000000000000000000000000000ffffffffffffffff166801000000000000000073ffffffffffffffffffffffffffffffffffffffff9586160217905592518b5193518551918252939095169085015216908201527f6bbba867c646be512c2f3241e65fdffdefd5528d7e7939649e06e10ee5addc3e9060600160405180910390a1505050505050806115c2906130f6565b90506110eb565b60005b818110156109f75760008383838181106115e8576115e8612c55565b90506020028101906115fa91906131cd565b61160390613201565b9050600061162660005473ffffffffffffffffffffffffffffffffffffffff1690565b825163ffffffff1660009081526007602090815260408083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff9081168352600182018054969091163314965093949193909284019161168390612e58565b80601f01602080910402602001604051908101604052809291908181526020018280546116af90612e58565b80156116fc5780601f106116d1576101008083540402835291602001916116fc565b820191906000526020600020905b8154815290600101906020018083116116df57829003601f168201915b50505050508152505090508115801561172c5750805173ffffffffffffffffffffffffffffffffffffffff163314155b15611763576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408084015160009081526008602052205468010000000000000000900473ffffffffffffffffffffffffffffffffffffffff16151580806117a757506040840151155b156117e65783604001516040517f64e2ee9200000000000000000000000000000000000000000000000000000000815260040161040991815260200190565b602084015173ffffffffffffffffffffffffffffffffffffffff16611837576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060840151805160000361187957806040517f3748d4c600000000000000000000000000000000000000000000000000000000815260040161040991906132dd565b604085810151600090815260086020522080546004906118a690640100000000900463ffffffff166132f0565b82546101009290920a63ffffffff81810219909316918316021790915560408681015160009081526008602052908120546401000000009004909116905b82518110156119605761190283828151811061141457611414612c55565b61193a57826040517f3748d4c600000000000000000000000000000000000000000000000000000000815260040161040991906132dd565b61194f83828151811061147957611479612c55565b50611959816130f6565b90506118e4565b5085516040808801805160009081526008602090815283822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff968716179055825180835284832060010155808b0151835183529184902080547fffffffff0000000000000000000000000000000000000000ffffffffffffffff166801000000000000000073ffffffffffffffffffffffffffffffffffffffff9094169390930292909217909155905189518351918252909316908301527f5bfe8a52ad26ac6ee7b0cd46d2fd92be04735a31c45ef8aa3d4b7ea1b61bbc1f910160405180910390a150505050505080611a5c906130f6565b90506115cc565b6040805180820190915260008152606060208201526000828152600760209081526040918290208251808401909352805473ffffffffffffffffffffffffffffffffffffffff1683526001810180549192840191611ac090612e58565b80601f0160208091040260200160405190810160405280929190818152602001828054611aec90612e58565b8015611b395780601f10611b0e57610100808354040283529160200191611b39565b820191906000526020600020905b815481529060010190602001808311611b1c57829003601f168201915b5050505050815250509050919050565b611b516123e0565b600b5463ffffffff16600081815260096020526040812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000168317640100000000851515021790555b85811015611c76576000878783818110611bb857611bb8612c55565b905060200201359050611bf581600960008663ffffffff1663ffffffff16815260200190815260200160002060010161246390919063ffffffff16565b15611c3b576040517f636e405700000000000000000000000000000000000000000000000000000000815263ffffffff8416600482015260248101829052604401610409565b63ffffffff8084166000908152600960205260409020611c6391600190910190839061247e16565b505080611c6f906130f6565b9050611b9c565b5060005b83811015611ed45736858583818110611c9557611c95612c55565b9050602002810190611ca79190612c84565b90508035611cb6600382612463565b611cef576040517fe181733f00000000000000000000000000000000000000000000000000000000815260048101829052602401610409565b611cfa600582612463565b15611d34576040517ff7d7a29400000000000000000000000000000000000000000000000000000000815260048101829052602401610409565b63ffffffff8085166000908152600960205260409020611d5c91600390910190839061246316565b15611da2576040517f3927d08000000000000000000000000000000000000000000000000000000000815263ffffffff8516600482015260248101829052604401610409565b60005b88811015611e5a5760008a8a83818110611dc157611dc1612c55565b602090810292909201356000818152600884526040808220805463ffffffff6401000000009091048116845260029091019095529020909350611e0992909150859061246316565b611e49576040517fa7e792500000000000000000000000000000000000000000000000000000000081526004810182905260248101849052604401610409565b50611e53816130f6565b9050611da5565b5063ffffffff8085166000908152600960205260409020611e8391600390910190839061247e16565b50611e916020830183613313565b63ffffffff86166000908152600960209081526040808320868452600501909152902091611ec0919083613378565b50505080611ecd906130f6565b9050611c7a565b50600b8054600090611eeb9063ffffffff166132f0565b91906101000a81548163ffffffff021916908363ffffffff1602179055507fab55f4c8fb4335a586285ae209d1f1e17a7ccb22e1131963624434d98c8546a58183604051611f4d92919063ffffffff9290921682521515602082015260400190565b60405180910390a1505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611fde576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610409565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b604080516080808201835260008083526020808401829052838501829052606084018290528582526002808252918590208551938401865280548452600180820154928501929092529182015493949293919284019160ff16908111156120c3576120c3612af1565b60018111156120d4576120d4612af1565b815260029190910154610100900473ffffffffffffffffffffffffffffffffffffffff1660209091015292915050565b61210c6123e0565b612117600382612463565b612150576040517fe181733f00000000000000000000000000000000000000000000000000000000815260048101829052602401610409565b61215b600582612463565b15612195576040517ff7d7a29400000000000000000000000000000000000000000000000000000000815260048101829052602401610409565b6121a060058261247e565b5060405181907fdcea1b78b6ddc31592a94607d537543fcaafda6cc52d6d5cc7bbfca1422baf2190600090a250565b63ffffffff82166000908152600960209081526040808320848452600501909152902080546060919061220190612e58565b80601f016020809104026020016040519081016040528092919081815260200182805461222d90612e58565b801561227a5780601f1061224f5761010080835404028352916020019161227a565b820191906000526020600020905b81548152906001019060200180831161225d57829003601f168201915b5050505050905092915050565b60606000612295600361248a565b905060006122a36005612497565b82516122af9190613493565b67ffffffffffffffff8111156122c7576122c7612cc2565b60405190808252806020026020018201604052801561233757816020015b6040805160808101825260008082526020808301829052928201819052606082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816122e55790505b5090506000805b83518110156123c357600084828151811061235b5761235b612c55565b6020026020010151905061237981600561246390919063ffffffff16565b6123b2576123868161205a565b84848151811061239857612398612c55565b602002602001018190525082806123ae906130f6565b9350505b506123bc816130f6565b905061233e565b50909392505050565b6123d46123e0565b6123dd816124a1565b50565b60005473ffffffffffffffffffffffffffffffffffffffff163314612461576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610409565b565b600081815260018301602052604081205415155b9392505050565b60006124778383612596565b60606000612477836125e5565b6000610933825490565b3373ffffffffffffffffffffffffffffffffffffffff821603612520576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610409565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60008181526001830160205260408120546125dd57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610933565b506000610933565b60608160000180548060200260200160405190810160405280929190818152602001828054801561263557602002820191906000526020600020905b815481526020019060010190808311612621575b50505050509050919050565b50805461264d90612e58565b6000825580601f1061265d575050565b601f0160209004906000526020600020908101906123dd91905b8082111561268b5760008155600101612677565b5090565b60008083601f8401126126a157600080fd5b50813567ffffffffffffffff8111156126b957600080fd5b6020830191508360208260051b85010111156126d457600080fd5b9250929050565b600080600080604085870312156126f157600080fd5b843567ffffffffffffffff8082111561270957600080fd5b6127158883890161268f565b9096509450602087013591508082111561272e57600080fd5b5061273b8782880161268f565b95989497509550505050565b60006080828403121561275957600080fd5b50919050565b60006020828403121561277157600080fd5b5035919050565b6000815180845260005b8181101561279e57602081850181015186830182015201612782565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815260006124776020830184612778565b6000806020838503121561280257600080fd5b823567ffffffffffffffff81111561281957600080fd5b6128258582860161268f565b90969095509350505050565b803563ffffffff8116811461284557600080fd5b919050565b60006020828403121561285c57600080fd5b61247782612831565b600081518084526020808501945080840160005b8381101561289557815187529582019590820190600101612879565b509495945050505050565b63ffffffff85168152600060208515158184015260406080818501526128c96080850187612865565b8481036060860152855180825283820190600581901b8301850185890160005b83811015612946578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001855281518051845288015188840188905261293388850182612778565b95890195935050908701906001016128e9565b50909c9b505050505050505050505050565b6000806040838503121561296b57600080fd5b50508035926020909101359150565b60408152600060c0820163ffffffff8551166040840152602073ffffffffffffffffffffffffffffffffffffffff81870151166060850152604086015160808501526060860151608060a086015282815180855260e0870191508383019450600092505b808310156129fe57845182529383019360019290920191908301906129de565b5063ffffffff8716838701529350612a139050565b50509392505050565b6020815273ffffffffffffffffffffffffffffffffffffffff825116602082015260006020830151604080840152612a576060840182612778565b949350505050565b80151581146123dd57600080fd5b600080600080600060608688031215612a8557600080fd5b853567ffffffffffffffff80821115612a9d57600080fd5b612aa989838a0161268f565b90975095506020880135915080821115612ac257600080fd5b50612acf8882890161268f565b9094509250506040860135612ae381612a5f565b809150509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8051825260208101516020830152604081015160028110612b6a577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b604083015260609081015173ffffffffffffffffffffffffffffffffffffffff16910152565b608081016109338284612b20565b60008060408385031215612bb157600080fd5b612bba83612831565b946020939093013593505050565b6020808252825182820181905260009190848201906040850190845b81811015612c0a57612bf7838551612b20565b9284019260809290920191600101612be4565b50909695505050505050565b73ffffffffffffffffffffffffffffffffffffffff811681146123dd57600080fd5b600060208284031215612c4a57600080fd5b813561247781612c16565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112612cb857600080fd5b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715612d1457612d14612cc2565b60405290565b6040516080810167ffffffffffffffff81118282101715612d1457612d14612cc2565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715612d8457612d84612cc2565b604052919050565b600060408236031215612d9e57600080fd5b612da6612cf1565b8235612db181612c16565b815260208381013567ffffffffffffffff80821115612dcf57600080fd5b9085019036601f830112612de257600080fd5b813581811115612df457612df4612cc2565b612e24847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601612d3d565b91508082523684828501011115612e3a57600080fd5b80848401858401376000908201840152918301919091525092915050565b600181811c90821680612e6c57607f821691505b602082108103612759577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000602080835260008454612eb981612e58565b80848701526040600180841660008114612eda5760018114612f1257612f40565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008516838a01528284151560051b8a01019550612f40565b896000528660002060005b85811015612f385781548b8201860152908301908801612f1d565b8a0184019650505b509398975050505050505050565b601f8211156109f757600081815260208120601f850160051c81016020861015612f755750805b601f850160051c820191505b818110156106d357828155600101612f81565b815167ffffffffffffffff811115612fae57612fae612cc2565b612fc281612fbc8454612e58565b84612f4e565b602080601f8311600181146130155760008415612fdf5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556106d3565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561306257888601518255948401946001909101908401613043565b508582101561309e57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b828152604060208201526000612a576040830184612778565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613127576131276130c7565b5060010190565b60006020828403121561314057600080fd5b815161247781612a5f565b81358155602082013560018201556002810160408301356002811061316f57600080fd5b8154606085013561317f81612c16565b74ffffffffffffffffffffffffffffffffffffffff008160081b1660ff84167fffffffffffffffffffffff000000000000000000000000000000000000000000841617178455505050505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112612cb857600080fd5b60006080823603121561321357600080fd5b61321b612d1a565b61322483612831565b815260208084013561323581612c16565b8282015260408481013590830152606084013567ffffffffffffffff8082111561325e57600080fd5b9085019036601f83011261327157600080fd5b81358181111561328357613283612cc2565b8060051b9150613294848301612d3d565b81815291830184019184810190368411156132ae57600080fd5b938501935b838510156132cc578435825293850193908501906132b3565b606087015250939695505050505050565b6020815260006124776020830184612865565b600063ffffffff808316818103613309576133096130c7565b6001019392505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261334857600080fd5b83018035915067ffffffffffffffff82111561336357600080fd5b6020019150368190038213156126d457600080fd5b67ffffffffffffffff83111561339057613390612cc2565b6133a48361339e8354612e58565b83612f4e565b6000601f8411600181146133f657600085156133c05750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b17835561348c565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156134455786850135825560209485019460019092019101613425565b5086821015613480577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b81810381811115610933576109336130c756fea164736f6c6343000813000a", } var CapabilityRegistryABI = CapabilityRegistryMetaData.ABI @@ -247,25 +239,28 @@ func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetCapability(hashed return _CapabilityRegistry.Contract.GetCapability(&_CapabilityRegistry.CallOpts, hashedId) } -func (_CapabilityRegistry *CapabilityRegistryCaller) GetDON(opts *bind.CallOpts, donId uint32) (CapabilityRegistryDONInfo, error) { +func (_CapabilityRegistry *CapabilityRegistryCaller) GetDON(opts *bind.CallOpts, donId uint32) (uint32, bool, [][32]byte, []CapabilityRegistryCapabilityConfiguration, error) { var out []interface{} err := _CapabilityRegistry.contract.Call(opts, &out, "getDON", donId) if err != nil { - return *new(CapabilityRegistryDONInfo), err + return *new(uint32), *new(bool), *new([][32]byte), *new([]CapabilityRegistryCapabilityConfiguration), err } - out0 := *abi.ConvertType(out[0], new(CapabilityRegistryDONInfo)).(*CapabilityRegistryDONInfo) + out0 := *abi.ConvertType(out[0], new(uint32)).(*uint32) + out1 := *abi.ConvertType(out[1], new(bool)).(*bool) + out2 := *abi.ConvertType(out[2], new([][32]byte)).(*[][32]byte) + out3 := *abi.ConvertType(out[3], new([]CapabilityRegistryCapabilityConfiguration)).(*[]CapabilityRegistryCapabilityConfiguration) - return out0, err + return out0, out1, out2, out3, err } -func (_CapabilityRegistry *CapabilityRegistrySession) GetDON(donId uint32) (CapabilityRegistryDONInfo, error) { +func (_CapabilityRegistry *CapabilityRegistrySession) GetDON(donId uint32) (uint32, bool, [][32]byte, []CapabilityRegistryCapabilityConfiguration, error) { return _CapabilityRegistry.Contract.GetDON(&_CapabilityRegistry.CallOpts, donId) } -func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetDON(donId uint32) (CapabilityRegistryDONInfo, error) { +func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetDON(donId uint32) (uint32, bool, [][32]byte, []CapabilityRegistryCapabilityConfiguration, error) { return _CapabilityRegistry.Contract.GetDON(&_CapabilityRegistry.CallOpts, donId) } @@ -291,28 +286,6 @@ func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetDONCapabilityConf return _CapabilityRegistry.Contract.GetDONCapabilityConfig(&_CapabilityRegistry.CallOpts, donId, capabilityId) } -func (_CapabilityRegistry *CapabilityRegistryCaller) GetDONs(opts *bind.CallOpts) ([]CapabilityRegistryDONInfo, error) { - var out []interface{} - err := _CapabilityRegistry.contract.Call(opts, &out, "getDONs") - - if err != nil { - return *new([]CapabilityRegistryDONInfo), err - } - - out0 := *abi.ConvertType(out[0], new([]CapabilityRegistryDONInfo)).(*[]CapabilityRegistryDONInfo) - - return out0, err - -} - -func (_CapabilityRegistry *CapabilityRegistrySession) GetDONs() ([]CapabilityRegistryDONInfo, error) { - return _CapabilityRegistry.Contract.GetDONs(&_CapabilityRegistry.CallOpts) -} - -func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetDONs() ([]CapabilityRegistryDONInfo, error) { - return _CapabilityRegistry.Contract.GetDONs(&_CapabilityRegistry.CallOpts) -} - func (_CapabilityRegistry *CapabilityRegistryCaller) GetHashedCapabilityId(opts *bind.CallOpts, labelledName [32]byte, version [32]byte) ([32]byte, error) { var out []interface{} err := _CapabilityRegistry.contract.Call(opts, &out, "getHashedCapabilityId", labelledName, version) @@ -335,26 +308,26 @@ func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetHashedCapabilityI return _CapabilityRegistry.Contract.GetHashedCapabilityId(&_CapabilityRegistry.CallOpts, labelledName, version) } -func (_CapabilityRegistry *CapabilityRegistryCaller) GetNode(opts *bind.CallOpts, p2pId [32]byte) (CapabilityRegistryNodeInfo, uint32, error) { +func (_CapabilityRegistry *CapabilityRegistryCaller) GetNode(opts *bind.CallOpts, p2pId [32]byte) (CapabilityRegistryNodeParams, uint32, error) { var out []interface{} err := _CapabilityRegistry.contract.Call(opts, &out, "getNode", p2pId) if err != nil { - return *new(CapabilityRegistryNodeInfo), *new(uint32), err + return *new(CapabilityRegistryNodeParams), *new(uint32), err } - out0 := *abi.ConvertType(out[0], new(CapabilityRegistryNodeInfo)).(*CapabilityRegistryNodeInfo) + out0 := *abi.ConvertType(out[0], new(CapabilityRegistryNodeParams)).(*CapabilityRegistryNodeParams) out1 := *abi.ConvertType(out[1], new(uint32)).(*uint32) return out0, out1, err } -func (_CapabilityRegistry *CapabilityRegistrySession) GetNode(p2pId [32]byte) (CapabilityRegistryNodeInfo, uint32, error) { +func (_CapabilityRegistry *CapabilityRegistrySession) GetNode(p2pId [32]byte) (CapabilityRegistryNodeParams, uint32, error) { return _CapabilityRegistry.Contract.GetNode(&_CapabilityRegistry.CallOpts, p2pId) } -func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetNode(p2pId [32]byte) (CapabilityRegistryNodeInfo, uint32, error) { +func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetNode(p2pId [32]byte) (CapabilityRegistryNodeParams, uint32, error) { return _CapabilityRegistry.Contract.GetNode(&_CapabilityRegistry.CallOpts, p2pId) } @@ -380,51 +353,6 @@ func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetNodeOperator(node return _CapabilityRegistry.Contract.GetNodeOperator(&_CapabilityRegistry.CallOpts, nodeOperatorId) } -func (_CapabilityRegistry *CapabilityRegistryCaller) GetNodeOperators(opts *bind.CallOpts) ([]CapabilityRegistryNodeOperator, error) { - var out []interface{} - err := _CapabilityRegistry.contract.Call(opts, &out, "getNodeOperators") - - if err != nil { - return *new([]CapabilityRegistryNodeOperator), err - } - - out0 := *abi.ConvertType(out[0], new([]CapabilityRegistryNodeOperator)).(*[]CapabilityRegistryNodeOperator) - - return out0, err - -} - -func (_CapabilityRegistry *CapabilityRegistrySession) GetNodeOperators() ([]CapabilityRegistryNodeOperator, error) { - return _CapabilityRegistry.Contract.GetNodeOperators(&_CapabilityRegistry.CallOpts) -} - -func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetNodeOperators() ([]CapabilityRegistryNodeOperator, error) { - return _CapabilityRegistry.Contract.GetNodeOperators(&_CapabilityRegistry.CallOpts) -} - -func (_CapabilityRegistry *CapabilityRegistryCaller) GetNodes(opts *bind.CallOpts) ([]CapabilityRegistryNodeInfo, []uint32, error) { - var out []interface{} - err := _CapabilityRegistry.contract.Call(opts, &out, "getNodes") - - if err != nil { - return *new([]CapabilityRegistryNodeInfo), *new([]uint32), err - } - - out0 := *abi.ConvertType(out[0], new([]CapabilityRegistryNodeInfo)).(*[]CapabilityRegistryNodeInfo) - out1 := *abi.ConvertType(out[1], new([]uint32)).(*[]uint32) - - return out0, out1, err - -} - -func (_CapabilityRegistry *CapabilityRegistrySession) GetNodes() ([]CapabilityRegistryNodeInfo, []uint32, error) { - return _CapabilityRegistry.Contract.GetNodes(&_CapabilityRegistry.CallOpts) -} - -func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetNodes() ([]CapabilityRegistryNodeInfo, []uint32, error) { - return _CapabilityRegistry.Contract.GetNodes(&_CapabilityRegistry.CallOpts) -} - func (_CapabilityRegistry *CapabilityRegistryCaller) IsCapabilityDeprecated(opts *bind.CallOpts, hashedCapabilityId [32]byte) (bool, error) { var out []interface{} err := _CapabilityRegistry.contract.Call(opts, &out, "isCapabilityDeprecated", hashedCapabilityId) @@ -539,15 +467,15 @@ func (_CapabilityRegistry *CapabilityRegistryTransactorSession) AddNodeOperators return _CapabilityRegistry.Contract.AddNodeOperators(&_CapabilityRegistry.TransactOpts, nodeOperators) } -func (_CapabilityRegistry *CapabilityRegistryTransactor) AddNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistryTransactor) AddNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { return _CapabilityRegistry.contract.Transact(opts, "addNodes", nodes) } -func (_CapabilityRegistry *CapabilityRegistrySession) AddNodes(nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistrySession) AddNodes(nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { return _CapabilityRegistry.Contract.AddNodes(&_CapabilityRegistry.TransactOpts, nodes) } -func (_CapabilityRegistry *CapabilityRegistryTransactorSession) AddNodes(nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistryTransactorSession) AddNodes(nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { return _CapabilityRegistry.Contract.AddNodes(&_CapabilityRegistry.TransactOpts, nodes) } @@ -563,18 +491,6 @@ func (_CapabilityRegistry *CapabilityRegistryTransactorSession) DeprecateCapabil return _CapabilityRegistry.Contract.DeprecateCapability(&_CapabilityRegistry.TransactOpts, hashedCapabilityId) } -func (_CapabilityRegistry *CapabilityRegistryTransactor) RemoveDONs(opts *bind.TransactOpts, donIds []uint32) (*types.Transaction, error) { - return _CapabilityRegistry.contract.Transact(opts, "removeDONs", donIds) -} - -func (_CapabilityRegistry *CapabilityRegistrySession) RemoveDONs(donIds []uint32) (*types.Transaction, error) { - return _CapabilityRegistry.Contract.RemoveDONs(&_CapabilityRegistry.TransactOpts, donIds) -} - -func (_CapabilityRegistry *CapabilityRegistryTransactorSession) RemoveDONs(donIds []uint32) (*types.Transaction, error) { - return _CapabilityRegistry.Contract.RemoveDONs(&_CapabilityRegistry.TransactOpts, donIds) -} - func (_CapabilityRegistry *CapabilityRegistryTransactor) RemoveNodeOperators(opts *bind.TransactOpts, nodeOperatorIds []*big.Int) (*types.Transaction, error) { return _CapabilityRegistry.contract.Transact(opts, "removeNodeOperators", nodeOperatorIds) } @@ -611,18 +527,6 @@ func (_CapabilityRegistry *CapabilityRegistryTransactorSession) TransferOwnershi return _CapabilityRegistry.Contract.TransferOwnership(&_CapabilityRegistry.TransactOpts, to) } -func (_CapabilityRegistry *CapabilityRegistryTransactor) UpdateDON(opts *bind.TransactOpts, donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilityRegistryCapabilityConfiguration, isPublic bool) (*types.Transaction, error) { - return _CapabilityRegistry.contract.Transact(opts, "updateDON", donId, nodes, capabilityConfigurations, isPublic) -} - -func (_CapabilityRegistry *CapabilityRegistrySession) UpdateDON(donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilityRegistryCapabilityConfiguration, isPublic bool) (*types.Transaction, error) { - return _CapabilityRegistry.Contract.UpdateDON(&_CapabilityRegistry.TransactOpts, donId, nodes, capabilityConfigurations, isPublic) -} - -func (_CapabilityRegistry *CapabilityRegistryTransactorSession) UpdateDON(donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilityRegistryCapabilityConfiguration, isPublic bool) (*types.Transaction, error) { - return _CapabilityRegistry.Contract.UpdateDON(&_CapabilityRegistry.TransactOpts, donId, nodes, capabilityConfigurations, isPublic) -} - func (_CapabilityRegistry *CapabilityRegistryTransactor) UpdateNodeOperators(opts *bind.TransactOpts, nodeOperatorIds []*big.Int, nodeOperators []CapabilityRegistryNodeOperator) (*types.Transaction, error) { return _CapabilityRegistry.contract.Transact(opts, "updateNodeOperators", nodeOperatorIds, nodeOperators) } @@ -635,15 +539,15 @@ func (_CapabilityRegistry *CapabilityRegistryTransactorSession) UpdateNodeOperat return _CapabilityRegistry.Contract.UpdateNodeOperators(&_CapabilityRegistry.TransactOpts, nodeOperatorIds, nodeOperators) } -func (_CapabilityRegistry *CapabilityRegistryTransactor) UpdateNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistryTransactor) UpdateNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { return _CapabilityRegistry.contract.Transact(opts, "updateNodes", nodes) } -func (_CapabilityRegistry *CapabilityRegistrySession) UpdateNodes(nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistrySession) UpdateNodes(nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { return _CapabilityRegistry.Contract.UpdateNodes(&_CapabilityRegistry.TransactOpts, nodes) } -func (_CapabilityRegistry *CapabilityRegistryTransactorSession) UpdateNodes(nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistryTransactorSession) UpdateNodes(nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { return _CapabilityRegistry.Contract.UpdateNodes(&_CapabilityRegistry.TransactOpts, nodes) } @@ -901,8 +805,8 @@ func (_CapabilityRegistry *CapabilityRegistryFilterer) ParseCapabilityDeprecated return event, nil } -type CapabilityRegistryConfigSetIterator struct { - Event *CapabilityRegistryConfigSet +type CapabilityRegistryDONAddedIterator struct { + Event *CapabilityRegistryDONAdded contract *bind.BoundContract event string @@ -913,7 +817,7 @@ type CapabilityRegistryConfigSetIterator struct { fail error } -func (it *CapabilityRegistryConfigSetIterator) Next() bool { +func (it *CapabilityRegistryDONAddedIterator) Next() bool { if it.fail != nil { return false @@ -922,7 +826,7 @@ func (it *CapabilityRegistryConfigSetIterator) Next() bool { if it.done { select { case log := <-it.logs: - it.Event = new(CapabilityRegistryConfigSet) + it.Event = new(CapabilityRegistryDONAdded) if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { it.fail = err return false @@ -937,7 +841,7 @@ func (it *CapabilityRegistryConfigSetIterator) Next() bool { select { case log := <-it.logs: - it.Event = new(CapabilityRegistryConfigSet) + it.Event = new(CapabilityRegistryDONAdded) if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { it.fail = err return false @@ -952,33 +856,33 @@ func (it *CapabilityRegistryConfigSetIterator) Next() bool { } } -func (it *CapabilityRegistryConfigSetIterator) Error() error { +func (it *CapabilityRegistryDONAddedIterator) Error() error { return it.fail } -func (it *CapabilityRegistryConfigSetIterator) Close() error { +func (it *CapabilityRegistryDONAddedIterator) Close() error { it.sub.Unsubscribe() return nil } -type CapabilityRegistryConfigSet struct { - DonId uint32 - ConfigCount uint32 - Raw types.Log +type CapabilityRegistryDONAdded struct { + DonId *big.Int + IsPublic bool + Raw types.Log } -func (_CapabilityRegistry *CapabilityRegistryFilterer) FilterConfigSet(opts *bind.FilterOpts) (*CapabilityRegistryConfigSetIterator, error) { +func (_CapabilityRegistry *CapabilityRegistryFilterer) FilterDONAdded(opts *bind.FilterOpts) (*CapabilityRegistryDONAddedIterator, error) { - logs, sub, err := _CapabilityRegistry.contract.FilterLogs(opts, "ConfigSet") + logs, sub, err := _CapabilityRegistry.contract.FilterLogs(opts, "DONAdded") if err != nil { return nil, err } - return &CapabilityRegistryConfigSetIterator{contract: _CapabilityRegistry.contract, event: "ConfigSet", logs: logs, sub: sub}, nil + return &CapabilityRegistryDONAddedIterator{contract: _CapabilityRegistry.contract, event: "DONAdded", logs: logs, sub: sub}, nil } -func (_CapabilityRegistry *CapabilityRegistryFilterer) WatchConfigSet(opts *bind.WatchOpts, sink chan<- *CapabilityRegistryConfigSet) (event.Subscription, error) { +func (_CapabilityRegistry *CapabilityRegistryFilterer) WatchDONAdded(opts *bind.WatchOpts, sink chan<- *CapabilityRegistryDONAdded) (event.Subscription, error) { - logs, sub, err := _CapabilityRegistry.contract.WatchLogs(opts, "ConfigSet") + logs, sub, err := _CapabilityRegistry.contract.WatchLogs(opts, "DONAdded") if err != nil { return nil, err } @@ -988,8 +892,8 @@ func (_CapabilityRegistry *CapabilityRegistryFilterer) WatchConfigSet(opts *bind select { case log := <-logs: - event := new(CapabilityRegistryConfigSet) - if err := _CapabilityRegistry.contract.UnpackLog(event, "ConfigSet", log); err != nil { + event := new(CapabilityRegistryDONAdded) + if err := _CapabilityRegistry.contract.UnpackLog(event, "DONAdded", log); err != nil { return err } event.Raw = log @@ -1010,9 +914,9 @@ func (_CapabilityRegistry *CapabilityRegistryFilterer) WatchConfigSet(opts *bind }), nil } -func (_CapabilityRegistry *CapabilityRegistryFilterer) ParseConfigSet(log types.Log) (*CapabilityRegistryConfigSet, error) { - event := new(CapabilityRegistryConfigSet) - if err := _CapabilityRegistry.contract.UnpackLog(event, "ConfigSet", log); err != nil { +func (_CapabilityRegistry *CapabilityRegistryFilterer) ParseDONAdded(log types.Log) (*CapabilityRegistryDONAdded, error) { + event := new(CapabilityRegistryDONAdded) + if err := _CapabilityRegistry.contract.UnpackLog(event, "DONAdded", log); err != nil { return nil, err } event.Raw = log @@ -1082,7 +986,6 @@ func (it *CapabilityRegistryNodeAddedIterator) Close() error { type CapabilityRegistryNodeAdded struct { P2pId [32]byte NodeOperatorId *big.Int - Signer [32]byte Raw types.Log } @@ -1693,7 +1596,7 @@ func (it *CapabilityRegistryNodeUpdatedIterator) Close() error { type CapabilityRegistryNodeUpdated struct { P2pId [32]byte NodeOperatorId *big.Int - Signer [32]byte + Signer common.Address Raw types.Log } @@ -2027,8 +1930,8 @@ func (_CapabilityRegistry *CapabilityRegistry) ParseLog(log types.Log) (generate return _CapabilityRegistry.ParseCapabilityAdded(log) case _CapabilityRegistry.abi.Events["CapabilityDeprecated"].ID: return _CapabilityRegistry.ParseCapabilityDeprecated(log) - case _CapabilityRegistry.abi.Events["ConfigSet"].ID: - return _CapabilityRegistry.ParseConfigSet(log) + case _CapabilityRegistry.abi.Events["DONAdded"].ID: + return _CapabilityRegistry.ParseDONAdded(log) case _CapabilityRegistry.abi.Events["NodeAdded"].ID: return _CapabilityRegistry.ParseNodeAdded(log) case _CapabilityRegistry.abi.Events["NodeOperatorAdded"].ID: @@ -2059,12 +1962,12 @@ func (CapabilityRegistryCapabilityDeprecated) Topic() common.Hash { return common.HexToHash("0xdcea1b78b6ddc31592a94607d537543fcaafda6cc52d6d5cc7bbfca1422baf21") } -func (CapabilityRegistryConfigSet) Topic() common.Hash { - return common.HexToHash("0xf264aae70bf6a9d90e68e0f9b393f4e7fbea67b063b0f336e0b36c1581703651") +func (CapabilityRegistryDONAdded) Topic() common.Hash { + return common.HexToHash("0xab55f4c8fb4335a586285ae209d1f1e17a7ccb22e1131963624434d98c8546a5") } func (CapabilityRegistryNodeAdded) Topic() common.Hash { - return common.HexToHash("0xc9296aa9b0951d8000e8ed7f2b5be30c5106de8df3dbedf9a57c93f5f9e4d7da") + return common.HexToHash("0x5bfe8a52ad26ac6ee7b0cd46d2fd92be04735a31c45ef8aa3d4b7ea1b61bbc1f") } func (CapabilityRegistryNodeOperatorAdded) Topic() common.Hash { @@ -2084,7 +1987,7 @@ func (CapabilityRegistryNodeRemoved) Topic() common.Hash { } func (CapabilityRegistryNodeUpdated) Topic() common.Hash { - return common.HexToHash("0xf101cfc54994c31624d25789378d71ec4dbdc533e26a4ecc6b7648f4798d0916") + return common.HexToHash("0x6bbba867c646be512c2f3241e65fdffdefd5528d7e7939649e06e10ee5addc3e") } func (CapabilityRegistryOwnershipTransferRequested) Topic() common.Hash { @@ -2104,22 +2007,16 @@ type CapabilityRegistryInterface interface { GetCapability(opts *bind.CallOpts, hashedId [32]byte) (CapabilityRegistryCapability, error) - GetDON(opts *bind.CallOpts, donId uint32) (CapabilityRegistryDONInfo, error) + GetDON(opts *bind.CallOpts, donId uint32) (uint32, bool, [][32]byte, []CapabilityRegistryCapabilityConfiguration, error) GetDONCapabilityConfig(opts *bind.CallOpts, donId uint32, capabilityId [32]byte) ([]byte, error) - GetDONs(opts *bind.CallOpts) ([]CapabilityRegistryDONInfo, error) - GetHashedCapabilityId(opts *bind.CallOpts, labelledName [32]byte, version [32]byte) ([32]byte, error) - GetNode(opts *bind.CallOpts, p2pId [32]byte) (CapabilityRegistryNodeInfo, uint32, error) + GetNode(opts *bind.CallOpts, p2pId [32]byte) (CapabilityRegistryNodeParams, uint32, error) GetNodeOperator(opts *bind.CallOpts, nodeOperatorId *big.Int) (CapabilityRegistryNodeOperator, error) - GetNodeOperators(opts *bind.CallOpts) ([]CapabilityRegistryNodeOperator, error) - - GetNodes(opts *bind.CallOpts) ([]CapabilityRegistryNodeInfo, []uint32, error) - IsCapabilityDeprecated(opts *bind.CallOpts, hashedCapabilityId [32]byte) (bool, error) Owner(opts *bind.CallOpts) (common.Address, error) @@ -2134,23 +2031,19 @@ type CapabilityRegistryInterface interface { AddNodeOperators(opts *bind.TransactOpts, nodeOperators []CapabilityRegistryNodeOperator) (*types.Transaction, error) - AddNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) + AddNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) DeprecateCapability(opts *bind.TransactOpts, hashedCapabilityId [32]byte) (*types.Transaction, error) - RemoveDONs(opts *bind.TransactOpts, donIds []uint32) (*types.Transaction, error) - RemoveNodeOperators(opts *bind.TransactOpts, nodeOperatorIds []*big.Int) (*types.Transaction, error) RemoveNodes(opts *bind.TransactOpts, removedNodeP2PIds [][32]byte) (*types.Transaction, error) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) - UpdateDON(opts *bind.TransactOpts, donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilityRegistryCapabilityConfiguration, isPublic bool) (*types.Transaction, error) - UpdateNodeOperators(opts *bind.TransactOpts, nodeOperatorIds []*big.Int, nodeOperators []CapabilityRegistryNodeOperator) (*types.Transaction, error) - UpdateNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) + UpdateNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) FilterCapabilityAdded(opts *bind.FilterOpts, hashedCapabilityId [][32]byte) (*CapabilityRegistryCapabilityAddedIterator, error) @@ -2164,11 +2057,11 @@ type CapabilityRegistryInterface interface { ParseCapabilityDeprecated(log types.Log) (*CapabilityRegistryCapabilityDeprecated, error) - FilterConfigSet(opts *bind.FilterOpts) (*CapabilityRegistryConfigSetIterator, error) + FilterDONAdded(opts *bind.FilterOpts) (*CapabilityRegistryDONAddedIterator, error) - WatchConfigSet(opts *bind.WatchOpts, sink chan<- *CapabilityRegistryConfigSet) (event.Subscription, error) + WatchDONAdded(opts *bind.WatchOpts, sink chan<- *CapabilityRegistryDONAdded) (event.Subscription, error) - ParseConfigSet(log types.Log) (*CapabilityRegistryConfigSet, error) + ParseDONAdded(log types.Log) (*CapabilityRegistryDONAdded, error) FilterNodeAdded(opts *bind.FilterOpts) (*CapabilityRegistryNodeAddedIterator, error) diff --git a/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt index bedaa8320eb..54d1355c62f 100644 --- a/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,4 +1,4 @@ GETH_VERSION: 1.13.8 -forwarder: ../../../contracts/solc/v0.8.19/KeystoneForwarder/KeystoneForwarder.abi ../../../contracts/solc/v0.8.19/KeystoneForwarder/KeystoneForwarder.bin 7de386c8c4f6cc82ee5d57c35725c522bc3ee0276356b3dce19e1735e70f17b2 -keystone_capability_registry: ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.abi ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.bin 0a79d0eba13fd4a4b83d7618bb181c21c42222f3cc6c5a90a09302f685555033 +forwarder: ../../../contracts/solc/v0.8.19/KeystoneForwarder/KeystoneForwarder.abi ../../../contracts/solc/v0.8.19/KeystoneForwarder/KeystoneForwarder.bin ed9164cfe4619dff824b11df46b66f4c6834b2ca072923f10d9ebc57ce508ed8 +keystone_capability_registry: ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.abi ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.bin d4e0661491c2adc7f0d7553287c938fb32664b9f07770f6d57ae6511ce9884cd ocr3_capability: ../../../contracts/solc/v0.8.19/OCR3Capability/OCR3Capability.abi ../../../contracts/solc/v0.8.19/OCR3Capability/OCR3Capability.bin 9dcbdf55bd5729ba266148da3f17733eb592c871c2108ccca546618628fd9ad2 From d76d2f69fda0c37848c1dc9f12d7d4457cf4c596 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Wed, 5 Jun 2024 19:40:00 +0200 Subject: [PATCH 36/53] ensure logpoller is not affected by FinalityTagBypass --- common/headtracker/head_tracker.go | 26 ++++++++------ .../evm/headtracker/head_tracker_test.go | 35 +++++++++---------- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/common/headtracker/head_tracker.go b/common/headtracker/head_tracker.go index 37eecbfcaad..bc5f5274c0c 100644 --- a/common/headtracker/head_tracker.go +++ b/common/headtracker/head_tracker.go @@ -42,7 +42,8 @@ type HeadTracker[H types.Head[BLOCK_HASH], BLOCK_HASH types.Hashable] interface // Backfill given a head will fill in any missing heads up to latestFinalized Backfill(ctx context.Context, headWithChain H) (err error) LatestChain() H - // LatestAndFinalizedBlock - returns latest and finalized blocks + // LatestAndFinalizedBlock - returns latest and latest finalized blocks. + // NOTE: Returns latest finalized block as is, ignoring the FinalityTagBypass feature flag. LatestAndFinalizedBlock(ctx context.Context) (latest, finalized H, err error) } @@ -146,7 +147,7 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) handleInitialHead(ctx context.Con } ht.log.Debugw("Got initial head", "head", initialHead, "blockNumber", initialHead.BlockNumber(), "blockHash", initialHead.BlockHash()) - latestFinalized, err := ht.calculateLatestFinalized(ctx, initialHead) + latestFinalized, err := ht.calculateLatestFinalized(ctx, initialHead, ht.htConfig.FinalityTagBypass()) if err != nil { return fmt.Errorf("failed to calculate latest finalized head: %w", err) } @@ -197,7 +198,7 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) HealthReport() map[string]error { } func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) Backfill(ctx context.Context, headWithChain HTH) (err error) { - latestFinalized, err := ht.calculateLatestFinalized(ctx, headWithChain) + latestFinalized, err := ht.calculateLatestFinalized(ctx, headWithChain, ht.htConfig.FinalityTagBypass()) if err != nil { return fmt.Errorf("failed to calculate finalized block: %w", err) } @@ -214,6 +215,11 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) Backfill(ctx context.Context, hea return errors.New(errMsg) } + if headWithChain.BlockNumber()-latestFinalized.BlockNumber() > int64(ht.htConfig.MaxAllowedFinalityDepth()) { + return fmt.Errorf("gap between latest finalized block (%d) and current head (%d) is too large (> %d)", + latestFinalized.BlockNumber(), headWithChain.BlockNumber(), ht.htConfig.MaxAllowedFinalityDepth()) + } + return ht.backfill(ctx, headWithChain, latestFinalized) } @@ -335,6 +341,9 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) backfillLoop() { } } +// LatestAndFinalizedBlock - returns latest and latest finalized blocks. +// NOTE: Returns latest finalized block as is, ignoring the FinalityTagBypass feature flag. +// TODO: BCI-3321 use cached values instead of making RPC requests func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) LatestAndFinalizedBlock(ctx context.Context) (latest, finalized HTH, err error) { latest, err = ht.client.HeadByNumber(ctx, nil) if err != nil { @@ -347,7 +356,7 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) LatestAndFinalizedBlock(ctx conte return } - finalized, err = ht.calculateLatestFinalized(ctx, latest) + finalized, err = ht.calculateLatestFinalized(ctx, latest, false) if err != nil { err = fmt.Errorf("failed to calculate latest finalized block: %w", err) return @@ -381,8 +390,8 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) getHeadAtHeight(ctx context.Conte // calculateLatestFinalized - returns latest finalized block. It's expected that currentHeadNumber - is the head of // canonical chain. There is no guaranties that returned block belongs to the canonical chain. Additional verification // must be performed before usage. -func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) calculateLatestFinalized(ctx context.Context, currentHead HTH) (HTH, error) { - if ht.config.FinalityTagEnabled() && !ht.htConfig.FinalityTagBypass() { +func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) calculateLatestFinalized(ctx context.Context, currentHead HTH, finalityTagBypass bool) (HTH, error) { + if ht.config.FinalityTagEnabled() && !finalityTagBypass { latestFinalized, err := ht.client.LatestFinalizedBlock(ctx) if err != nil { return latestFinalized, fmt.Errorf("failed to get latest finalized block: %w", err) @@ -392,11 +401,6 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) calculateLatestFinalized(ctx cont return latestFinalized, fmt.Errorf("failed to get valid latest finalized block") } - if currentHead.BlockNumber()-latestFinalized.BlockNumber() > int64(ht.htConfig.MaxAllowedFinalityDepth()) { - return latestFinalized, fmt.Errorf("gap between latest finalized block (%d) and current head (%d) is too large (> %d)", - latestFinalized.BlockNumber(), currentHead.BlockNumber(), ht.htConfig.MaxAllowedFinalityDepth()) - } - if ht.config.FinalizedBlockOffset() == 0 { return latestFinalized, nil } diff --git a/core/chains/evm/headtracker/head_tracker_test.go b/core/chains/evm/headtracker/head_tracker_test.go index 8e1ff1aa7ba..800ea38c8c0 100644 --- a/core/chains/evm/headtracker/head_tracker_test.go +++ b/core/chains/evm/headtracker/head_tracker_test.go @@ -276,20 +276,6 @@ func TestHeadTracker_Start(t *testing.T) { ht.Start(t) tests.AssertLogEventually(t, ht.observer, "Error handling initial head") }) - t.Run("Logs error if finality gap is too big", func(t *testing.T) { - ht := newHeadTracker(t, opts{FinalityTagEnable: ptr(true), FinalityTagBypass: ptr(false), MaxAllowedFinalityDepth: ptr(uint32(10))}) - head := cltest.Head(1000) - ht.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(head, nil).Once() - ht.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(cltest.Head(989), nil).Once() - ht.ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(nil, errors.New("failed to connect")).Maybe() - ht.Start(t) - tests.AssertEventually(t, func() bool { - // must exactly match the error passed to logger - field := zap.String("err", "failed to calculate latest finalized head: gap between latest finalized block (989) and current head (1000) is too large (> 10)") - filtered := ht.observer.FilterMessage("Error handling initial head").FilterField(field) - return filtered.Len() > 0 - }) - }) t.Run("Happy path (finality tag)", func(t *testing.T) { head := cltest.Head(1000) ht := newHeadTracker(t, opts{FinalityTagEnable: ptr(true), FinalityTagBypass: ptr(false)}) @@ -888,10 +874,11 @@ func TestHeadTracker_Backfill(t *testing.T) { ctx := testutils.Context(t) type opts struct { - Heads []evmtypes.Head - FinalityTagEnabled bool - FinalizedBlockOffset uint32 - FinalityDepth uint32 + Heads []evmtypes.Head + FinalityTagEnabled bool + FinalizedBlockOffset uint32 + FinalityDepth uint32 + MaxAllowedFinalityDepth uint32 } newHeadTrackerUniverse := func(t *testing.T, opts opts) *headTrackerUniverse { cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, secrets *chainlink.Secrets) { @@ -899,6 +886,9 @@ func TestHeadTracker_Backfill(t *testing.T) { c.EVM[0].FinalizedBlockOffset = ptr(opts.FinalizedBlockOffset) c.EVM[0].FinalityDepth = ptr(opts.FinalityDepth) c.EVM[0].HeadTracker.FinalityTagBypass = ptr(false) + if opts.MaxAllowedFinalityDepth > 0 { + c.EVM[0].HeadTracker.MaxAllowedFinalityDepth = ptr(opts.MaxAllowedFinalityDepth) + } }) evmcfg := evmtest.NewChainScopedConfig(t, cfg) @@ -930,6 +920,13 @@ func TestHeadTracker_Backfill(t *testing.T) { err := htu.headTracker.Backfill(ctx, &h12) require.EqualError(t, err, "failed to calculate finalized block: failed to get valid latest finalized block") }) + t.Run("Returns error if finality gap is too big", func(t *testing.T) { + htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true, MaxAllowedFinalityDepth: 2}) + htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&h9, nil).Once() + + err := htu.headTracker.Backfill(ctx, &h12) + require.EqualError(t, err, "gap between latest finalized block (9) and current head (12) is too large (> 2)") + }) t.Run("Returns error if finalized head is ahead of canonical", func(t *testing.T) { htu := newHeadTrackerUniverse(t, opts{FinalityTagEnabled: true}) htu.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&h14Orphaned, nil).Once() @@ -1173,7 +1170,7 @@ func TestHeadTracker_LatestAndFinalizedBlock(t *testing.T) { c.EVM[0].FinalityTagEnabled = ptr(opts.FinalityTagEnabled) c.EVM[0].FinalizedBlockOffset = ptr(opts.FinalizedBlockOffset) c.EVM[0].FinalityDepth = ptr(opts.FinalityDepth) - c.EVM[0].HeadTracker.FinalityTagBypass = ptr(false) + //c.EVM[0].HeadTracker.FinalityTagBypass = ptr(false) }) evmcfg := evmtest.NewChainScopedConfig(t, cfg) From 3e3fced8113c1f622c9a4d4f7a47b1e5cc3c543d Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Wed, 5 Jun 2024 19:53:35 +0200 Subject: [PATCH 37/53] fix comment --- common/client/types.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/client/types.go b/common/client/types.go index 3e630b93dc0..72670bc328d 100644 --- a/common/client/types.go +++ b/common/client/types.go @@ -75,9 +75,9 @@ type NodeClient[ UnsubscribeAllExceptAliveLoop() IsSyncing(ctx context.Context) (bool, error) LatestFinalizedBlock(ctx context.Context) (HEAD, error) - // GetInterceptedChainInfo - returns latest and highest ChainInfo. - // Latest ChainInfo is the most recent value received within a NodeClient's current lifecycle between Dial and DisconnectAll. - // Highest ChainInfo is the highest ChainInfo observed by the NodeClient since the instance was created. + // 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. + // appLayerObservations ChainInfo is the highest ChainInfo observed excluding health checks calls. // Its values must not be reset. // The results of corresponding calls, to get the most recent head and the latest finalized head, must be // intercepted and reflected in ChainInfo before being returned to a caller. Otherwise, MultiNode is not able to @@ -85,7 +85,7 @@ type NodeClient[ // DisconnectAll must reset latest ChainInfo to default value. // Ensure implementation does not have a race condition when values are reset before request completion and as // a result latest ChainInfo contains information from the previous cycle. - GetInterceptedChainInfo() (latest, highest ChainInfo) + GetInterceptedChainInfo() (latest, appLayerObservations ChainInfo) } // clientAPI includes all the direct RPC methods required by the generalized common client to implement its own. From 71b52511732bfd551ce1ecf3601572fbcbdb0fd4 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Wed, 5 Jun 2024 21:11:57 +0200 Subject: [PATCH 38/53] reset keystone to develop --- .../keystone/generated/forwarder/forwarder.go | 56 ++--- .../keystone_capability_registry.go | 232 +++++++++++++----- ...rapper-dependency-versions-do-not-edit.txt | 4 +- 3 files changed, 196 insertions(+), 96 deletions(-) diff --git a/core/gethwrappers/keystone/generated/forwarder/forwarder.go b/core/gethwrappers/keystone/generated/forwarder/forwarder.go index fbdd024c64f..05ee44b415b 100644 --- a/core/gethwrappers/keystone/generated/forwarder/forwarder.go +++ b/core/gethwrappers/keystone/generated/forwarder/forwarder.go @@ -15,6 +15,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/event" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" ) @@ -31,8 +32,8 @@ var ( ) var KeystoneForwarderMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"DuplicateSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxSigners\",\"type\":\"uint256\"}],\"name\":\"ExcessSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FaultToleranceMustBePositive\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minSigners\",\"type\":\"uint256\"}],\"name\":\"InsufficientSigners\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"InvalidDonId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"InvalidSignature\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"received\",\"type\":\"uint256\"}],\"name\":\"InvalidSignatureCount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrantCall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"reportId\",\"type\":\"bytes32\"}],\"name\":\"ReportAlreadyProcessed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"workflowOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"result\",\"type\":\"bool\"}],\"name\":\"ReportProcessed\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"}],\"name\":\"getTransmitter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiverAddress\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"rawReport\",\"type\":\"bytes\"},{\"internalType\":\"bytes[]\",\"name\":\"signatures\",\"type\":\"bytes[]\"}],\"name\":\"report\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", - Bin: "0x608060405234801561001057600080fd5b5033806000816100675760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615610097576100978161009f565b505050610148565b336001600160a01b038216036100f75760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161005e565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b61151d806101576000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c806379ba50971161005b57806379ba5097146101175780638da5cb5b1461011f578063c0965dc31461013d578063f2fde38b1461015057600080fd5b8063134a46f014610082578063181f5a7714610097578063390d0b15146100df575b600080fd5b610095610090366004611106565b610163565b005b604080518082018252601781527f4b657973746f6e65466f7277617264657220312e302e30000000000000000000602082015290516100d691906111de565b60405180910390f35b6100f26100ed366004611221565b61057d565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100d6565b61009561060e565b60005473ffffffffffffffffffffffffffffffffffffffff166100f2565b61009561014b36600461124b565b61070b565b61009561015e3660046112fa565b610ded565b60015474010000000000000000000000000000000000000000900460ff16156101b8576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000017905560ff8316600003610234576040517f0743bae600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601f81111561027e576040517f61750f4000000000000000000000000000000000000000000000000000000000815260048101829052601f60248201526044015b60405180910390fd5b610289836003611344565b60ff1681116102e7578061029e846003611344565b6102a9906001611367565b6040517f9dd9e6d8000000000000000000000000000000000000000000000000000000008152600481019290925260ff166024820152604401610275565b60005b63ffffffff85166000908152600260205260409020600101548110156103885763ffffffff8516600090815260026020526040812060010180548390811061033457610334611380565b600091825260208083209091015463ffffffff891683526002808352604080852073ffffffffffffffffffffffffffffffffffffffff9093168552910190915281205550610381816113af565b90506102ea565b5063ffffffff841660009081526002602052604090206103ac906001018383610ffe565b5060005b8181101561050a5760008383838181106103cc576103cc611380565b90506020020160208101906103e191906112fa565b63ffffffff8716600090815260026020818152604080842073ffffffffffffffffffffffffffffffffffffffff8616855290920190529020549091501561046c576040517fe021c4f200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610275565b610477826001611367565b63ffffffff8716600090815260026020818152604080842073ffffffffffffffffffffffffffffffffffffffff909616808552868401835290842060ff959095169094559081526001938401805494850181558252902090910180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169091179055610503816113af565b90506103b0565b50505063ffffffff91909116600090815260026020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff169055565b6000806105df84846040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084901b1660208201526034810182905260009060540160405160208183030381529060405280519060200120905092915050565b60009081526003602052604090205473ffffffffffffffffffffffffffffffffffffffff169150505b92915050565b60015473ffffffffffffffffffffffffffffffffffffffff16331461068f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610275565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b60015474010000000000000000000000000000000000000000900460ff1615610760576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000017905560588310156107da576040517fb55ac75400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060008061081f88888080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610e0192505050565b63ffffffff8316600090815260026020526040812054949850929650909450925060ff9091169003610885576040517fea1b312900000000000000000000000000000000000000000000000000000000815263ffffffff84166004820152602401610275565b604080517fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060608c901b1660208083019190915260348083018690528351808403909101815260549092018352815191810191909120600081815260039092529190205473ffffffffffffffffffffffffffffffffffffffff1615610939576040517f1aac3d2900000000000000000000000000000000000000000000000000000000815260048101829052602401610275565b63ffffffff8416600090815260026020526040902054869061095f9060ff166001611367565b60ff16146109ca5763ffffffff841660009081526002602052604090205461098b9060ff166001611367565b6040517fd6022e8e00000000000000000000000000000000000000000000000000000000815260ff909116600482015260248101879052604401610275565b600089896040516109dc9291906113e7565b604051809103902090506109ee611086565b6000805b89811015610c5c576000806000610a608e8e86818110610a1457610a14611380565b9050602002810190610a2691906113f7565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610e2692505050565b925092509250600060018883868660405160008152602001604052604051610aa4949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa158015610ac6573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015163ffffffff8f1660009081526002602081815284832073ffffffffffffffffffffffffffffffffffffffff851684529091019052918220549850925060ff881690039050610b81576040517fbf18af4300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610275565b610b8c60018761145c565b955060008760ff8816601f8110610ba557610ba5611380565b602002015173ffffffffffffffffffffffffffffffffffffffff1614610c0f576040517fe021c4f200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610275565b80878760ff16601f8110610c2557610c25611380565b73ffffffffffffffffffffffffffffffffffffffff909216602092909202015250610c5592508391506113af9050565b90506109f2565b5050505060008a905060008173ffffffffffffffffffffffffffffffffffffffff1663ff5a027088868e8e6058908092610c9893929190611475565b6040518563ffffffff1660e01b8152600401610cb7949392919061149f565b600060405180830381600087803b158015610cd157600080fd5b505af1925050508015610ce2575060015b15610ceb575060015b604080518082018252338152821515602080830191825260008781526003909152839020915182549151151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00000000000000000000000000000000000000000090921673ffffffffffffffffffffffffffffffffffffffff91821617919091179091559051869186811691908f16907fdae8e752043eb5fc7e4a6eced57ceaf159548b630125ece9ffc41cfc952c208190610daf90861515815260200190565b60405180910390a45050600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16905550505050505050505050565b610df5610e86565b610dfe81610f09565b50565b602081015160408201516044830151606490930151919360e09190911c929160601c90565b60008060006041845114610e6857836040517f2adfdc3000000000000000000000000000000000000000000000000000000000815260040161027591906111de565b50505060208101516040820151606090920151909260009190911a90565b60005473ffffffffffffffffffffffffffffffffffffffff163314610f07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610275565b565b3373ffffffffffffffffffffffffffffffffffffffff821603610f88576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610275565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b828054828255906000526020600020908101928215611076579160200282015b828111156110765781547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84351617825560209092019160019091019061101e565b506110829291506110a5565b5090565b604051806103e00160405280601f906020820280368337509192915050565b5b8082111561108257600081556001016110a6565b60008083601f8401126110cc57600080fd5b50813567ffffffffffffffff8111156110e457600080fd5b6020830191508360208260051b85010111156110ff57600080fd5b9250929050565b6000806000806060858703121561111c57600080fd5b843563ffffffff8116811461113057600080fd5b9350602085013560ff8116811461114657600080fd5b9250604085013567ffffffffffffffff81111561116257600080fd5b61116e878288016110ba565b95989497509550505050565b6000815180845260005b818110156111a057602081850181015186830182015201611184565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815260006111f1602083018461117a565b9392505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461121c57600080fd5b919050565b6000806040838503121561123457600080fd5b61123d836111f8565b946020939093013593505050565b60008060008060006060868803121561126357600080fd5b61126c866111f8565b9450602086013567ffffffffffffffff8082111561128957600080fd5b818801915088601f83011261129d57600080fd5b8135818111156112ac57600080fd5b8960208285010111156112be57600080fd5b6020830196508095505060408801359150808211156112dc57600080fd5b506112e9888289016110ba565b969995985093965092949392505050565b60006020828403121561130c57600080fd5b6111f1826111f8565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60ff818116838216029081169081811461136057611360611315565b5092915050565b60ff818116838216019081111561060857610608611315565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036113e0576113e0611315565b5060010190565b8183823760009101908152919050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261142c57600080fd5b83018035915067ffffffffffffffff82111561144757600080fd5b6020019150368190038213156110ff57600080fd5b60ff828116828216039081111561060857610608611315565b6000808585111561148557600080fd5b8386111561149257600080fd5b5050820193919092039150565b84815273ffffffffffffffffffffffffffffffffffffffff8416602082015260606040820152816060820152818360808301376000818301608090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160101939250505056fea164736f6c6343000813000a", + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"}],\"name\":\"AlreadyProcessed\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"DuplicateSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxSigners\",\"type\":\"uint256\"}],\"name\":\"ExcessSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FaultToleranceMustBePositive\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minSigners\",\"type\":\"uint256\"}],\"name\":\"InsufficientSigners\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"InvalidDonId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"InvalidSignature\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"received\",\"type\":\"uint256\"}],\"name\":\"InvalidSignatureCount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"InvalidVersion\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrantCall\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"result\",\"type\":\"bool\"}],\"name\":\"ReportProcessed\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes2\",\"name\":\"reportId\",\"type\":\"bytes2\"}],\"name\":\"getTransmitter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiverAddress\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"rawReport\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"reportContext\",\"type\":\"bytes\"},{\"internalType\":\"bytes[]\",\"name\":\"signatures\",\"type\":\"bytes[]\"}],\"name\":\"report\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b5033806000816100675760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615610097576100978161009f565b505050610148565b336001600160a01b038216036100f75760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161005e565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6115ec806101576000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c806379ba50971161005b57806379ba5097146100f25780638864b864146100fa5780638da5cb5b146101d3578063f2fde38b146101f157600080fd5b80631128956514610082578063134a46f014610097578063181f5a77146100aa575b600080fd5b61009561009036600461119d565b610204565b005b6100956100a5366004611248565b61093e565b604080518082018252601781527f4b657973746f6e65466f7277617264657220312e302e30000000000000000000602082015290516100e99190611320565b60405180910390f35b610095610c9f565b6101ae61010836600461133a565b6040805160609490941b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660208086019190915260348501939093527fffff000000000000000000000000000000000000000000000000000000000000919091166054840152805160368185030181526056909301815282519282019290922060009081526003909152205473ffffffffffffffffffffffffffffffffffffffff1690565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100e9565b60005473ffffffffffffffffffffffffffffffffffffffff166101ae565b6100956101ff36600461139f565b610d9c565b60015474010000000000000000000000000000000000000000900460ff1615610259576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000179055606d8510156102d3576040517fb55ac75400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080600061031789898080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610db092505050565b63ffffffff831660009081526002602052604081205494975092955093505060ff9091169003610380576040517fea1b312900000000000000000000000000000000000000000000000000000000815263ffffffff831660048201526024015b60405180910390fd5b604080517fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060608d901b16602080830191909152603482018690527fffff000000000000000000000000000000000000000000000000000000000000841660548301528251808303603601815260569092018352815191810191909120600081815260039092529190205473ffffffffffffffffffffffffffffffffffffffff161561045b576040517f1a20d3e600000000000000000000000000000000000000000000000000000000815260048101829052602401610377565b63ffffffff831660009081526002602052604090205485906104819060ff1660016113e9565b60ff16146104ec5763ffffffff83166000908152600260205260409020546104ad9060ff1660016113e9565b6040517fd6022e8e00000000000000000000000000000000000000000000000000000000815260ff909116600482015260248101869052604401610377565b60008a8a6040516104fe929190611408565b604051908190038120610517918b908b90602001611418565b60405160208183030381529060405280519060200120905061053761102a565b6000805b888110156107a55760008060006105a98d8d8681811061055d5761055d611432565b905060200281019061056f9190611461565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610e5292505050565b9194509250905060006001886105c084601b6113e9565b6040805160008152602081018083529390935260ff90911690820152606081018690526080810185905260a0016020604051602081039080840390855afa15801561060f573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015163ffffffff8e1660009081526002602081815284832073ffffffffffffffffffffffffffffffffffffffff851684529091019052918220549850925060ff8816900390506106ca576040517fbf18af4300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610377565b6106d56001876114c6565b955060008760ff8816601f81106106ee576106ee611432565b602002015173ffffffffffffffffffffffffffffffffffffffff1614610758576040517fe021c4f200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610377565b80878760ff16601f811061076e5761076e611432565b73ffffffffffffffffffffffffffffffffffffffff90921660209290920201525061079e92508391506114df9050565b905061053b565b5050505060008b73ffffffffffffffffffffffffffffffffffffffff1663805f21328c8c602d90606d926107db93929190611517565b8e8e606d9080926107ee93929190611517565b6040518563ffffffff1660e01b815260040161080d949392919061158a565b600060405180830381600087803b15801561082757600080fd5b505af1925050508015610838575060015b15610841575060015b604080518082018252338152821515602080830191825260008681526003909152839020915182549151151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00000000000000000000000000000000000000000090921673ffffffffffffffffffffffffffffffffffffffff9182161791909117909155905186918e16907fbe015fd2fd7c1a00158e111095c794ae7030eb413d2a0990e5b78d3114df1d499061090090851515815260200190565b60405180910390a35050600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16905550505050505050505050565b610946610eb2565b8260ff16600003610983576040517f0743bae600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601f8111156109c8576040517f61750f4000000000000000000000000000000000000000000000000000000000815260048101829052601f6024820152604401610377565b6109d38360036115bc565b60ff168111610a3157806109e88460036115bc565b6109f39060016113e9565b6040517f9dd9e6d8000000000000000000000000000000000000000000000000000000008152600481019290925260ff166024820152604401610377565b60005b63ffffffff8516600090815260026020526040902060010154811015610ad25763ffffffff85166000908152600260205260408120600101805483908110610a7e57610a7e611432565b600091825260208083209091015463ffffffff891683526002808352604080852073ffffffffffffffffffffffffffffffffffffffff9093168552910190915281205550610acb816114df565b9050610a34565b5063ffffffff84166000908152600260205260409020610af6906001018383611049565b5060005b81811015610c54576000838383818110610b1657610b16611432565b9050602002016020810190610b2b919061139f565b63ffffffff8716600090815260026020818152604080842073ffffffffffffffffffffffffffffffffffffffff86168552909201905290205490915015610bb6576040517fe021c4f200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610377565b610bc18260016113e9565b63ffffffff8716600090815260026020818152604080842073ffffffffffffffffffffffffffffffffffffffff909616808552868401835290842060ff959095169094559081526001938401805494850181558252902090910180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169091179055610c4d816114df565b9050610afa565b50505063ffffffff91909116600090815260026020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055565b60015473ffffffffffffffffffffffffffffffffffffffff163314610d20576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610377565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610da4610eb2565b610dad81610f35565b50565b60008060008084600081518110610dc957610dc9611432565b60209101015160f81c600114610e2a5784600081518110610dec57610dec611432565b01602001516040517f7207be2000000000000000000000000000000000000000000000000000000000815260f89190911c6004820152602401610377565b50505050602181015160458201516049830151608b90930151919360e091821c9390911c9190565b60008060006041845114610e9457836040517f2adfdc300000000000000000000000000000000000000000000000000000000081526004016103779190611320565b50505060208101516040820151606090920151909260009190911a90565b60005473ffffffffffffffffffffffffffffffffffffffff163314610f33576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610377565b565b3373ffffffffffffffffffffffffffffffffffffffff821603610fb4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610377565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b604051806103e00160405280601f906020820280368337509192915050565b8280548282559060005260206000209081019282156110c1579160200282015b828111156110c15781547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff843516178255602090920191600190910190611069565b506110cd9291506110d1565b5090565b5b808211156110cd57600081556001016110d2565b803573ffffffffffffffffffffffffffffffffffffffff8116811461110a57600080fd5b919050565b60008083601f84011261112157600080fd5b50813567ffffffffffffffff81111561113957600080fd5b60208301915083602082850101111561115157600080fd5b9250929050565b60008083601f84011261116a57600080fd5b50813567ffffffffffffffff81111561118257600080fd5b6020830191508360208260051b850101111561115157600080fd5b60008060008060008060006080888a0312156111b857600080fd5b6111c1886110e6565b9650602088013567ffffffffffffffff808211156111de57600080fd5b6111ea8b838c0161110f565b909850965060408a013591508082111561120357600080fd5b61120f8b838c0161110f565b909650945060608a013591508082111561122857600080fd5b506112358a828b01611158565b989b979a50959850939692959293505050565b6000806000806060858703121561125e57600080fd5b843563ffffffff8116811461127257600080fd5b9350602085013560ff8116811461128857600080fd5b9250604085013567ffffffffffffffff8111156112a457600080fd5b6112b087828801611158565b95989497509550505050565b6000815180845260005b818110156112e2576020818501810151868301820152016112c6565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b60208152600061133360208301846112bc565b9392505050565b60008060006060848603121561134f57600080fd5b611358846110e6565b92506020840135915060408401357fffff0000000000000000000000000000000000000000000000000000000000008116811461139457600080fd5b809150509250925092565b6000602082840312156113b157600080fd5b611333826110e6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60ff8181168382160190811115611402576114026113ba565b92915050565b8183823760009101908152919050565b838152818360208301376000910160200190815292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261149657600080fd5b83018035915067ffffffffffffffff8211156114b157600080fd5b60200191503681900382131561115157600080fd5b60ff8281168282160390811115611402576114026113ba565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203611510576115106113ba565b5060010190565b6000808585111561152757600080fd5b8386111561153457600080fd5b5050820193919092039150565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b60408152600061159e604083018688611541565b82810360208401526115b1818587611541565b979650505050505050565b60ff81811683821602908116908181146115d8576115d86113ba565b509291505056fea164736f6c6343000813000a", } var KeystoneForwarderABI = KeystoneForwarderMetaData.ABI @@ -171,9 +172,9 @@ func (_KeystoneForwarder *KeystoneForwarderTransactorRaw) Transact(opts *bind.Tr return _KeystoneForwarder.Contract.contract.Transact(opts, method, params...) } -func (_KeystoneForwarder *KeystoneForwarderCaller) GetTransmitter(opts *bind.CallOpts, receiver common.Address, workflowExecutionId [32]byte) (common.Address, error) { +func (_KeystoneForwarder *KeystoneForwarderCaller) GetTransmitter(opts *bind.CallOpts, receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (common.Address, error) { var out []interface{} - err := _KeystoneForwarder.contract.Call(opts, &out, "getTransmitter", receiver, workflowExecutionId) + err := _KeystoneForwarder.contract.Call(opts, &out, "getTransmitter", receiver, workflowExecutionId, reportId) if err != nil { return *new(common.Address), err @@ -185,12 +186,12 @@ func (_KeystoneForwarder *KeystoneForwarderCaller) GetTransmitter(opts *bind.Cal } -func (_KeystoneForwarder *KeystoneForwarderSession) GetTransmitter(receiver common.Address, workflowExecutionId [32]byte) (common.Address, error) { - return _KeystoneForwarder.Contract.GetTransmitter(&_KeystoneForwarder.CallOpts, receiver, workflowExecutionId) +func (_KeystoneForwarder *KeystoneForwarderSession) GetTransmitter(receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (common.Address, error) { + return _KeystoneForwarder.Contract.GetTransmitter(&_KeystoneForwarder.CallOpts, receiver, workflowExecutionId, reportId) } -func (_KeystoneForwarder *KeystoneForwarderCallerSession) GetTransmitter(receiver common.Address, workflowExecutionId [32]byte) (common.Address, error) { - return _KeystoneForwarder.Contract.GetTransmitter(&_KeystoneForwarder.CallOpts, receiver, workflowExecutionId) +func (_KeystoneForwarder *KeystoneForwarderCallerSession) GetTransmitter(receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (common.Address, error) { + return _KeystoneForwarder.Contract.GetTransmitter(&_KeystoneForwarder.CallOpts, receiver, workflowExecutionId, reportId) } func (_KeystoneForwarder *KeystoneForwarderCaller) Owner(opts *bind.CallOpts) (common.Address, error) { @@ -249,16 +250,16 @@ func (_KeystoneForwarder *KeystoneForwarderTransactorSession) AcceptOwnership() return _KeystoneForwarder.Contract.AcceptOwnership(&_KeystoneForwarder.TransactOpts) } -func (_KeystoneForwarder *KeystoneForwarderTransactor) Report(opts *bind.TransactOpts, receiverAddress common.Address, rawReport []byte, signatures [][]byte) (*types.Transaction, error) { - return _KeystoneForwarder.contract.Transact(opts, "report", receiverAddress, rawReport, signatures) +func (_KeystoneForwarder *KeystoneForwarderTransactor) Report(opts *bind.TransactOpts, receiverAddress common.Address, rawReport []byte, reportContext []byte, signatures [][]byte) (*types.Transaction, error) { + return _KeystoneForwarder.contract.Transact(opts, "report", receiverAddress, rawReport, reportContext, signatures) } -func (_KeystoneForwarder *KeystoneForwarderSession) Report(receiverAddress common.Address, rawReport []byte, signatures [][]byte) (*types.Transaction, error) { - return _KeystoneForwarder.Contract.Report(&_KeystoneForwarder.TransactOpts, receiverAddress, rawReport, signatures) +func (_KeystoneForwarder *KeystoneForwarderSession) Report(receiverAddress common.Address, rawReport []byte, reportContext []byte, signatures [][]byte) (*types.Transaction, error) { + return _KeystoneForwarder.Contract.Report(&_KeystoneForwarder.TransactOpts, receiverAddress, rawReport, reportContext, signatures) } -func (_KeystoneForwarder *KeystoneForwarderTransactorSession) Report(receiverAddress common.Address, rawReport []byte, signatures [][]byte) (*types.Transaction, error) { - return _KeystoneForwarder.Contract.Report(&_KeystoneForwarder.TransactOpts, receiverAddress, rawReport, signatures) +func (_KeystoneForwarder *KeystoneForwarderTransactorSession) Report(receiverAddress common.Address, rawReport []byte, reportContext []byte, signatures [][]byte) (*types.Transaction, error) { + return _KeystoneForwarder.Contract.Report(&_KeystoneForwarder.TransactOpts, receiverAddress, rawReport, reportContext, signatures) } func (_KeystoneForwarder *KeystoneForwarderTransactor) SetConfig(opts *bind.TransactOpts, donId uint32, f uint8, signers []common.Address) (*types.Transaction, error) { @@ -619,50 +620,41 @@ func (it *KeystoneForwarderReportProcessedIterator) Close() error { type KeystoneForwarderReportProcessed struct { Receiver common.Address - WorkflowOwner common.Address WorkflowExecutionId [32]byte Result bool Raw types.Log } -func (_KeystoneForwarder *KeystoneForwarderFilterer) FilterReportProcessed(opts *bind.FilterOpts, receiver []common.Address, workflowOwner []common.Address, workflowExecutionId [][32]byte) (*KeystoneForwarderReportProcessedIterator, error) { +func (_KeystoneForwarder *KeystoneForwarderFilterer) FilterReportProcessed(opts *bind.FilterOpts, receiver []common.Address, workflowExecutionId [][32]byte) (*KeystoneForwarderReportProcessedIterator, error) { var receiverRule []interface{} for _, receiverItem := range receiver { receiverRule = append(receiverRule, receiverItem) } - var workflowOwnerRule []interface{} - for _, workflowOwnerItem := range workflowOwner { - workflowOwnerRule = append(workflowOwnerRule, workflowOwnerItem) - } var workflowExecutionIdRule []interface{} for _, workflowExecutionIdItem := range workflowExecutionId { workflowExecutionIdRule = append(workflowExecutionIdRule, workflowExecutionIdItem) } - logs, sub, err := _KeystoneForwarder.contract.FilterLogs(opts, "ReportProcessed", receiverRule, workflowOwnerRule, workflowExecutionIdRule) + logs, sub, err := _KeystoneForwarder.contract.FilterLogs(opts, "ReportProcessed", receiverRule, workflowExecutionIdRule) if err != nil { return nil, err } return &KeystoneForwarderReportProcessedIterator{contract: _KeystoneForwarder.contract, event: "ReportProcessed", logs: logs, sub: sub}, nil } -func (_KeystoneForwarder *KeystoneForwarderFilterer) WatchReportProcessed(opts *bind.WatchOpts, sink chan<- *KeystoneForwarderReportProcessed, receiver []common.Address, workflowOwner []common.Address, workflowExecutionId [][32]byte) (event.Subscription, error) { +func (_KeystoneForwarder *KeystoneForwarderFilterer) WatchReportProcessed(opts *bind.WatchOpts, sink chan<- *KeystoneForwarderReportProcessed, receiver []common.Address, workflowExecutionId [][32]byte) (event.Subscription, error) { var receiverRule []interface{} for _, receiverItem := range receiver { receiverRule = append(receiverRule, receiverItem) } - var workflowOwnerRule []interface{} - for _, workflowOwnerItem := range workflowOwner { - workflowOwnerRule = append(workflowOwnerRule, workflowOwnerItem) - } var workflowExecutionIdRule []interface{} for _, workflowExecutionIdItem := range workflowExecutionId { workflowExecutionIdRule = append(workflowExecutionIdRule, workflowExecutionIdItem) } - logs, sub, err := _KeystoneForwarder.contract.WatchLogs(opts, "ReportProcessed", receiverRule, workflowOwnerRule, workflowExecutionIdRule) + logs, sub, err := _KeystoneForwarder.contract.WatchLogs(opts, "ReportProcessed", receiverRule, workflowExecutionIdRule) if err != nil { return nil, err } @@ -726,7 +718,7 @@ func (KeystoneForwarderOwnershipTransferred) Topic() common.Hash { } func (KeystoneForwarderReportProcessed) Topic() common.Hash { - return common.HexToHash("0xdae8e752043eb5fc7e4a6eced57ceaf159548b630125ece9ffc41cfc952c2081") + return common.HexToHash("0xbe015fd2fd7c1a00158e111095c794ae7030eb413d2a0990e5b78d3114df1d49") } func (_KeystoneForwarder *KeystoneForwarder) Address() common.Address { @@ -734,7 +726,7 @@ func (_KeystoneForwarder *KeystoneForwarder) Address() common.Address { } type KeystoneForwarderInterface interface { - GetTransmitter(opts *bind.CallOpts, receiver common.Address, workflowExecutionId [32]byte) (common.Address, error) + GetTransmitter(opts *bind.CallOpts, receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (common.Address, error) Owner(opts *bind.CallOpts) (common.Address, error) @@ -742,7 +734,7 @@ type KeystoneForwarderInterface interface { AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) - Report(opts *bind.TransactOpts, receiverAddress common.Address, rawReport []byte, signatures [][]byte) (*types.Transaction, error) + Report(opts *bind.TransactOpts, receiverAddress common.Address, rawReport []byte, reportContext []byte, signatures [][]byte) (*types.Transaction, error) SetConfig(opts *bind.TransactOpts, donId uint32, f uint8, signers []common.Address) (*types.Transaction, error) @@ -760,9 +752,9 @@ type KeystoneForwarderInterface interface { ParseOwnershipTransferred(log types.Log) (*KeystoneForwarderOwnershipTransferred, error) - FilterReportProcessed(opts *bind.FilterOpts, receiver []common.Address, workflowOwner []common.Address, workflowExecutionId [][32]byte) (*KeystoneForwarderReportProcessedIterator, error) + FilterReportProcessed(opts *bind.FilterOpts, receiver []common.Address, workflowExecutionId [][32]byte) (*KeystoneForwarderReportProcessedIterator, error) - WatchReportProcessed(opts *bind.WatchOpts, sink chan<- *KeystoneForwarderReportProcessed, receiver []common.Address, workflowOwner []common.Address, workflowExecutionId [][32]byte) (event.Subscription, error) + WatchReportProcessed(opts *bind.WatchOpts, sink chan<- *KeystoneForwarderReportProcessed, receiver []common.Address, workflowExecutionId [][32]byte) (event.Subscription, error) ParseReportProcessed(log types.Log) (*KeystoneForwarderReportProcessed, error) diff --git a/core/gethwrappers/keystone/generated/keystone_capability_registry/keystone_capability_registry.go b/core/gethwrappers/keystone/generated/keystone_capability_registry/keystone_capability_registry.go index 3357bf8f5fe..ab4b89d18e1 100644 --- a/core/gethwrappers/keystone/generated/keystone_capability_registry/keystone_capability_registry.go +++ b/core/gethwrappers/keystone/generated/keystone_capability_registry/keystone_capability_registry.go @@ -15,6 +15,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/event" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" ) @@ -42,21 +43,29 @@ type CapabilityRegistryCapabilityConfiguration struct { Config []byte } -type CapabilityRegistryNodeOperator struct { - Admin common.Address - Name string +type CapabilityRegistryDONInfo struct { + Id uint32 + ConfigCount uint32 + IsPublic bool + NodeP2PIds [][32]byte + CapabilityConfigurations []CapabilityRegistryCapabilityConfiguration } -type CapabilityRegistryNodeParams struct { +type CapabilityRegistryNodeInfo struct { NodeOperatorId uint32 - Signer common.Address + Signer [32]byte P2pId [32]byte HashedCapabilityIds [][32]byte } +type CapabilityRegistryNodeOperator struct { + Admin common.Address + Name string +} + var CapabilityRegistryMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"name\":\"AccessForbidden\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CapabilityAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityIsDeprecated\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONCapability\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONNode\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"proposedConfigurationContract\",\"type\":\"address\"}],\"name\":\"InvalidCapabilityConfigurationContractInterface\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"name\":\"InvalidNodeCapabilities\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeOperatorAdmin\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"InvalidNodeP2PId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"lengthOne\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lengthTwo\",\"type\":\"uint256\"}],\"name\":\"LengthMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"NodeDoesNotSupportCapability\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDeprecated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"donId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"}],\"name\":\"DONAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"}],\"name\":\"NodeAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"}],\"name\":\"NodeOperatorRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"NodeRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"NodeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability\",\"name\":\"capability\",\"type\":\"tuple\"}],\"name\":\"addCapability\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"nodes\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"}],\"name\":\"addDON\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"addNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeParams[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"addNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"deprecateCapability\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCapabilities\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedId\",\"type\":\"bytes32\"}],\"name\":\"getCapability\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"getDON\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"},{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"getDONCapabilityConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"}],\"name\":\"getHashedCapabilityId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"getNode\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeParams\",\"name\":\"\",\"type\":\"tuple\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"}],\"name\":\"getNodeOperator\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"isCapabilityDeprecated\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint256[]\"}],\"name\":\"removeNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"removedNodeP2PIds\",\"type\":\"bytes32[]\"}],\"name\":\"removeNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"updateNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeParams[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"updateNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x6080604052600b805463ffffffff191660011790553480156200002157600080fd5b503380600081620000795760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000ac57620000ac81620000b5565b50505062000160565b336001600160a01b038216036200010f5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000070565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6134b380620001706000396000f3fe608060405234801561001057600080fd5b50600436106101775760003560e01c806350e03b16116100d85780638da5cb5b1161008c578063b06e07a711610066578063b06e07a71461038f578063ddbe4f82146103a2578063f2fde38b146103b757600080fd5b80638da5cb5b146103345780639cb7c5f41461035c578063ae3c241c1461037c57600080fd5b806365c14dc7116100bd57806365c14dc7146102f95780636ae5c5911461031957806379ba50971461032c57600080fd5b806350e03b16146102d35780635840cd45146102e657600080fd5b8063235374051161012f57806336b402fb1161011457806336b402fb14610257578063398f37731461029f57806350c946fe146102b257600080fd5b806323537405146102215780632c01a1e81461024457600080fd5b8063125700111161016057806312570011146101a4578063181f5a77146101cc5780631cdf63431461020e57600080fd5b80630c5801e31461017c578063117392ce14610191575b600080fd5b61018f61018a3660046126db565b6103ca565b005b61018f61019f366004612747565b6106db565b6101b76101b236600461275f565b610926565b60405190151581526020015b60405180910390f35b60408051808201909152601881527f4361706162696c697479526567697374727920312e302e30000000000000000060208201525b6040516101c391906127dc565b61018f61021c3660046127ef565b610939565b61023461022f36600461284a565b6109fc565b6040516101c394939291906128a0565b61018f6102523660046127ef565b610c0f565b610291610265366004612958565b604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b6040519081526020016101c3565b61018f6102ad3660046127ef565b610e8d565b6102c56102c036600461275f565b611026565b6040516101c392919061297a565b61018f6102e13660046127ef565b6110e8565b61018f6102f43660046127ef565b6115c9565b61030c61030736600461275f565b611a63565b6040516101c39190612a1c565b61018f610327366004612a6d565b611b49565b61018f611f5d565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101c3565b61036f61036a36600461275f565b61205a565b6040516101c39190612b90565b61018f61038a36600461275f565b612104565b61020161039d366004612b9e565b6121cf565b6103aa612287565b6040516101c39190612bc8565b61018f6103c5366004612c38565b6123cc565b828114610412576040517fab8b67c600000000000000000000000000000000000000000000000000000000815260048101849052602481018290526044015b60405180910390fd5b6000805473ffffffffffffffffffffffffffffffffffffffff16905b848110156106d357600086868381811061044a5761044a612c55565b905060200201359050600085858481811061046757610467612c55565b90506020028101906104799190612c84565b61048290612d8c565b805190915073ffffffffffffffffffffffffffffffffffffffff166104d3576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805173ffffffffffffffffffffffffffffffffffffffff16331480159061051057503373ffffffffffffffffffffffffffffffffffffffff851614155b15610547576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805160008381526007602052604090205473ffffffffffffffffffffffffffffffffffffffff90811691161415806105f9575060208082015160405161058d92016127dc565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201206000868152600783529290922091926105e0926001019101612ea5565b6040516020818303038152906040528051906020012014155b156106c0578051600083815260076020908152604090912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9093169290921782558201516001909101906106669082612f94565b50806000015173ffffffffffffffffffffffffffffffffffffffff167f14c8f513e8a6d86d2d16b0cb64976de4e72386c4f8068eca3b7354373f8fe97a8383602001516040516106b79291906130ae565b60405180910390a25b5050806106cc906130f6565b905061042e565b505050505050565b6106e36123e0565b60408051823560208281019190915280840135828401528251808303840181526060909201909252805191012061071b600382612463565b15610752576040517fe288638f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006107646080840160608501612c38565b73ffffffffffffffffffffffffffffffffffffffff16146108cf5761078f6080830160608401612c38565b73ffffffffffffffffffffffffffffffffffffffff163b158061086f57506107bd6080830160608401612c38565b6040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527f884efe6100000000000000000000000000000000000000000000000000000000600482015273ffffffffffffffffffffffffffffffffffffffff91909116906301ffc9a790602401602060405180830381865afa158015610849573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061086d919061312e565b155b156108cf576108846080830160608401612c38565b6040517fabb5e3fd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401610409565b6108da60038261247e565b50600081815260026020526040902082906108f5828261314b565b505060405181907f65610e5677eedff94555572640e442f89848a109ef8593fa927ac30b2565ff0690600090a25050565b6000610933600583612463565b92915050565b6109416123e0565b60005b818110156109f757600083838381811061096057610960612c55565b60209081029290920135600081815260079093526040832080547fffffffffffffffffffffffff00000000000000000000000000000000000000001681559093509190506109b16001830182612641565b50506040518181527f1e5877d7b3001d1569bf733b76c7eceda58bd6c031e5b8d0b7042308ba2e9d4f9060200160405180910390a1506109f0816130f6565b9050610944565b505050565b63ffffffff81166000908152600960205260408120819060609081908390610a269060030161248a565b90506000815167ffffffffffffffff811115610a4457610a44612cc2565b604051908082528060200260200182016040528015610a8a57816020015b604080518082019091526000815260606020820152815260200190600190039081610a625790505b50905060005b8151811015610bc9576040518060400160405280848381518110610ab657610ab6612c55565b60200260200101518152602001600960008b63ffffffff1663ffffffff1681526020019081526020016000206005016000868581518110610af957610af9612c55565b602002602001015181526020019081526020016000208054610b1a90612e58565b80601f0160208091040260200160405190810160405280929190818152602001828054610b4690612e58565b8015610b935780601f10610b6857610100808354040283529160200191610b93565b820191906000526020600020905b815481529060010190602001808311610b7657829003601f168201915b5050505050815250828281518110610bad57610bad612c55565b602002602001018190525080610bc2906130f6565b9050610a90565b5063ffffffff8781166000908152600960205260409020805491821691640100000000900460ff1690610bfe9060010161248a565b919750955093509150509193509193565b6000805473ffffffffffffffffffffffffffffffffffffffff163314905b82811015610e87576000848483818110610c4957610c49612c55565b60209081029290920135600081815260089093526040909220549192505068010000000000000000900473ffffffffffffffffffffffffffffffffffffffff16151580610cc5576040517f64e2ee9200000000000000000000000000000000000000000000000000000000815260048101839052602401610409565b60008281526008602090815260408083205463ffffffff168352600782528083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff1682526001810180549293919291840191610d1f90612e58565b80601f0160208091040260200160405190810160405280929190818152602001828054610d4b90612e58565b8015610d985780601f10610d6d57610100808354040283529160200191610d98565b820191906000526020600020905b815481529060010190602001808311610d7b57829003601f168201915b505050505081525050905084158015610dc85750805173ffffffffffffffffffffffffffffffffffffffff163314155b15610dff576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526008602052604080822080547fffffffff0000000000000000000000000000000000000000000000000000000016815560010191909155517f5254e609a97bab37b7cc79fe128f85c097bd6015c6e1624ae0ba392eb975320590610e6b9085815260200190565b60405180910390a150505080610e80906130f6565b9050610c2d565b50505050565b610e956123e0565b60005b818110156109f7576000838383818110610eb457610eb4612c55565b9050602002810190610ec69190612c84565b610ecf90612d8c565b805190915073ffffffffffffffffffffffffffffffffffffffff16610f20576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600a54604080518082018252835173ffffffffffffffffffffffffffffffffffffffff908116825260208086015181840190815260008681526007909252939020825181547fffffffffffffffffffffffff000000000000000000000000000000000000000016921691909117815591519091906001820190610fa39082612f94565b50905050600a60008154610fb6906130f6565b909155508151602083015160405173ffffffffffffffffffffffffffffffffffffffff909216917fda6697b182650034bd205cdc2dbfabb06bdb3a0a83a2b45bfefa3c4881284e0b9161100b918591906130ae565b60405180910390a250508061101f906130f6565b9050610e98565b604080516080810182526000808252602082018190529181019190915260608082015260408051608081018252600084815260086020908152838220805463ffffffff808216865273ffffffffffffffffffffffffffffffffffffffff6801000000000000000083041684870152600183015486880152640100000000909104168352600201905291822060608201906110bf9061248a565b905260009384526008602052604090932054929364010000000090930463ffffffff1692915050565b60005b818110156109f757600083838381811061110757611107612c55565b905060200281019061111991906131cd565b61112290613201565b9050600061114560005473ffffffffffffffffffffffffffffffffffffffff1690565b825163ffffffff1660009081526007602090815260408083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff908116835260018201805496909116331496509394919390928401916111a290612e58565b80601f01602080910402602001604051908101604052809291908181526020018280546111ce90612e58565b801561121b5780601f106111f05761010080835404028352916020019161121b565b820191906000526020600020905b8154815290600101906020018083116111fe57829003601f168201915b50505050508152505090508115801561124b5750805173ffffffffffffffffffffffffffffffffffffffff163314155b15611282576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408084015160009081526008602052205468010000000000000000900473ffffffffffffffffffffffffffffffffffffffff161515806112f75783604001516040517f64e2ee9200000000000000000000000000000000000000000000000000000000815260040161040991815260200190565b602084015173ffffffffffffffffffffffffffffffffffffffff16611348576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060840151805160000361138a57806040517f3748d4c600000000000000000000000000000000000000000000000000000000815260040161040991906132dd565b60408581015160009081526008602052208054640100000000900463ffffffff169060046113b7836132f0565b82546101009290920a63ffffffff8181021990931691831602179091556040878101516000908152600860205290812054640100000000900490911691505b82518110156114c25761142c83828151811061141457611414612c55565b6020026020010151600361246390919063ffffffff16565b61146457826040517f3748d4c600000000000000000000000000000000000000000000000000000000815260040161040991906132dd565b6114b183828151811061147957611479612c55565b6020908102919091018101516040808b015160009081526008845281812063ffffffff80891683526002909101909452209161247e16565b506114bb816130f6565b90506113f6565b5085516040808801805160009081526008602090815283822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff968716179055825180835284832060010155808b018051845184529285902080547fffffffff0000000000000000000000000000000000000000ffffffffffffffff166801000000000000000073ffffffffffffffffffffffffffffffffffffffff9586160217905592518b5193518551918252939095169085015216908201527f6bbba867c646be512c2f3241e65fdffdefd5528d7e7939649e06e10ee5addc3e9060600160405180910390a1505050505050806115c2906130f6565b90506110eb565b60005b818110156109f75760008383838181106115e8576115e8612c55565b90506020028101906115fa91906131cd565b61160390613201565b9050600061162660005473ffffffffffffffffffffffffffffffffffffffff1690565b825163ffffffff1660009081526007602090815260408083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff9081168352600182018054969091163314965093949193909284019161168390612e58565b80601f01602080910402602001604051908101604052809291908181526020018280546116af90612e58565b80156116fc5780601f106116d1576101008083540402835291602001916116fc565b820191906000526020600020905b8154815290600101906020018083116116df57829003601f168201915b50505050508152505090508115801561172c5750805173ffffffffffffffffffffffffffffffffffffffff163314155b15611763576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408084015160009081526008602052205468010000000000000000900473ffffffffffffffffffffffffffffffffffffffff16151580806117a757506040840151155b156117e65783604001516040517f64e2ee9200000000000000000000000000000000000000000000000000000000815260040161040991815260200190565b602084015173ffffffffffffffffffffffffffffffffffffffff16611837576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060840151805160000361187957806040517f3748d4c600000000000000000000000000000000000000000000000000000000815260040161040991906132dd565b604085810151600090815260086020522080546004906118a690640100000000900463ffffffff166132f0565b82546101009290920a63ffffffff81810219909316918316021790915560408681015160009081526008602052908120546401000000009004909116905b82518110156119605761190283828151811061141457611414612c55565b61193a57826040517f3748d4c600000000000000000000000000000000000000000000000000000000815260040161040991906132dd565b61194f83828151811061147957611479612c55565b50611959816130f6565b90506118e4565b5085516040808801805160009081526008602090815283822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff968716179055825180835284832060010155808b0151835183529184902080547fffffffff0000000000000000000000000000000000000000ffffffffffffffff166801000000000000000073ffffffffffffffffffffffffffffffffffffffff9094169390930292909217909155905189518351918252909316908301527f5bfe8a52ad26ac6ee7b0cd46d2fd92be04735a31c45ef8aa3d4b7ea1b61bbc1f910160405180910390a150505050505080611a5c906130f6565b90506115cc565b6040805180820190915260008152606060208201526000828152600760209081526040918290208251808401909352805473ffffffffffffffffffffffffffffffffffffffff1683526001810180549192840191611ac090612e58565b80601f0160208091040260200160405190810160405280929190818152602001828054611aec90612e58565b8015611b395780601f10611b0e57610100808354040283529160200191611b39565b820191906000526020600020905b815481529060010190602001808311611b1c57829003601f168201915b5050505050815250509050919050565b611b516123e0565b600b5463ffffffff16600081815260096020526040812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000168317640100000000851515021790555b85811015611c76576000878783818110611bb857611bb8612c55565b905060200201359050611bf581600960008663ffffffff1663ffffffff16815260200190815260200160002060010161246390919063ffffffff16565b15611c3b576040517f636e405700000000000000000000000000000000000000000000000000000000815263ffffffff8416600482015260248101829052604401610409565b63ffffffff8084166000908152600960205260409020611c6391600190910190839061247e16565b505080611c6f906130f6565b9050611b9c565b5060005b83811015611ed45736858583818110611c9557611c95612c55565b9050602002810190611ca79190612c84565b90508035611cb6600382612463565b611cef576040517fe181733f00000000000000000000000000000000000000000000000000000000815260048101829052602401610409565b611cfa600582612463565b15611d34576040517ff7d7a29400000000000000000000000000000000000000000000000000000000815260048101829052602401610409565b63ffffffff8085166000908152600960205260409020611d5c91600390910190839061246316565b15611da2576040517f3927d08000000000000000000000000000000000000000000000000000000000815263ffffffff8516600482015260248101829052604401610409565b60005b88811015611e5a5760008a8a83818110611dc157611dc1612c55565b602090810292909201356000818152600884526040808220805463ffffffff6401000000009091048116845260029091019095529020909350611e0992909150859061246316565b611e49576040517fa7e792500000000000000000000000000000000000000000000000000000000081526004810182905260248101849052604401610409565b50611e53816130f6565b9050611da5565b5063ffffffff8085166000908152600960205260409020611e8391600390910190839061247e16565b50611e916020830183613313565b63ffffffff86166000908152600960209081526040808320868452600501909152902091611ec0919083613378565b50505080611ecd906130f6565b9050611c7a565b50600b8054600090611eeb9063ffffffff166132f0565b91906101000a81548163ffffffff021916908363ffffffff1602179055507fab55f4c8fb4335a586285ae209d1f1e17a7ccb22e1131963624434d98c8546a58183604051611f4d92919063ffffffff9290921682521515602082015260400190565b60405180910390a1505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611fde576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610409565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b604080516080808201835260008083526020808401829052838501829052606084018290528582526002808252918590208551938401865280548452600180820154928501929092529182015493949293919284019160ff16908111156120c3576120c3612af1565b60018111156120d4576120d4612af1565b815260029190910154610100900473ffffffffffffffffffffffffffffffffffffffff1660209091015292915050565b61210c6123e0565b612117600382612463565b612150576040517fe181733f00000000000000000000000000000000000000000000000000000000815260048101829052602401610409565b61215b600582612463565b15612195576040517ff7d7a29400000000000000000000000000000000000000000000000000000000815260048101829052602401610409565b6121a060058261247e565b5060405181907fdcea1b78b6ddc31592a94607d537543fcaafda6cc52d6d5cc7bbfca1422baf2190600090a250565b63ffffffff82166000908152600960209081526040808320848452600501909152902080546060919061220190612e58565b80601f016020809104026020016040519081016040528092919081815260200182805461222d90612e58565b801561227a5780601f1061224f5761010080835404028352916020019161227a565b820191906000526020600020905b81548152906001019060200180831161225d57829003601f168201915b5050505050905092915050565b60606000612295600361248a565b905060006122a36005612497565b82516122af9190613493565b67ffffffffffffffff8111156122c7576122c7612cc2565b60405190808252806020026020018201604052801561233757816020015b6040805160808101825260008082526020808301829052928201819052606082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816122e55790505b5090506000805b83518110156123c357600084828151811061235b5761235b612c55565b6020026020010151905061237981600561246390919063ffffffff16565b6123b2576123868161205a565b84848151811061239857612398612c55565b602002602001018190525082806123ae906130f6565b9350505b506123bc816130f6565b905061233e565b50909392505050565b6123d46123e0565b6123dd816124a1565b50565b60005473ffffffffffffffffffffffffffffffffffffffff163314612461576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610409565b565b600081815260018301602052604081205415155b9392505050565b60006124778383612596565b60606000612477836125e5565b6000610933825490565b3373ffffffffffffffffffffffffffffffffffffffff821603612520576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610409565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60008181526001830160205260408120546125dd57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610933565b506000610933565b60608160000180548060200260200160405190810160405280929190818152602001828054801561263557602002820191906000526020600020905b815481526020019060010190808311612621575b50505050509050919050565b50805461264d90612e58565b6000825580601f1061265d575050565b601f0160209004906000526020600020908101906123dd91905b8082111561268b5760008155600101612677565b5090565b60008083601f8401126126a157600080fd5b50813567ffffffffffffffff8111156126b957600080fd5b6020830191508360208260051b85010111156126d457600080fd5b9250929050565b600080600080604085870312156126f157600080fd5b843567ffffffffffffffff8082111561270957600080fd5b6127158883890161268f565b9096509450602087013591508082111561272e57600080fd5b5061273b8782880161268f565b95989497509550505050565b60006080828403121561275957600080fd5b50919050565b60006020828403121561277157600080fd5b5035919050565b6000815180845260005b8181101561279e57602081850181015186830182015201612782565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815260006124776020830184612778565b6000806020838503121561280257600080fd5b823567ffffffffffffffff81111561281957600080fd5b6128258582860161268f565b90969095509350505050565b803563ffffffff8116811461284557600080fd5b919050565b60006020828403121561285c57600080fd5b61247782612831565b600081518084526020808501945080840160005b8381101561289557815187529582019590820190600101612879565b509495945050505050565b63ffffffff85168152600060208515158184015260406080818501526128c96080850187612865565b8481036060860152855180825283820190600581901b8301850185890160005b83811015612946578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001855281518051845288015188840188905261293388850182612778565b95890195935050908701906001016128e9565b50909c9b505050505050505050505050565b6000806040838503121561296b57600080fd5b50508035926020909101359150565b60408152600060c0820163ffffffff8551166040840152602073ffffffffffffffffffffffffffffffffffffffff81870151166060850152604086015160808501526060860151608060a086015282815180855260e0870191508383019450600092505b808310156129fe57845182529383019360019290920191908301906129de565b5063ffffffff8716838701529350612a139050565b50509392505050565b6020815273ffffffffffffffffffffffffffffffffffffffff825116602082015260006020830151604080840152612a576060840182612778565b949350505050565b80151581146123dd57600080fd5b600080600080600060608688031215612a8557600080fd5b853567ffffffffffffffff80821115612a9d57600080fd5b612aa989838a0161268f565b90975095506020880135915080821115612ac257600080fd5b50612acf8882890161268f565b9094509250506040860135612ae381612a5f565b809150509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8051825260208101516020830152604081015160028110612b6a577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b604083015260609081015173ffffffffffffffffffffffffffffffffffffffff16910152565b608081016109338284612b20565b60008060408385031215612bb157600080fd5b612bba83612831565b946020939093013593505050565b6020808252825182820181905260009190848201906040850190845b81811015612c0a57612bf7838551612b20565b9284019260809290920191600101612be4565b50909695505050505050565b73ffffffffffffffffffffffffffffffffffffffff811681146123dd57600080fd5b600060208284031215612c4a57600080fd5b813561247781612c16565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112612cb857600080fd5b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715612d1457612d14612cc2565b60405290565b6040516080810167ffffffffffffffff81118282101715612d1457612d14612cc2565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715612d8457612d84612cc2565b604052919050565b600060408236031215612d9e57600080fd5b612da6612cf1565b8235612db181612c16565b815260208381013567ffffffffffffffff80821115612dcf57600080fd5b9085019036601f830112612de257600080fd5b813581811115612df457612df4612cc2565b612e24847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601612d3d565b91508082523684828501011115612e3a57600080fd5b80848401858401376000908201840152918301919091525092915050565b600181811c90821680612e6c57607f821691505b602082108103612759577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000602080835260008454612eb981612e58565b80848701526040600180841660008114612eda5760018114612f1257612f40565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008516838a01528284151560051b8a01019550612f40565b896000528660002060005b85811015612f385781548b8201860152908301908801612f1d565b8a0184019650505b509398975050505050505050565b601f8211156109f757600081815260208120601f850160051c81016020861015612f755750805b601f850160051c820191505b818110156106d357828155600101612f81565b815167ffffffffffffffff811115612fae57612fae612cc2565b612fc281612fbc8454612e58565b84612f4e565b602080601f8311600181146130155760008415612fdf5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556106d3565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561306257888601518255948401946001909101908401613043565b508582101561309e57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b828152604060208201526000612a576040830184612778565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613127576131276130c7565b5060010190565b60006020828403121561314057600080fd5b815161247781612a5f565b81358155602082013560018201556002810160408301356002811061316f57600080fd5b8154606085013561317f81612c16565b74ffffffffffffffffffffffffffffffffffffffff008160081b1660ff84167fffffffffffffffffffffff000000000000000000000000000000000000000000841617178455505050505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112612cb857600080fd5b60006080823603121561321357600080fd5b61321b612d1a565b61322483612831565b815260208084013561323581612c16565b8282015260408481013590830152606084013567ffffffffffffffff8082111561325e57600080fd5b9085019036601f83011261327157600080fd5b81358181111561328357613283612cc2565b8060051b9150613294848301612d3d565b81815291830184019184810190368411156132ae57600080fd5b938501935b838510156132cc578435825293850193908501906132b3565b606087015250939695505050505050565b6020815260006124776020830184612865565b600063ffffffff808316818103613309576133096130c7565b6001019392505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261334857600080fd5b83018035915067ffffffffffffffff82111561336357600080fd5b6020019150368190038213156126d457600080fd5b67ffffffffffffffff83111561339057613390612cc2565b6133a48361339e8354612e58565b83612f4e565b6000601f8411600181146133f657600085156133c05750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b17835561348c565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156134455786850135825560209485019460019092019101613425565b5086821015613480577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b81810381811115610933576109336130c756fea164736f6c6343000813000a", + ABI: "[{\"inputs\":[],\"name\":\"AccessForbidden\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CapabilityAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityIsDeprecated\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"DONDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONCapability\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONNode\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"proposedConfigurationContract\",\"type\":\"address\"}],\"name\":\"InvalidCapabilityConfigurationContractInterface\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"name\":\"InvalidNodeCapabilities\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeOperatorAdmin\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"InvalidNodeP2PId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"lengthOne\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lengthTwo\",\"type\":\"uint256\"}],\"name\":\"LengthMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"NodeDoesNotSupportCapability\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDeprecated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"}],\"name\":\"NodeAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"}],\"name\":\"NodeOperatorRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"NodeRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"}],\"name\":\"NodeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability\",\"name\":\"capability\",\"type\":\"tuple\"}],\"name\":\"addCapability\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"nodes\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"}],\"name\":\"addDON\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"addNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeInfo[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"addNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"deprecateCapability\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCapabilities\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedId\",\"type\":\"bytes32\"}],\"name\":\"getCapability\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"getDON\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"id\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodeP2PIds\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"}],\"internalType\":\"structCapabilityRegistry.DONInfo\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"getDONCapabilityConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDONs\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"id\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodeP2PIds\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"}],\"internalType\":\"structCapabilityRegistry.DONInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"}],\"name\":\"getHashedCapabilityId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"getNode\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeInfo\",\"name\":\"\",\"type\":\"tuple\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"}],\"name\":\"getNodeOperator\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getNodeOperators\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getNodes\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"},{\"internalType\":\"uint32[]\",\"name\":\"\",\"type\":\"uint32[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"isCapabilityDeprecated\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32[]\",\"name\":\"donIds\",\"type\":\"uint32[]\"}],\"name\":\"removeDONs\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint256[]\"}],\"name\":\"removeNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"removedNodeP2PIds\",\"type\":\"bytes32[]\"}],\"name\":\"removeNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodes\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"}],\"name\":\"updateDON\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"updateNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeInfo[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"updateNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x6080604052601280546001600160401b0319166401000000011790553480156200002857600080fd5b503380600081620000805760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000b357620000b381620000bc565b50505062000167565b336001600160a01b03821603620001165760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000077565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b61437580620001776000396000f3fe608060405234801561001057600080fd5b50600436106101ae5760003560e01c806365c14dc7116100ee5780639cb7c5f411610097578063c63239c511610071578063c63239c514610413578063ddbe4f8214610426578063e29581aa1461043b578063f2fde38b1461045157600080fd5b80639cb7c5f4146103cd578063ae3c241c146103ed578063b06e07a71461040057600080fd5b806373ac22b4116100c857806373ac22b41461038a57806379ba50971461039d5780638da5cb5b146103a557600080fd5b806365c14dc71461034257806366acaa33146103625780636ae5c5911461037757600080fd5b8063214502431161015b57806336b402fb1161013557806336b402fb146102b3578063398f3773146102fb57806350c946fe1461030e5780635e65e3091461032f57600080fd5b8063214502431461026b57806323537405146102805780632c01a1e8146102a057600080fd5b8063181f5a771161018c578063181f5a77146102035780631cdf6343146102455780631d05394c1461025857600080fd5b80630c5801e3146101b3578063117392ce146101c857806312570011146101db575b600080fd5b6101c66101c136600461328f565b610464565b005b6101c66101d63660046132fb565b610775565b6101ee6101e9366004613313565b6109c0565b60405190151581526020015b60405180910390f35b60408051808201909152601881527f4361706162696c697479526567697374727920312e302e30000000000000000060208201525b6040516101fa9190613390565b6101c66102533660046133a3565b6109d3565b6101c66102663660046133a3565b610aa3565b610273610be3565b6040516101fa91906134f5565b61029361028e36600461358e565b610d32565b6040516101fa91906135a9565b6101c66102ae3660046133a3565b610d65565b6102ed6102c13660046135bc565b604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b6040519081526020016101fa565b6101c66103093660046133a3565b611009565b61032161031c366004613313565b6111cc565b6040516101fa92919061361f565b6101c661033d3660046133a3565b611201565b610355610350366004613313565b611704565b6040516101fa919061367c565b61036a6117ea565b6040516101fa919061368f565b6101c6610385366004613710565b6119a8565b6101c66103983660046133a3565b611a4b565b6101c6611eb1565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101fa565b6103e06103db366004613313565b611fae565b6040516101fa9190613833565b6101c66103fb366004613313565b612058565b61023861040e366004613841565b612123565b6101c661042136600461386b565b6121f8565b61042e612287565b6040516101fa91906138fe565b61044361244d565b6040516101fa92919061394c565b6101c661045f366004613a2d565b6125d8565b8281146104ac576040517fab8b67c600000000000000000000000000000000000000000000000000000000815260048101849052602481018290526044015b60405180910390fd5b6000805473ffffffffffffffffffffffffffffffffffffffff16905b8481101561076d5760008686838181106104e4576104e4613a4a565b905060200201359050600085858481811061050157610501613a4a565b90506020028101906105139190613a79565b61051c90613b81565b805190915073ffffffffffffffffffffffffffffffffffffffff1661056d576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805173ffffffffffffffffffffffffffffffffffffffff1633148015906105aa57503373ffffffffffffffffffffffffffffffffffffffff851614155b156105e1576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516000838152600f602052604090205473ffffffffffffffffffffffffffffffffffffffff908116911614158061069357506020808201516040516106279201613390565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201206000868152600f835292909220919261067a926001019101613c9a565b6040516020818303038152906040528051906020012014155b1561075a5780516000838152600f6020908152604090912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9093169290921782558201516001909101906107009082613d89565b50806000015173ffffffffffffffffffffffffffffffffffffffff167f14c8f513e8a6d86d2d16b0cb64976de4e72386c4f8068eca3b7354373f8fe97a838360200151604051610751929190613ea3565b60405180910390a25b50508061076690613eeb565b90506104c8565b505050505050565b61077d6125ec565b6040805182356020828101919091528084013582840152825180830384018152606090920190925280519101206107b560038261266f565b156107ec576040517fe288638f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006107fe6080840160608501613a2d565b73ffffffffffffffffffffffffffffffffffffffff1614610969576108296080830160608401613a2d565b73ffffffffffffffffffffffffffffffffffffffff163b158061090957506108576080830160608401613a2d565b6040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527f73e8b41d00000000000000000000000000000000000000000000000000000000600482015273ffffffffffffffffffffffffffffffffffffffff91909116906301ffc9a790602401602060405180830381865afa1580156108e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109079190613f23565b155b156109695761091e6080830160608401613a2d565b6040517fabb5e3fd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016104a3565b61097460038261268a565b506000818152600260205260409020829061098f8282613f40565b505060405181907f65610e5677eedff94555572640e442f89848a109ef8593fa927ac30b2565ff0690600090a25050565b60006109cd60058361266f565b92915050565b6109db6125ec565b60005b81811015610a9e5760008383838181106109fa576109fa613a4a565b602090810292909201356000818152600f9093526040832080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168155909350919050610a4b60018301826131f5565b50610a59905060078261268a565b506040518181527f1e5877d7b3001d1569bf733b76c7eceda58bd6c031e5b8d0b7042308ba2e9d4f9060200160405180910390a150610a9781613eeb565b90506109de565b505050565b610aab6125ec565b60005b81811015610a9e576000838383818110610aca57610aca613a4a565b9050602002016020810190610adf919061358e565b63ffffffff808216600090815260116020526040812080549394509264010000000090049091169003610b46576040517f2b62be9b00000000000000000000000000000000000000000000000000000000815263ffffffff831660048201526024016104a3565b63ffffffff808316600081815260116020526040902080547fffffffffffffffffffffffffffffffffffffffffffffff000000000000000000169055610b90916009919061268a16565b506040805163ffffffff84168152600060208201527ff264aae70bf6a9d90e68e0f9b393f4e7fbea67b063b0f336e0b36c1581703651910160405180910390a1505080610bdc90613eeb565b9050610aae565b601254606090640100000000900463ffffffff1660006001610c056009612696565b601254610c209190640100000000900463ffffffff16613fc2565b610c2a9190613fc2565b67ffffffffffffffff811115610c4257610c42613ab7565b604051908082528060200260200182016040528015610cb857816020015b6040805160a08101825260008082526020808301829052928201526060808201819052608082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909201910181610c605790505b509050600060015b838163ffffffff161015610d2957610ce2600963ffffffff8084169061266f16565b610d1957610cef816126a0565b838381518110610d0157610d01613a4a565b602002602001018190525081610d1690613eeb565b91505b610d2281613fd5565b9050610cc0565b50909392505050565b6040805160a08101825260008082526020820181905291810191909152606080820181905260808201526109cd826126a0565b6000805473ffffffffffffffffffffffffffffffffffffffff163314905b82811015611003576000848483818110610d9f57610d9f613a4a565b602090810292909201356000818152601090935260409092206001015491925050151580610dfc576040517f64e2ee92000000000000000000000000000000000000000000000000000000008152600481018390526024016104a3565b60008281526010602090815260408083205463ffffffff168352600f82528083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff1682526001810180549293919291840191610e5690613c4d565b80601f0160208091040260200160405190810160405280929190818152602001828054610e8290613c4d565b8015610ecf5780601f10610ea457610100808354040283529160200191610ecf565b820191906000526020600020905b815481529060010190602001808311610eb257829003601f168201915b505050505081525050905084158015610eff5750805173ffffffffffffffffffffffffffffffffffffffff163314155b15610f36576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260106020526040902060010154610f5490600b90612939565b50600083815260106020526040902060020154610f7390600d90612939565b5060008381526010602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001681556001810183905560020191909155517f5254e609a97bab37b7cc79fe128f85c097bd6015c6e1624ae0ba392eb975320590610fe79085815260200190565b60405180910390a150505080610ffc90613eeb565b9050610d83565b50505050565b6110116125ec565b60005b81811015610a9e57600083838381811061103057611030613a4a565b90506020028101906110429190613a79565b61104b90613b81565b805190915073ffffffffffffffffffffffffffffffffffffffff1661109c576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601254604080518082018252835173ffffffffffffffffffffffffffffffffffffffff908116825260208086015181840190815263ffffffff9095166000818152600f909252939020825181547fffffffffffffffffffffffff000000000000000000000000000000000000000016921691909117815592519192909160018201906111289082613d89565b5050601280549091506000906111439063ffffffff16613fd5565b91906101000a81548163ffffffff021916908363ffffffff160217905550816000015173ffffffffffffffffffffffffffffffffffffffff167fda6697b182650034bd205cdc2dbfabb06bdb3a0a83a2b45bfefa3c4881284e0b8284602001516040516111b1929190613ea3565b60405180910390a25050806111c590613eeb565b9050611014565b60408051608081018252600080825260208201819052918101829052606080820152906111f883612945565b91509150915091565b60005b81811015610a9e57600083838381811061122057611220613a4a565b90506020028101906112329190613ff8565b61123b9061402c565b9050600061125e60005473ffffffffffffffffffffffffffffffffffffffff1690565b825163ffffffff166000908152600f602090815260408083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff908116835260018201805496909116331496509394919390928401916112bb90613c4d565b80601f01602080910402602001604051908101604052809291908181526020018280546112e790613c4d565b80156113345780601f1061130957610100808354040283529160200191611334565b820191906000526020600020905b81548152906001019060200180831161131757829003601f168201915b5050505050815250509050811580156113645750805173ffffffffffffffffffffffffffffffffffffffff163314155b1561139b576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040808401516000908152601060205220600101541515806113f15783604001516040517f64e2ee920000000000000000000000000000000000000000000000000000000081526004016104a391815260200190565b6020840151158061143757508360200151601060008660400151815260200190815260200160002060010154141580156114375750602084015161143790600b9061266f565b1561146e576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606084015180516000036114b057806040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016104a391906140ff565b60408581015160009081526010602052208054640100000000900463ffffffff169060046114dd83613fd5565b82546101009290920a63ffffffff8181021990931691831602179091556040878101516000908152601060205290812054640100000000900490911691505b82518110156115e85761155283828151811061153a5761153a613a4a565b6020026020010151600361266f90919063ffffffff16565b61158a57826040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016104a391906140ff565b6115d783828151811061159f5761159f613a4a565b6020908102919091018101516040808b015160009081526010845281812063ffffffff80891683526003909101909452209161268a16565b506115e181613eeb565b905061151c565b5085516040808801805160009081526010602090815283822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff9096169590951790945581518082528382206002015581518152828120600190810154948b015192518252929020909101541461169d5761166c600b82612939565b50602080880180516040808b015160009081526010909452909220600101919091555161169b90600b9061268a565b505b60408781015188516020808b0151845193845263ffffffff909216908301528183015290517ff101cfc54994c31624d25789378d71ec4dbdc533e26a4ecc6b7648f4798d09169181900360600190a150505050505050806116fd90613eeb565b9050611204565b6040805180820190915260008152606060208201526000828152600f60209081526040918290208251808401909352805473ffffffffffffffffffffffffffffffffffffffff168352600181018054919284019161176190613c4d565b80601f016020809104026020016040519081016040528092919081815260200182805461178d90613c4d565b80156117da5780601f106117af576101008083540402835291602001916117da565b820191906000526020600020905b8154815290600101906020018083116117bd57829003601f168201915b5050505050815250509050919050565b60125460609063ffffffff16600060016118046007612696565b601254611817919063ffffffff16613fc2565b6118219190613fc2565b67ffffffffffffffff81111561183957611839613ab7565b60405190808252806020026020018201604052801561187f57816020015b6040805180820190915260008152606060208201528152602001906001900390816118575790505b509050600060015b8363ffffffff16811015610d29576118a060078261266f565b611998576000818152600f60209081526040918290208251808401909352805473ffffffffffffffffffffffffffffffffffffffff16835260018101805491928401916118ec90613c4d565b80601f016020809104026020016040519081016040528092919081815260200182805461191890613c4d565b80156119655780601f1061193a57610100808354040283529160200191611965565b820191906000526020600020905b81548152906001019060200180831161194857829003601f168201915b50505050508152505083838151811061198057611980613a4a565b60200260200101819052508161199590613eeb565b91505b6119a181613eeb565b9050611887565b6119b06125ec565b601254640100000000900463ffffffff16600081815260116020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001682179055611a0781600188888888886129ea565b60128054600490611a2590640100000000900463ffffffff16613fd5565b91906101000a81548163ffffffff021916908363ffffffff160217905550505050505050565b60005b81811015610a9e576000838383818110611a6a57611a6a613a4a565b9050602002810190611a7c9190613ff8565b611a859061402c565b90506000611aa860005473ffffffffffffffffffffffffffffffffffffffff1690565b825163ffffffff166000908152600f602090815260408083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff90811683526001820180549690911633149650939491939092840191611b0590613c4d565b80601f0160208091040260200160405190810160405280929190818152602001828054611b3190613c4d565b8015611b7e5780601f10611b5357610100808354040283529160200191611b7e565b820191906000526020600020905b815481529060010190602001808311611b6157829003601f168201915b505050505081525050905081158015611bae5750805173ffffffffffffffffffffffffffffffffffffffff163314155b15611be5576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408084015160009081526010602052206001015415158080611c0a57506040840151155b15611c495783604001516040517f64e2ee920000000000000000000000000000000000000000000000000000000081526004016104a391815260200190565b60208401511580611c6657506020840151611c6690600b9061266f565b15611c9d576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60608401518051600003611cdf57806040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016104a391906140ff565b60408581015160009081526010602052208054600490611d0c90640100000000900463ffffffff16613fd5565b82546101009290920a63ffffffff81810219909316918316021790915560408681015160009081526010602052908120546401000000009004909116905b8251811015611dc657611d6883828151811061153a5761153a613a4a565b611da057826040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016104a391906140ff565b611db583828151811061159f5761159f613a4a565b50611dbf81613eeb565b9050611d4a565b5085516040808801805160009081526010602090815283822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff9687161790558251808352848320600201558a018051925182529290206001015551611e3891600b919061268a16565b506040860151611e4a90600d9061268a565b5060408681015187516020808a0151845193845263ffffffff909216908301528183015290517fc9296aa9b0951d8000e8ed7f2b5be30c5106de8df3dbedf9a57c93f5f9e4d7da9181900360600190a150505050505080611eaa90613eeb565b9050611a4e565b60015473ffffffffffffffffffffffffffffffffffffffff163314611f32576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016104a3565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b604080516080808201835260008083526020808401829052838501829052606084018290528582526002808252918590208551938401865280548452600180820154928501929092529182015493949293919284019160ff169081111561201757612017613794565b600181111561202857612028613794565b815260029190910154610100900473ffffffffffffffffffffffffffffffffffffffff1660209091015292915050565b6120606125ec565b61206b60038261266f565b6120a4576040517fe181733f000000000000000000000000000000000000000000000000000000008152600481018290526024016104a3565b6120af60058261266f565b156120e9576040517ff7d7a294000000000000000000000000000000000000000000000000000000008152600481018290526024016104a3565b6120f460058261268a565b5060405181907fdcea1b78b6ddc31592a94607d537543fcaafda6cc52d6d5cc7bbfca1422baf2190600090a250565b63ffffffff8083166000908152601160209081526040808320805464010000000090049094168084526001909401825280832085845260030190915290208054606092919061217190613c4d565b80601f016020809104026020016040519081016040528092919081815260200182805461219d90613c4d565b80156121ea5780601f106121bf576101008083540402835291602001916121ea565b820191906000526020600020905b8154815290600101906020018083116121cd57829003601f168201915b505050505091505092915050565b6122006125ec565b63ffffffff808716600090815260116020526040812054640100000000900490911690819003612264576040517f2b62be9b00000000000000000000000000000000000000000000000000000000815263ffffffff881660048201526024016104a3565b61227e8761227183613fd5565b92508288888888886129ea565b50505050505050565b606060006122956003612e76565b905060006122a36005612696565b82516122af9190613fc2565b67ffffffffffffffff8111156122c7576122c7613ab7565b60405190808252806020026020018201604052801561233757816020015b6040805160808101825260008082526020808301829052928201819052606082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816122e55790505b5090506000805b8351811015610d2957600084828151811061235b5761235b613a4a565b6020026020010151905061237981600561266f90919063ffffffff16565b61243c576002600082815260200190815260200160002060405180608001604052908160008201548152602001600182015481526020016002820160009054906101000a900460ff1660018111156123d3576123d3613794565b60018111156123e4576123e4613794565b815260029190910154610100900473ffffffffffffffffffffffffffffffffffffffff16602090910152845185908590811061242257612422613a4a565b6020026020010181905250828061243890613eeb565b9350505b5061244681613eeb565b905061233e565b606080600061245c600d612e76565b90506000815167ffffffffffffffff81111561247a5761247a613ab7565b6040519080825280602002602001820160405280156124e957816020015b60408051608081018252600080825260208083018290529282015260608082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816124985790505b5090506000825167ffffffffffffffff81111561250857612508613ab7565b604051908082528060200260200182016040528015612531578160200160208202803683370190505b50905060005b83518110156125cd57600084828151811061255457612554613a4a565b6020026020010151905060008061256a83612945565b915091508186858151811061258157612581613a4a565b60200260200101819052508085858151811061259f5761259f613a4a565b602002602001019063ffffffff16908163ffffffff1681525050505050806125c690613eeb565b9050612537565b509094909350915050565b6125e06125ec565b6125e981612e83565b50565b60005473ffffffffffffffffffffffffffffffffffffffff16331461266d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016104a3565b565b600081815260018301602052604081205415155b9392505050565b60006126838383612f78565b60006109cd825490565b6040805160a081018252600080825260208083018290528284018290526060808401819052608084015263ffffffff85811683526011825284832080546401000000009004909116808452600190910182528483206002810180548751818602810186019098528088529596929591949390919083018282801561274357602002820191906000526020600020905b81548152602001906001019080831161272f575b505050505090506000815167ffffffffffffffff81111561276657612766613ab7565b6040519080825280602002602001820160405280156127ac57816020015b6040805180820190915260008152606060208201528152602001906001900390816127845790505b50905060005b81518110156128cd5760405180604001604052808483815181106127d8576127d8613a4a565b602002602001015181526020018560030160008685815181106127fd576127fd613a4a565b60200260200101518152602001908152602001600020805461281e90613c4d565b80601f016020809104026020016040519081016040528092919081815260200182805461284a90613c4d565b80156128975780601f1061286c57610100808354040283529160200191612897565b820191906000526020600020905b81548152906001019060200180831161287a57829003601f168201915b50505050508152508282815181106128b1576128b1613a4a565b6020026020010181905250806128c690613eeb565b90506127b2565b506040805160a08101825263ffffffff888116600081815260116020818152868320548086168752948b168187015292909152905268010000000000000000900460ff161515918101919091526060810161292785612e76565b81526020019190915295945050505050565b60006126838383612fc7565b604080516080810182526000808252602082018190529181019190915260608082015260408051608081018252600084815260106020908152838220805463ffffffff8082168652600183015484870152600283015486880152640100000000909104168352600301905291822060608201906129c190612e76565b905260009384526010602052604090932054929364010000000090930463ffffffff1692915050565b63ffffffff8088166000908152601160209081526040808320938a16835260019093019052908120905b85811015612ad857612a41878783818110612a3157612a31613a4a565b859260209091020135905061266f565b15612aa25788878783818110612a5957612a59613a4a565b6040517f636e405700000000000000000000000000000000000000000000000000000000815263ffffffff909416600485015260200291909101356024830152506044016104a3565b612ac7878783818110612ab757612ab7613a4a565b859260209091020135905061268a565b50612ad181613eeb565b9050612a14565b5060005b83811015612db75736858583818110612af757612af7613a4a565b9050602002810190612b099190613a79565b9050612b176003823561266f565b612b50576040517fe181733f000000000000000000000000000000000000000000000000000000008152813560048201526024016104a3565b612b5c6005823561266f565b15612b96576040517ff7d7a294000000000000000000000000000000000000000000000000000000008152813560048201526024016104a3565b8035600090815260038401602052604081208054612bb390613c4d565b90501115612bfc576040517f3927d08000000000000000000000000000000000000000000000000000000000815263ffffffff8b166004820152813560248201526044016104a3565b60005b87811015612d0e57612ca38235601060008c8c86818110612c2257612c22613a4a565b9050602002013581526020019081526020016000206003016000601060008e8e88818110612c5257612c52613a4a565b90506020020135815260200190815260200160002060000160049054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200190815260200160002061266f90919063ffffffff16565b612cfe57888882818110612cb957612cb9613a4a565b6040517fa7e7925000000000000000000000000000000000000000000000000000000000815260209091029290920135600483015250823560248201526044016104a3565b612d0781613eeb565b9050612bff565b5060028301805460018101825560009182526020918290208335910155612d3790820182614137565b82356000908152600386016020526040902091612d5591908361419c565b50612da68a8a83358b8b612d6c6020880188614137565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506130ba92505050565b50612db081613eeb565b9050612adc565b5063ffffffff88811660008181526011602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff1668010000000000000000881515027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff1617640100000000958d1695860217905581519283528201929092527ff264aae70bf6a9d90e68e0f9b393f4e7fbea67b063b0f336e0b36c1581703651910160405180910390a15050505050505050565b6060600061268383613199565b3373ffffffffffffffffffffffffffffffffffffffff821603612f02576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016104a3565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000818152600183016020526040812054612fbf575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556109cd565b5060006109cd565b600081815260018301602052604081205480156130b0576000612feb600183613fc2565b8554909150600090612fff90600190613fc2565b905081811461306457600086600001828154811061301f5761301f613a4a565b906000526020600020015490508087600001848154811061304257613042613a4a565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080613075576130756142b7565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506109cd565b60009150506109cd565b60008481526002602081905260409091200154610100900473ffffffffffffffffffffffffffffffffffffffff161561076d57600084815260026020819052604091829020015490517ffba64a7c00000000000000000000000000000000000000000000000000000000815261010090910473ffffffffffffffffffffffffffffffffffffffff169063fba64a7c9061315f908690869086908b908d906004016142e6565b600060405180830381600087803b15801561317957600080fd5b505af115801561318d573d6000803e3d6000fd5b50505050505050505050565b6060816000018054806020026020016040519081016040528092919081815260200182805480156131e957602002820191906000526020600020905b8154815260200190600101908083116131d5575b50505050509050919050565b50805461320190613c4d565b6000825580601f10613211575050565b601f0160209004906000526020600020908101906125e991905b8082111561323f576000815560010161322b565b5090565b60008083601f84011261325557600080fd5b50813567ffffffffffffffff81111561326d57600080fd5b6020830191508360208260051b850101111561328857600080fd5b9250929050565b600080600080604085870312156132a557600080fd5b843567ffffffffffffffff808211156132bd57600080fd5b6132c988838901613243565b909650945060208701359150808211156132e257600080fd5b506132ef87828801613243565b95989497509550505050565b60006080828403121561330d57600080fd5b50919050565b60006020828403121561332557600080fd5b5035919050565b6000815180845260005b8181101561335257602081850181015186830182015201613336565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081526000612683602083018461332c565b600080602083850312156133b657600080fd5b823567ffffffffffffffff8111156133cd57600080fd5b6133d985828601613243565b90969095509350505050565b600081518084526020808501945080840160005b83811015613415578151875295820195908201906001016133f9565b509495945050505050565b600063ffffffff8083511684526020818185015116818601526040915081840151151582860152606084015160a0606087015261346060a08701826133e5565b9050608085015186820360808801528181518084528484019150848160051b850101858401935060005b828110156134e7578582037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00184528451805183528701518783018990526134d48984018261332c565b958801959488019492505060010161348a565b509998505050505050505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015613568577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452613556858351613420565b9450928501929085019060010161351c565b5092979650505050505050565b803563ffffffff8116811461358957600080fd5b919050565b6000602082840312156135a057600080fd5b61268382613575565b6020815260006126836020830184613420565b600080604083850312156135cf57600080fd5b50508035926020909101359150565b63ffffffff81511682526020810151602083015260408101516040830152600060608201516080606085015261361760808501826133e5565b949350505050565b60408152600061363260408301856135de565b905063ffffffff831660208301529392505050565b73ffffffffffffffffffffffffffffffffffffffff81511682526000602082015160406020850152613617604085018261332c565b6020815260006126836020830184613647565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015613568577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526136f0858351613647565b945092850192908501906001016136b6565b80151581146125e957600080fd5b60008060008060006060868803121561372857600080fd5b853567ffffffffffffffff8082111561374057600080fd5b61374c89838a01613243565b9097509550602088013591508082111561376557600080fd5b5061377288828901613243565b909450925050604086013561378681613702565b809150509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b805182526020810151602083015260408101516002811061380d577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b604083015260609081015173ffffffffffffffffffffffffffffffffffffffff16910152565b608081016109cd82846137c3565b6000806040838503121561385457600080fd5b61385d83613575565b946020939093013593505050565b6000806000806000806080878903121561388457600080fd5b61388d87613575565b9550602087013567ffffffffffffffff808211156138aa57600080fd5b6138b68a838b01613243565b909750955060408901359150808211156138cf57600080fd5b506138dc89828a01613243565b90945092505060608701356138f081613702565b809150509295509295509295565b6020808252825182820181905260009190848201906040850190845b818110156139405761392d8385516137c3565b928401926080929092019160010161391a565b50909695505050505050565b6000604082016040835280855180835260608501915060608160051b8601019250602080880160005b838110156139c1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08887030185526139af8683516135de565b95509382019390820190600101613975565b50508584038187015286518085528782019482019350915060005b828110156139fe57845163ffffffff16845293810193928101926001016139dc565b5091979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff811681146125e957600080fd5b600060208284031215613a3f57600080fd5b813561268381613a0b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112613aad57600080fd5b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715613b0957613b09613ab7565b60405290565b6040516080810167ffffffffffffffff81118282101715613b0957613b09613ab7565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613b7957613b79613ab7565b604052919050565b600060408236031215613b9357600080fd5b613b9b613ae6565b8235613ba681613a0b565b815260208381013567ffffffffffffffff80821115613bc457600080fd5b9085019036601f830112613bd757600080fd5b813581811115613be957613be9613ab7565b613c19847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613b32565b91508082523684828501011115613c2f57600080fd5b80848401858401376000908201840152918301919091525092915050565b600181811c90821680613c6157607f821691505b60208210810361330d577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000602080835260008454613cae81613c4d565b80848701526040600180841660008114613ccf5760018114613d0757613d35565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008516838a01528284151560051b8a01019550613d35565b896000528660002060005b85811015613d2d5781548b8201860152908301908801613d12565b8a0184019650505b509398975050505050505050565b601f821115610a9e57600081815260208120601f850160051c81016020861015613d6a5750805b601f850160051c820191505b8181101561076d57828155600101613d76565b815167ffffffffffffffff811115613da357613da3613ab7565b613db781613db18454613c4d565b84613d43565b602080601f831160018114613e0a5760008415613dd45750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b17855561076d565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015613e5757888601518255948401946001909101908401613e38565b5085821015613e9357878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b828152604060208201526000613617604083018461332c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613f1c57613f1c613ebc565b5060010190565b600060208284031215613f3557600080fd5b815161268381613702565b813581556020820135600182015560028101604083013560028110613f6457600080fd5b81546060850135613f7481613a0b565b74ffffffffffffffffffffffffffffffffffffffff008160081b1660ff84167fffffffffffffffffffffff000000000000000000000000000000000000000000841617178455505050505050565b818103818111156109cd576109cd613ebc565b600063ffffffff808316818103613fee57613fee613ebc565b6001019392505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112613aad57600080fd5b60006080823603121561403e57600080fd5b614046613b0f565b61404f83613575565b81526020808401358183015260408401356040830152606084013567ffffffffffffffff8082111561408057600080fd5b9085019036601f83011261409357600080fd5b8135818111156140a5576140a5613ab7565b8060051b91506140b6848301613b32565b81815291830184019184810190368411156140d057600080fd5b938501935b838510156140ee578435825293850193908501906140d5565b606087015250939695505050505050565b6020808252825182820181905260009190848201906040850190845b818110156139405783518352928401929184019160010161411b565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261416c57600080fd5b83018035915067ffffffffffffffff82111561418757600080fd5b60200191503681900382131561328857600080fd5b67ffffffffffffffff8311156141b4576141b4613ab7565b6141c8836141c28354613c4d565b83613d43565b6000601f84116001811461421a57600085156141e45750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b1783556142b0565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156142695786850135825560209485019460019092019101614249565b50868210156142a4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b6080815284608082015260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86111561431f57600080fd5b8560051b808860a0850137820182810360a090810160208501526143459082018761332c565b91505063ffffffff8085166040840152808416606084015250969550505050505056fea164736f6c6343000813000a", } var CapabilityRegistryABI = CapabilityRegistryMetaData.ABI @@ -239,28 +248,25 @@ func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetCapability(hashed return _CapabilityRegistry.Contract.GetCapability(&_CapabilityRegistry.CallOpts, hashedId) } -func (_CapabilityRegistry *CapabilityRegistryCaller) GetDON(opts *bind.CallOpts, donId uint32) (uint32, bool, [][32]byte, []CapabilityRegistryCapabilityConfiguration, error) { +func (_CapabilityRegistry *CapabilityRegistryCaller) GetDON(opts *bind.CallOpts, donId uint32) (CapabilityRegistryDONInfo, error) { var out []interface{} err := _CapabilityRegistry.contract.Call(opts, &out, "getDON", donId) if err != nil { - return *new(uint32), *new(bool), *new([][32]byte), *new([]CapabilityRegistryCapabilityConfiguration), err + return *new(CapabilityRegistryDONInfo), err } - out0 := *abi.ConvertType(out[0], new(uint32)).(*uint32) - out1 := *abi.ConvertType(out[1], new(bool)).(*bool) - out2 := *abi.ConvertType(out[2], new([][32]byte)).(*[][32]byte) - out3 := *abi.ConvertType(out[3], new([]CapabilityRegistryCapabilityConfiguration)).(*[]CapabilityRegistryCapabilityConfiguration) + out0 := *abi.ConvertType(out[0], new(CapabilityRegistryDONInfo)).(*CapabilityRegistryDONInfo) - return out0, out1, out2, out3, err + return out0, err } -func (_CapabilityRegistry *CapabilityRegistrySession) GetDON(donId uint32) (uint32, bool, [][32]byte, []CapabilityRegistryCapabilityConfiguration, error) { +func (_CapabilityRegistry *CapabilityRegistrySession) GetDON(donId uint32) (CapabilityRegistryDONInfo, error) { return _CapabilityRegistry.Contract.GetDON(&_CapabilityRegistry.CallOpts, donId) } -func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetDON(donId uint32) (uint32, bool, [][32]byte, []CapabilityRegistryCapabilityConfiguration, error) { +func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetDON(donId uint32) (CapabilityRegistryDONInfo, error) { return _CapabilityRegistry.Contract.GetDON(&_CapabilityRegistry.CallOpts, donId) } @@ -286,6 +292,28 @@ func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetDONCapabilityConf return _CapabilityRegistry.Contract.GetDONCapabilityConfig(&_CapabilityRegistry.CallOpts, donId, capabilityId) } +func (_CapabilityRegistry *CapabilityRegistryCaller) GetDONs(opts *bind.CallOpts) ([]CapabilityRegistryDONInfo, error) { + var out []interface{} + err := _CapabilityRegistry.contract.Call(opts, &out, "getDONs") + + if err != nil { + return *new([]CapabilityRegistryDONInfo), err + } + + out0 := *abi.ConvertType(out[0], new([]CapabilityRegistryDONInfo)).(*[]CapabilityRegistryDONInfo) + + return out0, err + +} + +func (_CapabilityRegistry *CapabilityRegistrySession) GetDONs() ([]CapabilityRegistryDONInfo, error) { + return _CapabilityRegistry.Contract.GetDONs(&_CapabilityRegistry.CallOpts) +} + +func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetDONs() ([]CapabilityRegistryDONInfo, error) { + return _CapabilityRegistry.Contract.GetDONs(&_CapabilityRegistry.CallOpts) +} + func (_CapabilityRegistry *CapabilityRegistryCaller) GetHashedCapabilityId(opts *bind.CallOpts, labelledName [32]byte, version [32]byte) ([32]byte, error) { var out []interface{} err := _CapabilityRegistry.contract.Call(opts, &out, "getHashedCapabilityId", labelledName, version) @@ -308,26 +336,26 @@ func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetHashedCapabilityI return _CapabilityRegistry.Contract.GetHashedCapabilityId(&_CapabilityRegistry.CallOpts, labelledName, version) } -func (_CapabilityRegistry *CapabilityRegistryCaller) GetNode(opts *bind.CallOpts, p2pId [32]byte) (CapabilityRegistryNodeParams, uint32, error) { +func (_CapabilityRegistry *CapabilityRegistryCaller) GetNode(opts *bind.CallOpts, p2pId [32]byte) (CapabilityRegistryNodeInfo, uint32, error) { var out []interface{} err := _CapabilityRegistry.contract.Call(opts, &out, "getNode", p2pId) if err != nil { - return *new(CapabilityRegistryNodeParams), *new(uint32), err + return *new(CapabilityRegistryNodeInfo), *new(uint32), err } - out0 := *abi.ConvertType(out[0], new(CapabilityRegistryNodeParams)).(*CapabilityRegistryNodeParams) + out0 := *abi.ConvertType(out[0], new(CapabilityRegistryNodeInfo)).(*CapabilityRegistryNodeInfo) out1 := *abi.ConvertType(out[1], new(uint32)).(*uint32) return out0, out1, err } -func (_CapabilityRegistry *CapabilityRegistrySession) GetNode(p2pId [32]byte) (CapabilityRegistryNodeParams, uint32, error) { +func (_CapabilityRegistry *CapabilityRegistrySession) GetNode(p2pId [32]byte) (CapabilityRegistryNodeInfo, uint32, error) { return _CapabilityRegistry.Contract.GetNode(&_CapabilityRegistry.CallOpts, p2pId) } -func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetNode(p2pId [32]byte) (CapabilityRegistryNodeParams, uint32, error) { +func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetNode(p2pId [32]byte) (CapabilityRegistryNodeInfo, uint32, error) { return _CapabilityRegistry.Contract.GetNode(&_CapabilityRegistry.CallOpts, p2pId) } @@ -353,6 +381,51 @@ func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetNodeOperator(node return _CapabilityRegistry.Contract.GetNodeOperator(&_CapabilityRegistry.CallOpts, nodeOperatorId) } +func (_CapabilityRegistry *CapabilityRegistryCaller) GetNodeOperators(opts *bind.CallOpts) ([]CapabilityRegistryNodeOperator, error) { + var out []interface{} + err := _CapabilityRegistry.contract.Call(opts, &out, "getNodeOperators") + + if err != nil { + return *new([]CapabilityRegistryNodeOperator), err + } + + out0 := *abi.ConvertType(out[0], new([]CapabilityRegistryNodeOperator)).(*[]CapabilityRegistryNodeOperator) + + return out0, err + +} + +func (_CapabilityRegistry *CapabilityRegistrySession) GetNodeOperators() ([]CapabilityRegistryNodeOperator, error) { + return _CapabilityRegistry.Contract.GetNodeOperators(&_CapabilityRegistry.CallOpts) +} + +func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetNodeOperators() ([]CapabilityRegistryNodeOperator, error) { + return _CapabilityRegistry.Contract.GetNodeOperators(&_CapabilityRegistry.CallOpts) +} + +func (_CapabilityRegistry *CapabilityRegistryCaller) GetNodes(opts *bind.CallOpts) ([]CapabilityRegistryNodeInfo, []uint32, error) { + var out []interface{} + err := _CapabilityRegistry.contract.Call(opts, &out, "getNodes") + + if err != nil { + return *new([]CapabilityRegistryNodeInfo), *new([]uint32), err + } + + out0 := *abi.ConvertType(out[0], new([]CapabilityRegistryNodeInfo)).(*[]CapabilityRegistryNodeInfo) + out1 := *abi.ConvertType(out[1], new([]uint32)).(*[]uint32) + + return out0, out1, err + +} + +func (_CapabilityRegistry *CapabilityRegistrySession) GetNodes() ([]CapabilityRegistryNodeInfo, []uint32, error) { + return _CapabilityRegistry.Contract.GetNodes(&_CapabilityRegistry.CallOpts) +} + +func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetNodes() ([]CapabilityRegistryNodeInfo, []uint32, error) { + return _CapabilityRegistry.Contract.GetNodes(&_CapabilityRegistry.CallOpts) +} + func (_CapabilityRegistry *CapabilityRegistryCaller) IsCapabilityDeprecated(opts *bind.CallOpts, hashedCapabilityId [32]byte) (bool, error) { var out []interface{} err := _CapabilityRegistry.contract.Call(opts, &out, "isCapabilityDeprecated", hashedCapabilityId) @@ -467,15 +540,15 @@ func (_CapabilityRegistry *CapabilityRegistryTransactorSession) AddNodeOperators return _CapabilityRegistry.Contract.AddNodeOperators(&_CapabilityRegistry.TransactOpts, nodeOperators) } -func (_CapabilityRegistry *CapabilityRegistryTransactor) AddNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistryTransactor) AddNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { return _CapabilityRegistry.contract.Transact(opts, "addNodes", nodes) } -func (_CapabilityRegistry *CapabilityRegistrySession) AddNodes(nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistrySession) AddNodes(nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { return _CapabilityRegistry.Contract.AddNodes(&_CapabilityRegistry.TransactOpts, nodes) } -func (_CapabilityRegistry *CapabilityRegistryTransactorSession) AddNodes(nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistryTransactorSession) AddNodes(nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { return _CapabilityRegistry.Contract.AddNodes(&_CapabilityRegistry.TransactOpts, nodes) } @@ -491,6 +564,18 @@ func (_CapabilityRegistry *CapabilityRegistryTransactorSession) DeprecateCapabil return _CapabilityRegistry.Contract.DeprecateCapability(&_CapabilityRegistry.TransactOpts, hashedCapabilityId) } +func (_CapabilityRegistry *CapabilityRegistryTransactor) RemoveDONs(opts *bind.TransactOpts, donIds []uint32) (*types.Transaction, error) { + return _CapabilityRegistry.contract.Transact(opts, "removeDONs", donIds) +} + +func (_CapabilityRegistry *CapabilityRegistrySession) RemoveDONs(donIds []uint32) (*types.Transaction, error) { + return _CapabilityRegistry.Contract.RemoveDONs(&_CapabilityRegistry.TransactOpts, donIds) +} + +func (_CapabilityRegistry *CapabilityRegistryTransactorSession) RemoveDONs(donIds []uint32) (*types.Transaction, error) { + return _CapabilityRegistry.Contract.RemoveDONs(&_CapabilityRegistry.TransactOpts, donIds) +} + func (_CapabilityRegistry *CapabilityRegistryTransactor) RemoveNodeOperators(opts *bind.TransactOpts, nodeOperatorIds []*big.Int) (*types.Transaction, error) { return _CapabilityRegistry.contract.Transact(opts, "removeNodeOperators", nodeOperatorIds) } @@ -527,6 +612,18 @@ func (_CapabilityRegistry *CapabilityRegistryTransactorSession) TransferOwnershi return _CapabilityRegistry.Contract.TransferOwnership(&_CapabilityRegistry.TransactOpts, to) } +func (_CapabilityRegistry *CapabilityRegistryTransactor) UpdateDON(opts *bind.TransactOpts, donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilityRegistryCapabilityConfiguration, isPublic bool) (*types.Transaction, error) { + return _CapabilityRegistry.contract.Transact(opts, "updateDON", donId, nodes, capabilityConfigurations, isPublic) +} + +func (_CapabilityRegistry *CapabilityRegistrySession) UpdateDON(donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilityRegistryCapabilityConfiguration, isPublic bool) (*types.Transaction, error) { + return _CapabilityRegistry.Contract.UpdateDON(&_CapabilityRegistry.TransactOpts, donId, nodes, capabilityConfigurations, isPublic) +} + +func (_CapabilityRegistry *CapabilityRegistryTransactorSession) UpdateDON(donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilityRegistryCapabilityConfiguration, isPublic bool) (*types.Transaction, error) { + return _CapabilityRegistry.Contract.UpdateDON(&_CapabilityRegistry.TransactOpts, donId, nodes, capabilityConfigurations, isPublic) +} + func (_CapabilityRegistry *CapabilityRegistryTransactor) UpdateNodeOperators(opts *bind.TransactOpts, nodeOperatorIds []*big.Int, nodeOperators []CapabilityRegistryNodeOperator) (*types.Transaction, error) { return _CapabilityRegistry.contract.Transact(opts, "updateNodeOperators", nodeOperatorIds, nodeOperators) } @@ -539,15 +636,15 @@ func (_CapabilityRegistry *CapabilityRegistryTransactorSession) UpdateNodeOperat return _CapabilityRegistry.Contract.UpdateNodeOperators(&_CapabilityRegistry.TransactOpts, nodeOperatorIds, nodeOperators) } -func (_CapabilityRegistry *CapabilityRegistryTransactor) UpdateNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistryTransactor) UpdateNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { return _CapabilityRegistry.contract.Transact(opts, "updateNodes", nodes) } -func (_CapabilityRegistry *CapabilityRegistrySession) UpdateNodes(nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistrySession) UpdateNodes(nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { return _CapabilityRegistry.Contract.UpdateNodes(&_CapabilityRegistry.TransactOpts, nodes) } -func (_CapabilityRegistry *CapabilityRegistryTransactorSession) UpdateNodes(nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistryTransactorSession) UpdateNodes(nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { return _CapabilityRegistry.Contract.UpdateNodes(&_CapabilityRegistry.TransactOpts, nodes) } @@ -805,8 +902,8 @@ func (_CapabilityRegistry *CapabilityRegistryFilterer) ParseCapabilityDeprecated return event, nil } -type CapabilityRegistryDONAddedIterator struct { - Event *CapabilityRegistryDONAdded +type CapabilityRegistryConfigSetIterator struct { + Event *CapabilityRegistryConfigSet contract *bind.BoundContract event string @@ -817,7 +914,7 @@ type CapabilityRegistryDONAddedIterator struct { fail error } -func (it *CapabilityRegistryDONAddedIterator) Next() bool { +func (it *CapabilityRegistryConfigSetIterator) Next() bool { if it.fail != nil { return false @@ -826,7 +923,7 @@ func (it *CapabilityRegistryDONAddedIterator) Next() bool { if it.done { select { case log := <-it.logs: - it.Event = new(CapabilityRegistryDONAdded) + it.Event = new(CapabilityRegistryConfigSet) if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { it.fail = err return false @@ -841,7 +938,7 @@ func (it *CapabilityRegistryDONAddedIterator) Next() bool { select { case log := <-it.logs: - it.Event = new(CapabilityRegistryDONAdded) + it.Event = new(CapabilityRegistryConfigSet) if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { it.fail = err return false @@ -856,33 +953,33 @@ func (it *CapabilityRegistryDONAddedIterator) Next() bool { } } -func (it *CapabilityRegistryDONAddedIterator) Error() error { +func (it *CapabilityRegistryConfigSetIterator) Error() error { return it.fail } -func (it *CapabilityRegistryDONAddedIterator) Close() error { +func (it *CapabilityRegistryConfigSetIterator) Close() error { it.sub.Unsubscribe() return nil } -type CapabilityRegistryDONAdded struct { - DonId *big.Int - IsPublic bool - Raw types.Log +type CapabilityRegistryConfigSet struct { + DonId uint32 + ConfigCount uint32 + Raw types.Log } -func (_CapabilityRegistry *CapabilityRegistryFilterer) FilterDONAdded(opts *bind.FilterOpts) (*CapabilityRegistryDONAddedIterator, error) { +func (_CapabilityRegistry *CapabilityRegistryFilterer) FilterConfigSet(opts *bind.FilterOpts) (*CapabilityRegistryConfigSetIterator, error) { - logs, sub, err := _CapabilityRegistry.contract.FilterLogs(opts, "DONAdded") + logs, sub, err := _CapabilityRegistry.contract.FilterLogs(opts, "ConfigSet") if err != nil { return nil, err } - return &CapabilityRegistryDONAddedIterator{contract: _CapabilityRegistry.contract, event: "DONAdded", logs: logs, sub: sub}, nil + return &CapabilityRegistryConfigSetIterator{contract: _CapabilityRegistry.contract, event: "ConfigSet", logs: logs, sub: sub}, nil } -func (_CapabilityRegistry *CapabilityRegistryFilterer) WatchDONAdded(opts *bind.WatchOpts, sink chan<- *CapabilityRegistryDONAdded) (event.Subscription, error) { +func (_CapabilityRegistry *CapabilityRegistryFilterer) WatchConfigSet(opts *bind.WatchOpts, sink chan<- *CapabilityRegistryConfigSet) (event.Subscription, error) { - logs, sub, err := _CapabilityRegistry.contract.WatchLogs(opts, "DONAdded") + logs, sub, err := _CapabilityRegistry.contract.WatchLogs(opts, "ConfigSet") if err != nil { return nil, err } @@ -892,8 +989,8 @@ func (_CapabilityRegistry *CapabilityRegistryFilterer) WatchDONAdded(opts *bind. select { case log := <-logs: - event := new(CapabilityRegistryDONAdded) - if err := _CapabilityRegistry.contract.UnpackLog(event, "DONAdded", log); err != nil { + event := new(CapabilityRegistryConfigSet) + if err := _CapabilityRegistry.contract.UnpackLog(event, "ConfigSet", log); err != nil { return err } event.Raw = log @@ -914,9 +1011,9 @@ func (_CapabilityRegistry *CapabilityRegistryFilterer) WatchDONAdded(opts *bind. }), nil } -func (_CapabilityRegistry *CapabilityRegistryFilterer) ParseDONAdded(log types.Log) (*CapabilityRegistryDONAdded, error) { - event := new(CapabilityRegistryDONAdded) - if err := _CapabilityRegistry.contract.UnpackLog(event, "DONAdded", log); err != nil { +func (_CapabilityRegistry *CapabilityRegistryFilterer) ParseConfigSet(log types.Log) (*CapabilityRegistryConfigSet, error) { + event := new(CapabilityRegistryConfigSet) + if err := _CapabilityRegistry.contract.UnpackLog(event, "ConfigSet", log); err != nil { return nil, err } event.Raw = log @@ -986,6 +1083,7 @@ func (it *CapabilityRegistryNodeAddedIterator) Close() error { type CapabilityRegistryNodeAdded struct { P2pId [32]byte NodeOperatorId *big.Int + Signer [32]byte Raw types.Log } @@ -1596,7 +1694,7 @@ func (it *CapabilityRegistryNodeUpdatedIterator) Close() error { type CapabilityRegistryNodeUpdated struct { P2pId [32]byte NodeOperatorId *big.Int - Signer common.Address + Signer [32]byte Raw types.Log } @@ -1930,8 +2028,8 @@ func (_CapabilityRegistry *CapabilityRegistry) ParseLog(log types.Log) (generate return _CapabilityRegistry.ParseCapabilityAdded(log) case _CapabilityRegistry.abi.Events["CapabilityDeprecated"].ID: return _CapabilityRegistry.ParseCapabilityDeprecated(log) - case _CapabilityRegistry.abi.Events["DONAdded"].ID: - return _CapabilityRegistry.ParseDONAdded(log) + case _CapabilityRegistry.abi.Events["ConfigSet"].ID: + return _CapabilityRegistry.ParseConfigSet(log) case _CapabilityRegistry.abi.Events["NodeAdded"].ID: return _CapabilityRegistry.ParseNodeAdded(log) case _CapabilityRegistry.abi.Events["NodeOperatorAdded"].ID: @@ -1962,12 +2060,12 @@ func (CapabilityRegistryCapabilityDeprecated) Topic() common.Hash { return common.HexToHash("0xdcea1b78b6ddc31592a94607d537543fcaafda6cc52d6d5cc7bbfca1422baf21") } -func (CapabilityRegistryDONAdded) Topic() common.Hash { - return common.HexToHash("0xab55f4c8fb4335a586285ae209d1f1e17a7ccb22e1131963624434d98c8546a5") +func (CapabilityRegistryConfigSet) Topic() common.Hash { + return common.HexToHash("0xf264aae70bf6a9d90e68e0f9b393f4e7fbea67b063b0f336e0b36c1581703651") } func (CapabilityRegistryNodeAdded) Topic() common.Hash { - return common.HexToHash("0x5bfe8a52ad26ac6ee7b0cd46d2fd92be04735a31c45ef8aa3d4b7ea1b61bbc1f") + return common.HexToHash("0xc9296aa9b0951d8000e8ed7f2b5be30c5106de8df3dbedf9a57c93f5f9e4d7da") } func (CapabilityRegistryNodeOperatorAdded) Topic() common.Hash { @@ -1987,7 +2085,7 @@ func (CapabilityRegistryNodeRemoved) Topic() common.Hash { } func (CapabilityRegistryNodeUpdated) Topic() common.Hash { - return common.HexToHash("0x6bbba867c646be512c2f3241e65fdffdefd5528d7e7939649e06e10ee5addc3e") + return common.HexToHash("0xf101cfc54994c31624d25789378d71ec4dbdc533e26a4ecc6b7648f4798d0916") } func (CapabilityRegistryOwnershipTransferRequested) Topic() common.Hash { @@ -2007,16 +2105,22 @@ type CapabilityRegistryInterface interface { GetCapability(opts *bind.CallOpts, hashedId [32]byte) (CapabilityRegistryCapability, error) - GetDON(opts *bind.CallOpts, donId uint32) (uint32, bool, [][32]byte, []CapabilityRegistryCapabilityConfiguration, error) + GetDON(opts *bind.CallOpts, donId uint32) (CapabilityRegistryDONInfo, error) GetDONCapabilityConfig(opts *bind.CallOpts, donId uint32, capabilityId [32]byte) ([]byte, error) + GetDONs(opts *bind.CallOpts) ([]CapabilityRegistryDONInfo, error) + GetHashedCapabilityId(opts *bind.CallOpts, labelledName [32]byte, version [32]byte) ([32]byte, error) - GetNode(opts *bind.CallOpts, p2pId [32]byte) (CapabilityRegistryNodeParams, uint32, error) + GetNode(opts *bind.CallOpts, p2pId [32]byte) (CapabilityRegistryNodeInfo, uint32, error) GetNodeOperator(opts *bind.CallOpts, nodeOperatorId *big.Int) (CapabilityRegistryNodeOperator, error) + GetNodeOperators(opts *bind.CallOpts) ([]CapabilityRegistryNodeOperator, error) + + GetNodes(opts *bind.CallOpts) ([]CapabilityRegistryNodeInfo, []uint32, error) + IsCapabilityDeprecated(opts *bind.CallOpts, hashedCapabilityId [32]byte) (bool, error) Owner(opts *bind.CallOpts) (common.Address, error) @@ -2031,19 +2135,23 @@ type CapabilityRegistryInterface interface { AddNodeOperators(opts *bind.TransactOpts, nodeOperators []CapabilityRegistryNodeOperator) (*types.Transaction, error) - AddNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) + AddNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) DeprecateCapability(opts *bind.TransactOpts, hashedCapabilityId [32]byte) (*types.Transaction, error) + RemoveDONs(opts *bind.TransactOpts, donIds []uint32) (*types.Transaction, error) + RemoveNodeOperators(opts *bind.TransactOpts, nodeOperatorIds []*big.Int) (*types.Transaction, error) RemoveNodes(opts *bind.TransactOpts, removedNodeP2PIds [][32]byte) (*types.Transaction, error) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + UpdateDON(opts *bind.TransactOpts, donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilityRegistryCapabilityConfiguration, isPublic bool) (*types.Transaction, error) + UpdateNodeOperators(opts *bind.TransactOpts, nodeOperatorIds []*big.Int, nodeOperators []CapabilityRegistryNodeOperator) (*types.Transaction, error) - UpdateNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) + UpdateNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) FilterCapabilityAdded(opts *bind.FilterOpts, hashedCapabilityId [][32]byte) (*CapabilityRegistryCapabilityAddedIterator, error) @@ -2057,11 +2165,11 @@ type CapabilityRegistryInterface interface { ParseCapabilityDeprecated(log types.Log) (*CapabilityRegistryCapabilityDeprecated, error) - FilterDONAdded(opts *bind.FilterOpts) (*CapabilityRegistryDONAddedIterator, error) + FilterConfigSet(opts *bind.FilterOpts) (*CapabilityRegistryConfigSetIterator, error) - WatchDONAdded(opts *bind.WatchOpts, sink chan<- *CapabilityRegistryDONAdded) (event.Subscription, error) + WatchConfigSet(opts *bind.WatchOpts, sink chan<- *CapabilityRegistryConfigSet) (event.Subscription, error) - ParseDONAdded(log types.Log) (*CapabilityRegistryDONAdded, error) + ParseConfigSet(log types.Log) (*CapabilityRegistryConfigSet, error) FilterNodeAdded(opts *bind.FilterOpts) (*CapabilityRegistryNodeAddedIterator, error) diff --git a/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 54d1355c62f..9167c638fbb 100644 --- a/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,4 +1,4 @@ GETH_VERSION: 1.13.8 -forwarder: ../../../contracts/solc/v0.8.19/KeystoneForwarder/KeystoneForwarder.abi ../../../contracts/solc/v0.8.19/KeystoneForwarder/KeystoneForwarder.bin ed9164cfe4619dff824b11df46b66f4c6834b2ca072923f10d9ebc57ce508ed8 -keystone_capability_registry: ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.abi ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.bin d4e0661491c2adc7f0d7553287c938fb32664b9f07770f6d57ae6511ce9884cd +forwarder: ../../../contracts/solc/v0.8.19/KeystoneForwarder/KeystoneForwarder.abi ../../../contracts/solc/v0.8.19/KeystoneForwarder/KeystoneForwarder.bin 892c6ced16576bebd887eb581147c02139853d5143a0c9b77704efefd4ab7ec7 +keystone_capability_registry: ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.abi ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.bin 0a79d0eba13fd4a4b83d7618bb181c21c42222f3cc6c5a90a09302f685555033 ocr3_capability: ../../../contracts/solc/v0.8.19/OCR3Capability/OCR3Capability.abi ../../../contracts/solc/v0.8.19/OCR3Capability/OCR3Capability.bin 9dcbdf55bd5729ba266148da3f17733eb592c871c2108ccca546618628fd9ad2 From 3b9dfc3fbddf815677a850a7d311efb9cd4f2d42 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Wed, 5 Jun 2024 21:31:30 +0200 Subject: [PATCH 39/53] reset keystone --- .../keystone/generated/forwarder/forwarder.go | 56 +++-- .../keystone_capability_registry.go | 232 +++++------------- ...rapper-dependency-versions-do-not-edit.txt | 4 +- 3 files changed, 96 insertions(+), 196 deletions(-) diff --git a/core/gethwrappers/keystone/generated/forwarder/forwarder.go b/core/gethwrappers/keystone/generated/forwarder/forwarder.go index 05ee44b415b..fbdd024c64f 100644 --- a/core/gethwrappers/keystone/generated/forwarder/forwarder.go +++ b/core/gethwrappers/keystone/generated/forwarder/forwarder.go @@ -15,7 +15,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/event" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" ) @@ -32,8 +31,8 @@ var ( ) var KeystoneForwarderMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"}],\"name\":\"AlreadyProcessed\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"DuplicateSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxSigners\",\"type\":\"uint256\"}],\"name\":\"ExcessSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FaultToleranceMustBePositive\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minSigners\",\"type\":\"uint256\"}],\"name\":\"InsufficientSigners\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"InvalidDonId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"InvalidSignature\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"received\",\"type\":\"uint256\"}],\"name\":\"InvalidSignatureCount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"InvalidVersion\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrantCall\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"result\",\"type\":\"bool\"}],\"name\":\"ReportProcessed\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes2\",\"name\":\"reportId\",\"type\":\"bytes2\"}],\"name\":\"getTransmitter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiverAddress\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"rawReport\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"reportContext\",\"type\":\"bytes\"},{\"internalType\":\"bytes[]\",\"name\":\"signatures\",\"type\":\"bytes[]\"}],\"name\":\"report\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", - Bin: "0x608060405234801561001057600080fd5b5033806000816100675760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615610097576100978161009f565b505050610148565b336001600160a01b038216036100f75760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161005e565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6115ec806101576000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c806379ba50971161005b57806379ba5097146100f25780638864b864146100fa5780638da5cb5b146101d3578063f2fde38b146101f157600080fd5b80631128956514610082578063134a46f014610097578063181f5a77146100aa575b600080fd5b61009561009036600461119d565b610204565b005b6100956100a5366004611248565b61093e565b604080518082018252601781527f4b657973746f6e65466f7277617264657220312e302e30000000000000000000602082015290516100e99190611320565b60405180910390f35b610095610c9f565b6101ae61010836600461133a565b6040805160609490941b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660208086019190915260348501939093527fffff000000000000000000000000000000000000000000000000000000000000919091166054840152805160368185030181526056909301815282519282019290922060009081526003909152205473ffffffffffffffffffffffffffffffffffffffff1690565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100e9565b60005473ffffffffffffffffffffffffffffffffffffffff166101ae565b6100956101ff36600461139f565b610d9c565b60015474010000000000000000000000000000000000000000900460ff1615610259576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000179055606d8510156102d3576040517fb55ac75400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080600061031789898080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610db092505050565b63ffffffff831660009081526002602052604081205494975092955093505060ff9091169003610380576040517fea1b312900000000000000000000000000000000000000000000000000000000815263ffffffff831660048201526024015b60405180910390fd5b604080517fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060608d901b16602080830191909152603482018690527fffff000000000000000000000000000000000000000000000000000000000000841660548301528251808303603601815260569092018352815191810191909120600081815260039092529190205473ffffffffffffffffffffffffffffffffffffffff161561045b576040517f1a20d3e600000000000000000000000000000000000000000000000000000000815260048101829052602401610377565b63ffffffff831660009081526002602052604090205485906104819060ff1660016113e9565b60ff16146104ec5763ffffffff83166000908152600260205260409020546104ad9060ff1660016113e9565b6040517fd6022e8e00000000000000000000000000000000000000000000000000000000815260ff909116600482015260248101869052604401610377565b60008a8a6040516104fe929190611408565b604051908190038120610517918b908b90602001611418565b60405160208183030381529060405280519060200120905061053761102a565b6000805b888110156107a55760008060006105a98d8d8681811061055d5761055d611432565b905060200281019061056f9190611461565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610e5292505050565b9194509250905060006001886105c084601b6113e9565b6040805160008152602081018083529390935260ff90911690820152606081018690526080810185905260a0016020604051602081039080840390855afa15801561060f573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015163ffffffff8e1660009081526002602081815284832073ffffffffffffffffffffffffffffffffffffffff851684529091019052918220549850925060ff8816900390506106ca576040517fbf18af4300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610377565b6106d56001876114c6565b955060008760ff8816601f81106106ee576106ee611432565b602002015173ffffffffffffffffffffffffffffffffffffffff1614610758576040517fe021c4f200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610377565b80878760ff16601f811061076e5761076e611432565b73ffffffffffffffffffffffffffffffffffffffff90921660209290920201525061079e92508391506114df9050565b905061053b565b5050505060008b73ffffffffffffffffffffffffffffffffffffffff1663805f21328c8c602d90606d926107db93929190611517565b8e8e606d9080926107ee93929190611517565b6040518563ffffffff1660e01b815260040161080d949392919061158a565b600060405180830381600087803b15801561082757600080fd5b505af1925050508015610838575060015b15610841575060015b604080518082018252338152821515602080830191825260008681526003909152839020915182549151151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00000000000000000000000000000000000000000090921673ffffffffffffffffffffffffffffffffffffffff9182161791909117909155905186918e16907fbe015fd2fd7c1a00158e111095c794ae7030eb413d2a0990e5b78d3114df1d499061090090851515815260200190565b60405180910390a35050600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16905550505050505050505050565b610946610eb2565b8260ff16600003610983576040517f0743bae600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601f8111156109c8576040517f61750f4000000000000000000000000000000000000000000000000000000000815260048101829052601f6024820152604401610377565b6109d38360036115bc565b60ff168111610a3157806109e88460036115bc565b6109f39060016113e9565b6040517f9dd9e6d8000000000000000000000000000000000000000000000000000000008152600481019290925260ff166024820152604401610377565b60005b63ffffffff8516600090815260026020526040902060010154811015610ad25763ffffffff85166000908152600260205260408120600101805483908110610a7e57610a7e611432565b600091825260208083209091015463ffffffff891683526002808352604080852073ffffffffffffffffffffffffffffffffffffffff9093168552910190915281205550610acb816114df565b9050610a34565b5063ffffffff84166000908152600260205260409020610af6906001018383611049565b5060005b81811015610c54576000838383818110610b1657610b16611432565b9050602002016020810190610b2b919061139f565b63ffffffff8716600090815260026020818152604080842073ffffffffffffffffffffffffffffffffffffffff86168552909201905290205490915015610bb6576040517fe021c4f200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610377565b610bc18260016113e9565b63ffffffff8716600090815260026020818152604080842073ffffffffffffffffffffffffffffffffffffffff909616808552868401835290842060ff959095169094559081526001938401805494850181558252902090910180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169091179055610c4d816114df565b9050610afa565b50505063ffffffff91909116600090815260026020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055565b60015473ffffffffffffffffffffffffffffffffffffffff163314610d20576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610377565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610da4610eb2565b610dad81610f35565b50565b60008060008084600081518110610dc957610dc9611432565b60209101015160f81c600114610e2a5784600081518110610dec57610dec611432565b01602001516040517f7207be2000000000000000000000000000000000000000000000000000000000815260f89190911c6004820152602401610377565b50505050602181015160458201516049830151608b90930151919360e091821c9390911c9190565b60008060006041845114610e9457836040517f2adfdc300000000000000000000000000000000000000000000000000000000081526004016103779190611320565b50505060208101516040820151606090920151909260009190911a90565b60005473ffffffffffffffffffffffffffffffffffffffff163314610f33576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610377565b565b3373ffffffffffffffffffffffffffffffffffffffff821603610fb4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610377565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b604051806103e00160405280601f906020820280368337509192915050565b8280548282559060005260206000209081019282156110c1579160200282015b828111156110c15781547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff843516178255602090920191600190910190611069565b506110cd9291506110d1565b5090565b5b808211156110cd57600081556001016110d2565b803573ffffffffffffffffffffffffffffffffffffffff8116811461110a57600080fd5b919050565b60008083601f84011261112157600080fd5b50813567ffffffffffffffff81111561113957600080fd5b60208301915083602082850101111561115157600080fd5b9250929050565b60008083601f84011261116a57600080fd5b50813567ffffffffffffffff81111561118257600080fd5b6020830191508360208260051b850101111561115157600080fd5b60008060008060008060006080888a0312156111b857600080fd5b6111c1886110e6565b9650602088013567ffffffffffffffff808211156111de57600080fd5b6111ea8b838c0161110f565b909850965060408a013591508082111561120357600080fd5b61120f8b838c0161110f565b909650945060608a013591508082111561122857600080fd5b506112358a828b01611158565b989b979a50959850939692959293505050565b6000806000806060858703121561125e57600080fd5b843563ffffffff8116811461127257600080fd5b9350602085013560ff8116811461128857600080fd5b9250604085013567ffffffffffffffff8111156112a457600080fd5b6112b087828801611158565b95989497509550505050565b6000815180845260005b818110156112e2576020818501810151868301820152016112c6565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b60208152600061133360208301846112bc565b9392505050565b60008060006060848603121561134f57600080fd5b611358846110e6565b92506020840135915060408401357fffff0000000000000000000000000000000000000000000000000000000000008116811461139457600080fd5b809150509250925092565b6000602082840312156113b157600080fd5b611333826110e6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60ff8181168382160190811115611402576114026113ba565b92915050565b8183823760009101908152919050565b838152818360208301376000910160200190815292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261149657600080fd5b83018035915067ffffffffffffffff8211156114b157600080fd5b60200191503681900382131561115157600080fd5b60ff8281168282160390811115611402576114026113ba565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203611510576115106113ba565b5060010190565b6000808585111561152757600080fd5b8386111561153457600080fd5b5050820193919092039150565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b60408152600061159e604083018688611541565b82810360208401526115b1818587611541565b979650505050505050565b60ff81811683821602908116908181146115d8576115d86113ba565b509291505056fea164736f6c6343000813000a", + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"DuplicateSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxSigners\",\"type\":\"uint256\"}],\"name\":\"ExcessSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FaultToleranceMustBePositive\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minSigners\",\"type\":\"uint256\"}],\"name\":\"InsufficientSigners\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"InvalidDonId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"InvalidSignature\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"received\",\"type\":\"uint256\"}],\"name\":\"InvalidSignatureCount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrantCall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"reportId\",\"type\":\"bytes32\"}],\"name\":\"ReportAlreadyProcessed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"workflowOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"result\",\"type\":\"bool\"}],\"name\":\"ReportProcessed\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"}],\"name\":\"getTransmitter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiverAddress\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"rawReport\",\"type\":\"bytes\"},{\"internalType\":\"bytes[]\",\"name\":\"signatures\",\"type\":\"bytes[]\"}],\"name\":\"report\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b5033806000816100675760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615610097576100978161009f565b505050610148565b336001600160a01b038216036100f75760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161005e565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b61151d806101576000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c806379ba50971161005b57806379ba5097146101175780638da5cb5b1461011f578063c0965dc31461013d578063f2fde38b1461015057600080fd5b8063134a46f014610082578063181f5a7714610097578063390d0b15146100df575b600080fd5b610095610090366004611106565b610163565b005b604080518082018252601781527f4b657973746f6e65466f7277617264657220312e302e30000000000000000000602082015290516100d691906111de565b60405180910390f35b6100f26100ed366004611221565b61057d565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100d6565b61009561060e565b60005473ffffffffffffffffffffffffffffffffffffffff166100f2565b61009561014b36600461124b565b61070b565b61009561015e3660046112fa565b610ded565b60015474010000000000000000000000000000000000000000900460ff16156101b8576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000017905560ff8316600003610234576040517f0743bae600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601f81111561027e576040517f61750f4000000000000000000000000000000000000000000000000000000000815260048101829052601f60248201526044015b60405180910390fd5b610289836003611344565b60ff1681116102e7578061029e846003611344565b6102a9906001611367565b6040517f9dd9e6d8000000000000000000000000000000000000000000000000000000008152600481019290925260ff166024820152604401610275565b60005b63ffffffff85166000908152600260205260409020600101548110156103885763ffffffff8516600090815260026020526040812060010180548390811061033457610334611380565b600091825260208083209091015463ffffffff891683526002808352604080852073ffffffffffffffffffffffffffffffffffffffff9093168552910190915281205550610381816113af565b90506102ea565b5063ffffffff841660009081526002602052604090206103ac906001018383610ffe565b5060005b8181101561050a5760008383838181106103cc576103cc611380565b90506020020160208101906103e191906112fa565b63ffffffff8716600090815260026020818152604080842073ffffffffffffffffffffffffffffffffffffffff8616855290920190529020549091501561046c576040517fe021c4f200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610275565b610477826001611367565b63ffffffff8716600090815260026020818152604080842073ffffffffffffffffffffffffffffffffffffffff909616808552868401835290842060ff959095169094559081526001938401805494850181558252902090910180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169091179055610503816113af565b90506103b0565b50505063ffffffff91909116600090815260026020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff169055565b6000806105df84846040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084901b1660208201526034810182905260009060540160405160208183030381529060405280519060200120905092915050565b60009081526003602052604090205473ffffffffffffffffffffffffffffffffffffffff169150505b92915050565b60015473ffffffffffffffffffffffffffffffffffffffff16331461068f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610275565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b60015474010000000000000000000000000000000000000000900460ff1615610760576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000017905560588310156107da576040517fb55ac75400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060008061081f88888080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610e0192505050565b63ffffffff8316600090815260026020526040812054949850929650909450925060ff9091169003610885576040517fea1b312900000000000000000000000000000000000000000000000000000000815263ffffffff84166004820152602401610275565b604080517fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060608c901b1660208083019190915260348083018690528351808403909101815260549092018352815191810191909120600081815260039092529190205473ffffffffffffffffffffffffffffffffffffffff1615610939576040517f1aac3d2900000000000000000000000000000000000000000000000000000000815260048101829052602401610275565b63ffffffff8416600090815260026020526040902054869061095f9060ff166001611367565b60ff16146109ca5763ffffffff841660009081526002602052604090205461098b9060ff166001611367565b6040517fd6022e8e00000000000000000000000000000000000000000000000000000000815260ff909116600482015260248101879052604401610275565b600089896040516109dc9291906113e7565b604051809103902090506109ee611086565b6000805b89811015610c5c576000806000610a608e8e86818110610a1457610a14611380565b9050602002810190610a2691906113f7565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610e2692505050565b925092509250600060018883868660405160008152602001604052604051610aa4949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa158015610ac6573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015163ffffffff8f1660009081526002602081815284832073ffffffffffffffffffffffffffffffffffffffff851684529091019052918220549850925060ff881690039050610b81576040517fbf18af4300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610275565b610b8c60018761145c565b955060008760ff8816601f8110610ba557610ba5611380565b602002015173ffffffffffffffffffffffffffffffffffffffff1614610c0f576040517fe021c4f200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610275565b80878760ff16601f8110610c2557610c25611380565b73ffffffffffffffffffffffffffffffffffffffff909216602092909202015250610c5592508391506113af9050565b90506109f2565b5050505060008a905060008173ffffffffffffffffffffffffffffffffffffffff1663ff5a027088868e8e6058908092610c9893929190611475565b6040518563ffffffff1660e01b8152600401610cb7949392919061149f565b600060405180830381600087803b158015610cd157600080fd5b505af1925050508015610ce2575060015b15610ceb575060015b604080518082018252338152821515602080830191825260008781526003909152839020915182549151151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00000000000000000000000000000000000000000090921673ffffffffffffffffffffffffffffffffffffffff91821617919091179091559051869186811691908f16907fdae8e752043eb5fc7e4a6eced57ceaf159548b630125ece9ffc41cfc952c208190610daf90861515815260200190565b60405180910390a45050600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16905550505050505050505050565b610df5610e86565b610dfe81610f09565b50565b602081015160408201516044830151606490930151919360e09190911c929160601c90565b60008060006041845114610e6857836040517f2adfdc3000000000000000000000000000000000000000000000000000000000815260040161027591906111de565b50505060208101516040820151606090920151909260009190911a90565b60005473ffffffffffffffffffffffffffffffffffffffff163314610f07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610275565b565b3373ffffffffffffffffffffffffffffffffffffffff821603610f88576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610275565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b828054828255906000526020600020908101928215611076579160200282015b828111156110765781547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84351617825560209092019160019091019061101e565b506110829291506110a5565b5090565b604051806103e00160405280601f906020820280368337509192915050565b5b8082111561108257600081556001016110a6565b60008083601f8401126110cc57600080fd5b50813567ffffffffffffffff8111156110e457600080fd5b6020830191508360208260051b85010111156110ff57600080fd5b9250929050565b6000806000806060858703121561111c57600080fd5b843563ffffffff8116811461113057600080fd5b9350602085013560ff8116811461114657600080fd5b9250604085013567ffffffffffffffff81111561116257600080fd5b61116e878288016110ba565b95989497509550505050565b6000815180845260005b818110156111a057602081850181015186830182015201611184565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815260006111f1602083018461117a565b9392505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461121c57600080fd5b919050565b6000806040838503121561123457600080fd5b61123d836111f8565b946020939093013593505050565b60008060008060006060868803121561126357600080fd5b61126c866111f8565b9450602086013567ffffffffffffffff8082111561128957600080fd5b818801915088601f83011261129d57600080fd5b8135818111156112ac57600080fd5b8960208285010111156112be57600080fd5b6020830196508095505060408801359150808211156112dc57600080fd5b506112e9888289016110ba565b969995985093965092949392505050565b60006020828403121561130c57600080fd5b6111f1826111f8565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60ff818116838216029081169081811461136057611360611315565b5092915050565b60ff818116838216019081111561060857610608611315565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036113e0576113e0611315565b5060010190565b8183823760009101908152919050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261142c57600080fd5b83018035915067ffffffffffffffff82111561144757600080fd5b6020019150368190038213156110ff57600080fd5b60ff828116828216039081111561060857610608611315565b6000808585111561148557600080fd5b8386111561149257600080fd5b5050820193919092039150565b84815273ffffffffffffffffffffffffffffffffffffffff8416602082015260606040820152816060820152818360808301376000818301608090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160101939250505056fea164736f6c6343000813000a", } var KeystoneForwarderABI = KeystoneForwarderMetaData.ABI @@ -172,9 +171,9 @@ func (_KeystoneForwarder *KeystoneForwarderTransactorRaw) Transact(opts *bind.Tr return _KeystoneForwarder.Contract.contract.Transact(opts, method, params...) } -func (_KeystoneForwarder *KeystoneForwarderCaller) GetTransmitter(opts *bind.CallOpts, receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (common.Address, error) { +func (_KeystoneForwarder *KeystoneForwarderCaller) GetTransmitter(opts *bind.CallOpts, receiver common.Address, workflowExecutionId [32]byte) (common.Address, error) { var out []interface{} - err := _KeystoneForwarder.contract.Call(opts, &out, "getTransmitter", receiver, workflowExecutionId, reportId) + err := _KeystoneForwarder.contract.Call(opts, &out, "getTransmitter", receiver, workflowExecutionId) if err != nil { return *new(common.Address), err @@ -186,12 +185,12 @@ func (_KeystoneForwarder *KeystoneForwarderCaller) GetTransmitter(opts *bind.Cal } -func (_KeystoneForwarder *KeystoneForwarderSession) GetTransmitter(receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (common.Address, error) { - return _KeystoneForwarder.Contract.GetTransmitter(&_KeystoneForwarder.CallOpts, receiver, workflowExecutionId, reportId) +func (_KeystoneForwarder *KeystoneForwarderSession) GetTransmitter(receiver common.Address, workflowExecutionId [32]byte) (common.Address, error) { + return _KeystoneForwarder.Contract.GetTransmitter(&_KeystoneForwarder.CallOpts, receiver, workflowExecutionId) } -func (_KeystoneForwarder *KeystoneForwarderCallerSession) GetTransmitter(receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (common.Address, error) { - return _KeystoneForwarder.Contract.GetTransmitter(&_KeystoneForwarder.CallOpts, receiver, workflowExecutionId, reportId) +func (_KeystoneForwarder *KeystoneForwarderCallerSession) GetTransmitter(receiver common.Address, workflowExecutionId [32]byte) (common.Address, error) { + return _KeystoneForwarder.Contract.GetTransmitter(&_KeystoneForwarder.CallOpts, receiver, workflowExecutionId) } func (_KeystoneForwarder *KeystoneForwarderCaller) Owner(opts *bind.CallOpts) (common.Address, error) { @@ -250,16 +249,16 @@ func (_KeystoneForwarder *KeystoneForwarderTransactorSession) AcceptOwnership() return _KeystoneForwarder.Contract.AcceptOwnership(&_KeystoneForwarder.TransactOpts) } -func (_KeystoneForwarder *KeystoneForwarderTransactor) Report(opts *bind.TransactOpts, receiverAddress common.Address, rawReport []byte, reportContext []byte, signatures [][]byte) (*types.Transaction, error) { - return _KeystoneForwarder.contract.Transact(opts, "report", receiverAddress, rawReport, reportContext, signatures) +func (_KeystoneForwarder *KeystoneForwarderTransactor) Report(opts *bind.TransactOpts, receiverAddress common.Address, rawReport []byte, signatures [][]byte) (*types.Transaction, error) { + return _KeystoneForwarder.contract.Transact(opts, "report", receiverAddress, rawReport, signatures) } -func (_KeystoneForwarder *KeystoneForwarderSession) Report(receiverAddress common.Address, rawReport []byte, reportContext []byte, signatures [][]byte) (*types.Transaction, error) { - return _KeystoneForwarder.Contract.Report(&_KeystoneForwarder.TransactOpts, receiverAddress, rawReport, reportContext, signatures) +func (_KeystoneForwarder *KeystoneForwarderSession) Report(receiverAddress common.Address, rawReport []byte, signatures [][]byte) (*types.Transaction, error) { + return _KeystoneForwarder.Contract.Report(&_KeystoneForwarder.TransactOpts, receiverAddress, rawReport, signatures) } -func (_KeystoneForwarder *KeystoneForwarderTransactorSession) Report(receiverAddress common.Address, rawReport []byte, reportContext []byte, signatures [][]byte) (*types.Transaction, error) { - return _KeystoneForwarder.Contract.Report(&_KeystoneForwarder.TransactOpts, receiverAddress, rawReport, reportContext, signatures) +func (_KeystoneForwarder *KeystoneForwarderTransactorSession) Report(receiverAddress common.Address, rawReport []byte, signatures [][]byte) (*types.Transaction, error) { + return _KeystoneForwarder.Contract.Report(&_KeystoneForwarder.TransactOpts, receiverAddress, rawReport, signatures) } func (_KeystoneForwarder *KeystoneForwarderTransactor) SetConfig(opts *bind.TransactOpts, donId uint32, f uint8, signers []common.Address) (*types.Transaction, error) { @@ -620,41 +619,50 @@ func (it *KeystoneForwarderReportProcessedIterator) Close() error { type KeystoneForwarderReportProcessed struct { Receiver common.Address + WorkflowOwner common.Address WorkflowExecutionId [32]byte Result bool Raw types.Log } -func (_KeystoneForwarder *KeystoneForwarderFilterer) FilterReportProcessed(opts *bind.FilterOpts, receiver []common.Address, workflowExecutionId [][32]byte) (*KeystoneForwarderReportProcessedIterator, error) { +func (_KeystoneForwarder *KeystoneForwarderFilterer) FilterReportProcessed(opts *bind.FilterOpts, receiver []common.Address, workflowOwner []common.Address, workflowExecutionId [][32]byte) (*KeystoneForwarderReportProcessedIterator, error) { var receiverRule []interface{} for _, receiverItem := range receiver { receiverRule = append(receiverRule, receiverItem) } + var workflowOwnerRule []interface{} + for _, workflowOwnerItem := range workflowOwner { + workflowOwnerRule = append(workflowOwnerRule, workflowOwnerItem) + } var workflowExecutionIdRule []interface{} for _, workflowExecutionIdItem := range workflowExecutionId { workflowExecutionIdRule = append(workflowExecutionIdRule, workflowExecutionIdItem) } - logs, sub, err := _KeystoneForwarder.contract.FilterLogs(opts, "ReportProcessed", receiverRule, workflowExecutionIdRule) + logs, sub, err := _KeystoneForwarder.contract.FilterLogs(opts, "ReportProcessed", receiverRule, workflowOwnerRule, workflowExecutionIdRule) if err != nil { return nil, err } return &KeystoneForwarderReportProcessedIterator{contract: _KeystoneForwarder.contract, event: "ReportProcessed", logs: logs, sub: sub}, nil } -func (_KeystoneForwarder *KeystoneForwarderFilterer) WatchReportProcessed(opts *bind.WatchOpts, sink chan<- *KeystoneForwarderReportProcessed, receiver []common.Address, workflowExecutionId [][32]byte) (event.Subscription, error) { +func (_KeystoneForwarder *KeystoneForwarderFilterer) WatchReportProcessed(opts *bind.WatchOpts, sink chan<- *KeystoneForwarderReportProcessed, receiver []common.Address, workflowOwner []common.Address, workflowExecutionId [][32]byte) (event.Subscription, error) { var receiverRule []interface{} for _, receiverItem := range receiver { receiverRule = append(receiverRule, receiverItem) } + var workflowOwnerRule []interface{} + for _, workflowOwnerItem := range workflowOwner { + workflowOwnerRule = append(workflowOwnerRule, workflowOwnerItem) + } var workflowExecutionIdRule []interface{} for _, workflowExecutionIdItem := range workflowExecutionId { workflowExecutionIdRule = append(workflowExecutionIdRule, workflowExecutionIdItem) } - logs, sub, err := _KeystoneForwarder.contract.WatchLogs(opts, "ReportProcessed", receiverRule, workflowExecutionIdRule) + logs, sub, err := _KeystoneForwarder.contract.WatchLogs(opts, "ReportProcessed", receiverRule, workflowOwnerRule, workflowExecutionIdRule) if err != nil { return nil, err } @@ -718,7 +726,7 @@ func (KeystoneForwarderOwnershipTransferred) Topic() common.Hash { } func (KeystoneForwarderReportProcessed) Topic() common.Hash { - return common.HexToHash("0xbe015fd2fd7c1a00158e111095c794ae7030eb413d2a0990e5b78d3114df1d49") + return common.HexToHash("0xdae8e752043eb5fc7e4a6eced57ceaf159548b630125ece9ffc41cfc952c2081") } func (_KeystoneForwarder *KeystoneForwarder) Address() common.Address { @@ -726,7 +734,7 @@ func (_KeystoneForwarder *KeystoneForwarder) Address() common.Address { } type KeystoneForwarderInterface interface { - GetTransmitter(opts *bind.CallOpts, receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (common.Address, error) + GetTransmitter(opts *bind.CallOpts, receiver common.Address, workflowExecutionId [32]byte) (common.Address, error) Owner(opts *bind.CallOpts) (common.Address, error) @@ -734,7 +742,7 @@ type KeystoneForwarderInterface interface { AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) - Report(opts *bind.TransactOpts, receiverAddress common.Address, rawReport []byte, reportContext []byte, signatures [][]byte) (*types.Transaction, error) + Report(opts *bind.TransactOpts, receiverAddress common.Address, rawReport []byte, signatures [][]byte) (*types.Transaction, error) SetConfig(opts *bind.TransactOpts, donId uint32, f uint8, signers []common.Address) (*types.Transaction, error) @@ -752,9 +760,9 @@ type KeystoneForwarderInterface interface { ParseOwnershipTransferred(log types.Log) (*KeystoneForwarderOwnershipTransferred, error) - FilterReportProcessed(opts *bind.FilterOpts, receiver []common.Address, workflowExecutionId [][32]byte) (*KeystoneForwarderReportProcessedIterator, error) + FilterReportProcessed(opts *bind.FilterOpts, receiver []common.Address, workflowOwner []common.Address, workflowExecutionId [][32]byte) (*KeystoneForwarderReportProcessedIterator, error) - WatchReportProcessed(opts *bind.WatchOpts, sink chan<- *KeystoneForwarderReportProcessed, receiver []common.Address, workflowExecutionId [][32]byte) (event.Subscription, error) + WatchReportProcessed(opts *bind.WatchOpts, sink chan<- *KeystoneForwarderReportProcessed, receiver []common.Address, workflowOwner []common.Address, workflowExecutionId [][32]byte) (event.Subscription, error) ParseReportProcessed(log types.Log) (*KeystoneForwarderReportProcessed, error) diff --git a/core/gethwrappers/keystone/generated/keystone_capability_registry/keystone_capability_registry.go b/core/gethwrappers/keystone/generated/keystone_capability_registry/keystone_capability_registry.go index ab4b89d18e1..3357bf8f5fe 100644 --- a/core/gethwrappers/keystone/generated/keystone_capability_registry/keystone_capability_registry.go +++ b/core/gethwrappers/keystone/generated/keystone_capability_registry/keystone_capability_registry.go @@ -15,7 +15,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/event" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" ) @@ -43,29 +42,21 @@ type CapabilityRegistryCapabilityConfiguration struct { Config []byte } -type CapabilityRegistryDONInfo struct { - Id uint32 - ConfigCount uint32 - IsPublic bool - NodeP2PIds [][32]byte - CapabilityConfigurations []CapabilityRegistryCapabilityConfiguration +type CapabilityRegistryNodeOperator struct { + Admin common.Address + Name string } -type CapabilityRegistryNodeInfo struct { +type CapabilityRegistryNodeParams struct { NodeOperatorId uint32 - Signer [32]byte + Signer common.Address P2pId [32]byte HashedCapabilityIds [][32]byte } -type CapabilityRegistryNodeOperator struct { - Admin common.Address - Name string -} - var CapabilityRegistryMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"name\":\"AccessForbidden\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CapabilityAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityIsDeprecated\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"DONDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONCapability\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONNode\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"proposedConfigurationContract\",\"type\":\"address\"}],\"name\":\"InvalidCapabilityConfigurationContractInterface\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"name\":\"InvalidNodeCapabilities\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeOperatorAdmin\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"InvalidNodeP2PId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"lengthOne\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lengthTwo\",\"type\":\"uint256\"}],\"name\":\"LengthMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"NodeDoesNotSupportCapability\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDeprecated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"}],\"name\":\"NodeAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"}],\"name\":\"NodeOperatorRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"NodeRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"}],\"name\":\"NodeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability\",\"name\":\"capability\",\"type\":\"tuple\"}],\"name\":\"addCapability\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"nodes\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"}],\"name\":\"addDON\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"addNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeInfo[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"addNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"deprecateCapability\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCapabilities\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedId\",\"type\":\"bytes32\"}],\"name\":\"getCapability\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"getDON\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"id\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodeP2PIds\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"}],\"internalType\":\"structCapabilityRegistry.DONInfo\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"getDONCapabilityConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDONs\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"id\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodeP2PIds\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"}],\"internalType\":\"structCapabilityRegistry.DONInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"}],\"name\":\"getHashedCapabilityId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"getNode\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeInfo\",\"name\":\"\",\"type\":\"tuple\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"}],\"name\":\"getNodeOperator\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getNodeOperators\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getNodes\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"},{\"internalType\":\"uint32[]\",\"name\":\"\",\"type\":\"uint32[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"isCapabilityDeprecated\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32[]\",\"name\":\"donIds\",\"type\":\"uint32[]\"}],\"name\":\"removeDONs\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint256[]\"}],\"name\":\"removeNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"removedNodeP2PIds\",\"type\":\"bytes32[]\"}],\"name\":\"removeNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodes\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"}],\"name\":\"updateDON\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"updateNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeInfo[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"updateNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x6080604052601280546001600160401b0319166401000000011790553480156200002857600080fd5b503380600081620000805760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000b357620000b381620000bc565b50505062000167565b336001600160a01b03821603620001165760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000077565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b61437580620001776000396000f3fe608060405234801561001057600080fd5b50600436106101ae5760003560e01c806365c14dc7116100ee5780639cb7c5f411610097578063c63239c511610071578063c63239c514610413578063ddbe4f8214610426578063e29581aa1461043b578063f2fde38b1461045157600080fd5b80639cb7c5f4146103cd578063ae3c241c146103ed578063b06e07a71461040057600080fd5b806373ac22b4116100c857806373ac22b41461038a57806379ba50971461039d5780638da5cb5b146103a557600080fd5b806365c14dc71461034257806366acaa33146103625780636ae5c5911461037757600080fd5b8063214502431161015b57806336b402fb1161013557806336b402fb146102b3578063398f3773146102fb57806350c946fe1461030e5780635e65e3091461032f57600080fd5b8063214502431461026b57806323537405146102805780632c01a1e8146102a057600080fd5b8063181f5a771161018c578063181f5a77146102035780631cdf6343146102455780631d05394c1461025857600080fd5b80630c5801e3146101b3578063117392ce146101c857806312570011146101db575b600080fd5b6101c66101c136600461328f565b610464565b005b6101c66101d63660046132fb565b610775565b6101ee6101e9366004613313565b6109c0565b60405190151581526020015b60405180910390f35b60408051808201909152601881527f4361706162696c697479526567697374727920312e302e30000000000000000060208201525b6040516101fa9190613390565b6101c66102533660046133a3565b6109d3565b6101c66102663660046133a3565b610aa3565b610273610be3565b6040516101fa91906134f5565b61029361028e36600461358e565b610d32565b6040516101fa91906135a9565b6101c66102ae3660046133a3565b610d65565b6102ed6102c13660046135bc565b604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b6040519081526020016101fa565b6101c66103093660046133a3565b611009565b61032161031c366004613313565b6111cc565b6040516101fa92919061361f565b6101c661033d3660046133a3565b611201565b610355610350366004613313565b611704565b6040516101fa919061367c565b61036a6117ea565b6040516101fa919061368f565b6101c6610385366004613710565b6119a8565b6101c66103983660046133a3565b611a4b565b6101c6611eb1565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101fa565b6103e06103db366004613313565b611fae565b6040516101fa9190613833565b6101c66103fb366004613313565b612058565b61023861040e366004613841565b612123565b6101c661042136600461386b565b6121f8565b61042e612287565b6040516101fa91906138fe565b61044361244d565b6040516101fa92919061394c565b6101c661045f366004613a2d565b6125d8565b8281146104ac576040517fab8b67c600000000000000000000000000000000000000000000000000000000815260048101849052602481018290526044015b60405180910390fd5b6000805473ffffffffffffffffffffffffffffffffffffffff16905b8481101561076d5760008686838181106104e4576104e4613a4a565b905060200201359050600085858481811061050157610501613a4a565b90506020028101906105139190613a79565b61051c90613b81565b805190915073ffffffffffffffffffffffffffffffffffffffff1661056d576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805173ffffffffffffffffffffffffffffffffffffffff1633148015906105aa57503373ffffffffffffffffffffffffffffffffffffffff851614155b156105e1576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516000838152600f602052604090205473ffffffffffffffffffffffffffffffffffffffff908116911614158061069357506020808201516040516106279201613390565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201206000868152600f835292909220919261067a926001019101613c9a565b6040516020818303038152906040528051906020012014155b1561075a5780516000838152600f6020908152604090912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9093169290921782558201516001909101906107009082613d89565b50806000015173ffffffffffffffffffffffffffffffffffffffff167f14c8f513e8a6d86d2d16b0cb64976de4e72386c4f8068eca3b7354373f8fe97a838360200151604051610751929190613ea3565b60405180910390a25b50508061076690613eeb565b90506104c8565b505050505050565b61077d6125ec565b6040805182356020828101919091528084013582840152825180830384018152606090920190925280519101206107b560038261266f565b156107ec576040517fe288638f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006107fe6080840160608501613a2d565b73ffffffffffffffffffffffffffffffffffffffff1614610969576108296080830160608401613a2d565b73ffffffffffffffffffffffffffffffffffffffff163b158061090957506108576080830160608401613a2d565b6040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527f73e8b41d00000000000000000000000000000000000000000000000000000000600482015273ffffffffffffffffffffffffffffffffffffffff91909116906301ffc9a790602401602060405180830381865afa1580156108e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109079190613f23565b155b156109695761091e6080830160608401613a2d565b6040517fabb5e3fd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016104a3565b61097460038261268a565b506000818152600260205260409020829061098f8282613f40565b505060405181907f65610e5677eedff94555572640e442f89848a109ef8593fa927ac30b2565ff0690600090a25050565b60006109cd60058361266f565b92915050565b6109db6125ec565b60005b81811015610a9e5760008383838181106109fa576109fa613a4a565b602090810292909201356000818152600f9093526040832080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168155909350919050610a4b60018301826131f5565b50610a59905060078261268a565b506040518181527f1e5877d7b3001d1569bf733b76c7eceda58bd6c031e5b8d0b7042308ba2e9d4f9060200160405180910390a150610a9781613eeb565b90506109de565b505050565b610aab6125ec565b60005b81811015610a9e576000838383818110610aca57610aca613a4a565b9050602002016020810190610adf919061358e565b63ffffffff808216600090815260116020526040812080549394509264010000000090049091169003610b46576040517f2b62be9b00000000000000000000000000000000000000000000000000000000815263ffffffff831660048201526024016104a3565b63ffffffff808316600081815260116020526040902080547fffffffffffffffffffffffffffffffffffffffffffffff000000000000000000169055610b90916009919061268a16565b506040805163ffffffff84168152600060208201527ff264aae70bf6a9d90e68e0f9b393f4e7fbea67b063b0f336e0b36c1581703651910160405180910390a1505080610bdc90613eeb565b9050610aae565b601254606090640100000000900463ffffffff1660006001610c056009612696565b601254610c209190640100000000900463ffffffff16613fc2565b610c2a9190613fc2565b67ffffffffffffffff811115610c4257610c42613ab7565b604051908082528060200260200182016040528015610cb857816020015b6040805160a08101825260008082526020808301829052928201526060808201819052608082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909201910181610c605790505b509050600060015b838163ffffffff161015610d2957610ce2600963ffffffff8084169061266f16565b610d1957610cef816126a0565b838381518110610d0157610d01613a4a565b602002602001018190525081610d1690613eeb565b91505b610d2281613fd5565b9050610cc0565b50909392505050565b6040805160a08101825260008082526020820181905291810191909152606080820181905260808201526109cd826126a0565b6000805473ffffffffffffffffffffffffffffffffffffffff163314905b82811015611003576000848483818110610d9f57610d9f613a4a565b602090810292909201356000818152601090935260409092206001015491925050151580610dfc576040517f64e2ee92000000000000000000000000000000000000000000000000000000008152600481018390526024016104a3565b60008281526010602090815260408083205463ffffffff168352600f82528083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff1682526001810180549293919291840191610e5690613c4d565b80601f0160208091040260200160405190810160405280929190818152602001828054610e8290613c4d565b8015610ecf5780601f10610ea457610100808354040283529160200191610ecf565b820191906000526020600020905b815481529060010190602001808311610eb257829003601f168201915b505050505081525050905084158015610eff5750805173ffffffffffffffffffffffffffffffffffffffff163314155b15610f36576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260106020526040902060010154610f5490600b90612939565b50600083815260106020526040902060020154610f7390600d90612939565b5060008381526010602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001681556001810183905560020191909155517f5254e609a97bab37b7cc79fe128f85c097bd6015c6e1624ae0ba392eb975320590610fe79085815260200190565b60405180910390a150505080610ffc90613eeb565b9050610d83565b50505050565b6110116125ec565b60005b81811015610a9e57600083838381811061103057611030613a4a565b90506020028101906110429190613a79565b61104b90613b81565b805190915073ffffffffffffffffffffffffffffffffffffffff1661109c576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601254604080518082018252835173ffffffffffffffffffffffffffffffffffffffff908116825260208086015181840190815263ffffffff9095166000818152600f909252939020825181547fffffffffffffffffffffffff000000000000000000000000000000000000000016921691909117815592519192909160018201906111289082613d89565b5050601280549091506000906111439063ffffffff16613fd5565b91906101000a81548163ffffffff021916908363ffffffff160217905550816000015173ffffffffffffffffffffffffffffffffffffffff167fda6697b182650034bd205cdc2dbfabb06bdb3a0a83a2b45bfefa3c4881284e0b8284602001516040516111b1929190613ea3565b60405180910390a25050806111c590613eeb565b9050611014565b60408051608081018252600080825260208201819052918101829052606080820152906111f883612945565b91509150915091565b60005b81811015610a9e57600083838381811061122057611220613a4a565b90506020028101906112329190613ff8565b61123b9061402c565b9050600061125e60005473ffffffffffffffffffffffffffffffffffffffff1690565b825163ffffffff166000908152600f602090815260408083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff908116835260018201805496909116331496509394919390928401916112bb90613c4d565b80601f01602080910402602001604051908101604052809291908181526020018280546112e790613c4d565b80156113345780601f1061130957610100808354040283529160200191611334565b820191906000526020600020905b81548152906001019060200180831161131757829003601f168201915b5050505050815250509050811580156113645750805173ffffffffffffffffffffffffffffffffffffffff163314155b1561139b576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040808401516000908152601060205220600101541515806113f15783604001516040517f64e2ee920000000000000000000000000000000000000000000000000000000081526004016104a391815260200190565b6020840151158061143757508360200151601060008660400151815260200190815260200160002060010154141580156114375750602084015161143790600b9061266f565b1561146e576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606084015180516000036114b057806040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016104a391906140ff565b60408581015160009081526010602052208054640100000000900463ffffffff169060046114dd83613fd5565b82546101009290920a63ffffffff8181021990931691831602179091556040878101516000908152601060205290812054640100000000900490911691505b82518110156115e85761155283828151811061153a5761153a613a4a565b6020026020010151600361266f90919063ffffffff16565b61158a57826040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016104a391906140ff565b6115d783828151811061159f5761159f613a4a565b6020908102919091018101516040808b015160009081526010845281812063ffffffff80891683526003909101909452209161268a16565b506115e181613eeb565b905061151c565b5085516040808801805160009081526010602090815283822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff9096169590951790945581518082528382206002015581518152828120600190810154948b015192518252929020909101541461169d5761166c600b82612939565b50602080880180516040808b015160009081526010909452909220600101919091555161169b90600b9061268a565b505b60408781015188516020808b0151845193845263ffffffff909216908301528183015290517ff101cfc54994c31624d25789378d71ec4dbdc533e26a4ecc6b7648f4798d09169181900360600190a150505050505050806116fd90613eeb565b9050611204565b6040805180820190915260008152606060208201526000828152600f60209081526040918290208251808401909352805473ffffffffffffffffffffffffffffffffffffffff168352600181018054919284019161176190613c4d565b80601f016020809104026020016040519081016040528092919081815260200182805461178d90613c4d565b80156117da5780601f106117af576101008083540402835291602001916117da565b820191906000526020600020905b8154815290600101906020018083116117bd57829003601f168201915b5050505050815250509050919050565b60125460609063ffffffff16600060016118046007612696565b601254611817919063ffffffff16613fc2565b6118219190613fc2565b67ffffffffffffffff81111561183957611839613ab7565b60405190808252806020026020018201604052801561187f57816020015b6040805180820190915260008152606060208201528152602001906001900390816118575790505b509050600060015b8363ffffffff16811015610d29576118a060078261266f565b611998576000818152600f60209081526040918290208251808401909352805473ffffffffffffffffffffffffffffffffffffffff16835260018101805491928401916118ec90613c4d565b80601f016020809104026020016040519081016040528092919081815260200182805461191890613c4d565b80156119655780601f1061193a57610100808354040283529160200191611965565b820191906000526020600020905b81548152906001019060200180831161194857829003601f168201915b50505050508152505083838151811061198057611980613a4a565b60200260200101819052508161199590613eeb565b91505b6119a181613eeb565b9050611887565b6119b06125ec565b601254640100000000900463ffffffff16600081815260116020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001682179055611a0781600188888888886129ea565b60128054600490611a2590640100000000900463ffffffff16613fd5565b91906101000a81548163ffffffff021916908363ffffffff160217905550505050505050565b60005b81811015610a9e576000838383818110611a6a57611a6a613a4a565b9050602002810190611a7c9190613ff8565b611a859061402c565b90506000611aa860005473ffffffffffffffffffffffffffffffffffffffff1690565b825163ffffffff166000908152600f602090815260408083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff90811683526001820180549690911633149650939491939092840191611b0590613c4d565b80601f0160208091040260200160405190810160405280929190818152602001828054611b3190613c4d565b8015611b7e5780601f10611b5357610100808354040283529160200191611b7e565b820191906000526020600020905b815481529060010190602001808311611b6157829003601f168201915b505050505081525050905081158015611bae5750805173ffffffffffffffffffffffffffffffffffffffff163314155b15611be5576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408084015160009081526010602052206001015415158080611c0a57506040840151155b15611c495783604001516040517f64e2ee920000000000000000000000000000000000000000000000000000000081526004016104a391815260200190565b60208401511580611c6657506020840151611c6690600b9061266f565b15611c9d576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60608401518051600003611cdf57806040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016104a391906140ff565b60408581015160009081526010602052208054600490611d0c90640100000000900463ffffffff16613fd5565b82546101009290920a63ffffffff81810219909316918316021790915560408681015160009081526010602052908120546401000000009004909116905b8251811015611dc657611d6883828151811061153a5761153a613a4a565b611da057826040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016104a391906140ff565b611db583828151811061159f5761159f613a4a565b50611dbf81613eeb565b9050611d4a565b5085516040808801805160009081526010602090815283822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff9687161790558251808352848320600201558a018051925182529290206001015551611e3891600b919061268a16565b506040860151611e4a90600d9061268a565b5060408681015187516020808a0151845193845263ffffffff909216908301528183015290517fc9296aa9b0951d8000e8ed7f2b5be30c5106de8df3dbedf9a57c93f5f9e4d7da9181900360600190a150505050505080611eaa90613eeb565b9050611a4e565b60015473ffffffffffffffffffffffffffffffffffffffff163314611f32576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016104a3565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b604080516080808201835260008083526020808401829052838501829052606084018290528582526002808252918590208551938401865280548452600180820154928501929092529182015493949293919284019160ff169081111561201757612017613794565b600181111561202857612028613794565b815260029190910154610100900473ffffffffffffffffffffffffffffffffffffffff1660209091015292915050565b6120606125ec565b61206b60038261266f565b6120a4576040517fe181733f000000000000000000000000000000000000000000000000000000008152600481018290526024016104a3565b6120af60058261266f565b156120e9576040517ff7d7a294000000000000000000000000000000000000000000000000000000008152600481018290526024016104a3565b6120f460058261268a565b5060405181907fdcea1b78b6ddc31592a94607d537543fcaafda6cc52d6d5cc7bbfca1422baf2190600090a250565b63ffffffff8083166000908152601160209081526040808320805464010000000090049094168084526001909401825280832085845260030190915290208054606092919061217190613c4d565b80601f016020809104026020016040519081016040528092919081815260200182805461219d90613c4d565b80156121ea5780601f106121bf576101008083540402835291602001916121ea565b820191906000526020600020905b8154815290600101906020018083116121cd57829003601f168201915b505050505091505092915050565b6122006125ec565b63ffffffff808716600090815260116020526040812054640100000000900490911690819003612264576040517f2b62be9b00000000000000000000000000000000000000000000000000000000815263ffffffff881660048201526024016104a3565b61227e8761227183613fd5565b92508288888888886129ea565b50505050505050565b606060006122956003612e76565b905060006122a36005612696565b82516122af9190613fc2565b67ffffffffffffffff8111156122c7576122c7613ab7565b60405190808252806020026020018201604052801561233757816020015b6040805160808101825260008082526020808301829052928201819052606082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816122e55790505b5090506000805b8351811015610d2957600084828151811061235b5761235b613a4a565b6020026020010151905061237981600561266f90919063ffffffff16565b61243c576002600082815260200190815260200160002060405180608001604052908160008201548152602001600182015481526020016002820160009054906101000a900460ff1660018111156123d3576123d3613794565b60018111156123e4576123e4613794565b815260029190910154610100900473ffffffffffffffffffffffffffffffffffffffff16602090910152845185908590811061242257612422613a4a565b6020026020010181905250828061243890613eeb565b9350505b5061244681613eeb565b905061233e565b606080600061245c600d612e76565b90506000815167ffffffffffffffff81111561247a5761247a613ab7565b6040519080825280602002602001820160405280156124e957816020015b60408051608081018252600080825260208083018290529282015260608082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816124985790505b5090506000825167ffffffffffffffff81111561250857612508613ab7565b604051908082528060200260200182016040528015612531578160200160208202803683370190505b50905060005b83518110156125cd57600084828151811061255457612554613a4a565b6020026020010151905060008061256a83612945565b915091508186858151811061258157612581613a4a565b60200260200101819052508085858151811061259f5761259f613a4a565b602002602001019063ffffffff16908163ffffffff1681525050505050806125c690613eeb565b9050612537565b509094909350915050565b6125e06125ec565b6125e981612e83565b50565b60005473ffffffffffffffffffffffffffffffffffffffff16331461266d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016104a3565b565b600081815260018301602052604081205415155b9392505050565b60006126838383612f78565b60006109cd825490565b6040805160a081018252600080825260208083018290528284018290526060808401819052608084015263ffffffff85811683526011825284832080546401000000009004909116808452600190910182528483206002810180548751818602810186019098528088529596929591949390919083018282801561274357602002820191906000526020600020905b81548152602001906001019080831161272f575b505050505090506000815167ffffffffffffffff81111561276657612766613ab7565b6040519080825280602002602001820160405280156127ac57816020015b6040805180820190915260008152606060208201528152602001906001900390816127845790505b50905060005b81518110156128cd5760405180604001604052808483815181106127d8576127d8613a4a565b602002602001015181526020018560030160008685815181106127fd576127fd613a4a565b60200260200101518152602001908152602001600020805461281e90613c4d565b80601f016020809104026020016040519081016040528092919081815260200182805461284a90613c4d565b80156128975780601f1061286c57610100808354040283529160200191612897565b820191906000526020600020905b81548152906001019060200180831161287a57829003601f168201915b50505050508152508282815181106128b1576128b1613a4a565b6020026020010181905250806128c690613eeb565b90506127b2565b506040805160a08101825263ffffffff888116600081815260116020818152868320548086168752948b168187015292909152905268010000000000000000900460ff161515918101919091526060810161292785612e76565b81526020019190915295945050505050565b60006126838383612fc7565b604080516080810182526000808252602082018190529181019190915260608082015260408051608081018252600084815260106020908152838220805463ffffffff8082168652600183015484870152600283015486880152640100000000909104168352600301905291822060608201906129c190612e76565b905260009384526010602052604090932054929364010000000090930463ffffffff1692915050565b63ffffffff8088166000908152601160209081526040808320938a16835260019093019052908120905b85811015612ad857612a41878783818110612a3157612a31613a4a565b859260209091020135905061266f565b15612aa25788878783818110612a5957612a59613a4a565b6040517f636e405700000000000000000000000000000000000000000000000000000000815263ffffffff909416600485015260200291909101356024830152506044016104a3565b612ac7878783818110612ab757612ab7613a4a565b859260209091020135905061268a565b50612ad181613eeb565b9050612a14565b5060005b83811015612db75736858583818110612af757612af7613a4a565b9050602002810190612b099190613a79565b9050612b176003823561266f565b612b50576040517fe181733f000000000000000000000000000000000000000000000000000000008152813560048201526024016104a3565b612b5c6005823561266f565b15612b96576040517ff7d7a294000000000000000000000000000000000000000000000000000000008152813560048201526024016104a3565b8035600090815260038401602052604081208054612bb390613c4d565b90501115612bfc576040517f3927d08000000000000000000000000000000000000000000000000000000000815263ffffffff8b166004820152813560248201526044016104a3565b60005b87811015612d0e57612ca38235601060008c8c86818110612c2257612c22613a4a565b9050602002013581526020019081526020016000206003016000601060008e8e88818110612c5257612c52613a4a565b90506020020135815260200190815260200160002060000160049054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200190815260200160002061266f90919063ffffffff16565b612cfe57888882818110612cb957612cb9613a4a565b6040517fa7e7925000000000000000000000000000000000000000000000000000000000815260209091029290920135600483015250823560248201526044016104a3565b612d0781613eeb565b9050612bff565b5060028301805460018101825560009182526020918290208335910155612d3790820182614137565b82356000908152600386016020526040902091612d5591908361419c565b50612da68a8a83358b8b612d6c6020880188614137565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506130ba92505050565b50612db081613eeb565b9050612adc565b5063ffffffff88811660008181526011602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff1668010000000000000000881515027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff1617640100000000958d1695860217905581519283528201929092527ff264aae70bf6a9d90e68e0f9b393f4e7fbea67b063b0f336e0b36c1581703651910160405180910390a15050505050505050565b6060600061268383613199565b3373ffffffffffffffffffffffffffffffffffffffff821603612f02576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016104a3565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000818152600183016020526040812054612fbf575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556109cd565b5060006109cd565b600081815260018301602052604081205480156130b0576000612feb600183613fc2565b8554909150600090612fff90600190613fc2565b905081811461306457600086600001828154811061301f5761301f613a4a565b906000526020600020015490508087600001848154811061304257613042613a4a565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080613075576130756142b7565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506109cd565b60009150506109cd565b60008481526002602081905260409091200154610100900473ffffffffffffffffffffffffffffffffffffffff161561076d57600084815260026020819052604091829020015490517ffba64a7c00000000000000000000000000000000000000000000000000000000815261010090910473ffffffffffffffffffffffffffffffffffffffff169063fba64a7c9061315f908690869086908b908d906004016142e6565b600060405180830381600087803b15801561317957600080fd5b505af115801561318d573d6000803e3d6000fd5b50505050505050505050565b6060816000018054806020026020016040519081016040528092919081815260200182805480156131e957602002820191906000526020600020905b8154815260200190600101908083116131d5575b50505050509050919050565b50805461320190613c4d565b6000825580601f10613211575050565b601f0160209004906000526020600020908101906125e991905b8082111561323f576000815560010161322b565b5090565b60008083601f84011261325557600080fd5b50813567ffffffffffffffff81111561326d57600080fd5b6020830191508360208260051b850101111561328857600080fd5b9250929050565b600080600080604085870312156132a557600080fd5b843567ffffffffffffffff808211156132bd57600080fd5b6132c988838901613243565b909650945060208701359150808211156132e257600080fd5b506132ef87828801613243565b95989497509550505050565b60006080828403121561330d57600080fd5b50919050565b60006020828403121561332557600080fd5b5035919050565b6000815180845260005b8181101561335257602081850181015186830182015201613336565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081526000612683602083018461332c565b600080602083850312156133b657600080fd5b823567ffffffffffffffff8111156133cd57600080fd5b6133d985828601613243565b90969095509350505050565b600081518084526020808501945080840160005b83811015613415578151875295820195908201906001016133f9565b509495945050505050565b600063ffffffff8083511684526020818185015116818601526040915081840151151582860152606084015160a0606087015261346060a08701826133e5565b9050608085015186820360808801528181518084528484019150848160051b850101858401935060005b828110156134e7578582037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00184528451805183528701518783018990526134d48984018261332c565b958801959488019492505060010161348a565b509998505050505050505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015613568577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452613556858351613420565b9450928501929085019060010161351c565b5092979650505050505050565b803563ffffffff8116811461358957600080fd5b919050565b6000602082840312156135a057600080fd5b61268382613575565b6020815260006126836020830184613420565b600080604083850312156135cf57600080fd5b50508035926020909101359150565b63ffffffff81511682526020810151602083015260408101516040830152600060608201516080606085015261361760808501826133e5565b949350505050565b60408152600061363260408301856135de565b905063ffffffff831660208301529392505050565b73ffffffffffffffffffffffffffffffffffffffff81511682526000602082015160406020850152613617604085018261332c565b6020815260006126836020830184613647565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015613568577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526136f0858351613647565b945092850192908501906001016136b6565b80151581146125e957600080fd5b60008060008060006060868803121561372857600080fd5b853567ffffffffffffffff8082111561374057600080fd5b61374c89838a01613243565b9097509550602088013591508082111561376557600080fd5b5061377288828901613243565b909450925050604086013561378681613702565b809150509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b805182526020810151602083015260408101516002811061380d577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b604083015260609081015173ffffffffffffffffffffffffffffffffffffffff16910152565b608081016109cd82846137c3565b6000806040838503121561385457600080fd5b61385d83613575565b946020939093013593505050565b6000806000806000806080878903121561388457600080fd5b61388d87613575565b9550602087013567ffffffffffffffff808211156138aa57600080fd5b6138b68a838b01613243565b909750955060408901359150808211156138cf57600080fd5b506138dc89828a01613243565b90945092505060608701356138f081613702565b809150509295509295509295565b6020808252825182820181905260009190848201906040850190845b818110156139405761392d8385516137c3565b928401926080929092019160010161391a565b50909695505050505050565b6000604082016040835280855180835260608501915060608160051b8601019250602080880160005b838110156139c1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08887030185526139af8683516135de565b95509382019390820190600101613975565b50508584038187015286518085528782019482019350915060005b828110156139fe57845163ffffffff16845293810193928101926001016139dc565b5091979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff811681146125e957600080fd5b600060208284031215613a3f57600080fd5b813561268381613a0b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112613aad57600080fd5b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715613b0957613b09613ab7565b60405290565b6040516080810167ffffffffffffffff81118282101715613b0957613b09613ab7565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613b7957613b79613ab7565b604052919050565b600060408236031215613b9357600080fd5b613b9b613ae6565b8235613ba681613a0b565b815260208381013567ffffffffffffffff80821115613bc457600080fd5b9085019036601f830112613bd757600080fd5b813581811115613be957613be9613ab7565b613c19847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613b32565b91508082523684828501011115613c2f57600080fd5b80848401858401376000908201840152918301919091525092915050565b600181811c90821680613c6157607f821691505b60208210810361330d577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000602080835260008454613cae81613c4d565b80848701526040600180841660008114613ccf5760018114613d0757613d35565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008516838a01528284151560051b8a01019550613d35565b896000528660002060005b85811015613d2d5781548b8201860152908301908801613d12565b8a0184019650505b509398975050505050505050565b601f821115610a9e57600081815260208120601f850160051c81016020861015613d6a5750805b601f850160051c820191505b8181101561076d57828155600101613d76565b815167ffffffffffffffff811115613da357613da3613ab7565b613db781613db18454613c4d565b84613d43565b602080601f831160018114613e0a5760008415613dd45750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b17855561076d565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015613e5757888601518255948401946001909101908401613e38565b5085821015613e9357878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b828152604060208201526000613617604083018461332c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613f1c57613f1c613ebc565b5060010190565b600060208284031215613f3557600080fd5b815161268381613702565b813581556020820135600182015560028101604083013560028110613f6457600080fd5b81546060850135613f7481613a0b565b74ffffffffffffffffffffffffffffffffffffffff008160081b1660ff84167fffffffffffffffffffffff000000000000000000000000000000000000000000841617178455505050505050565b818103818111156109cd576109cd613ebc565b600063ffffffff808316818103613fee57613fee613ebc565b6001019392505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112613aad57600080fd5b60006080823603121561403e57600080fd5b614046613b0f565b61404f83613575565b81526020808401358183015260408401356040830152606084013567ffffffffffffffff8082111561408057600080fd5b9085019036601f83011261409357600080fd5b8135818111156140a5576140a5613ab7565b8060051b91506140b6848301613b32565b81815291830184019184810190368411156140d057600080fd5b938501935b838510156140ee578435825293850193908501906140d5565b606087015250939695505050505050565b6020808252825182820181905260009190848201906040850190845b818110156139405783518352928401929184019160010161411b565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261416c57600080fd5b83018035915067ffffffffffffffff82111561418757600080fd5b60200191503681900382131561328857600080fd5b67ffffffffffffffff8311156141b4576141b4613ab7565b6141c8836141c28354613c4d565b83613d43565b6000601f84116001811461421a57600085156141e45750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b1783556142b0565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156142695786850135825560209485019460019092019101614249565b50868210156142a4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b6080815284608082015260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86111561431f57600080fd5b8560051b808860a0850137820182810360a090810160208501526143459082018761332c565b91505063ffffffff8085166040840152808416606084015250969550505050505056fea164736f6c6343000813000a", + ABI: "[{\"inputs\":[],\"name\":\"AccessForbidden\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CapabilityAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityIsDeprecated\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONCapability\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONNode\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"proposedConfigurationContract\",\"type\":\"address\"}],\"name\":\"InvalidCapabilityConfigurationContractInterface\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"name\":\"InvalidNodeCapabilities\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeOperatorAdmin\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"InvalidNodeP2PId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"lengthOne\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lengthTwo\",\"type\":\"uint256\"}],\"name\":\"LengthMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"NodeDoesNotSupportCapability\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDeprecated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"donId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"}],\"name\":\"DONAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"}],\"name\":\"NodeAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"}],\"name\":\"NodeOperatorRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"NodeRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"NodeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability\",\"name\":\"capability\",\"type\":\"tuple\"}],\"name\":\"addCapability\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"nodes\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"}],\"name\":\"addDON\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"addNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeParams[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"addNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"deprecateCapability\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCapabilities\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedId\",\"type\":\"bytes32\"}],\"name\":\"getCapability\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"getDON\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"},{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"getDONCapabilityConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"}],\"name\":\"getHashedCapabilityId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"getNode\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeParams\",\"name\":\"\",\"type\":\"tuple\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"}],\"name\":\"getNodeOperator\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"isCapabilityDeprecated\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint256[]\"}],\"name\":\"removeNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"removedNodeP2PIds\",\"type\":\"bytes32[]\"}],\"name\":\"removeNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"updateNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeParams[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"updateNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x6080604052600b805463ffffffff191660011790553480156200002157600080fd5b503380600081620000795760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000ac57620000ac81620000b5565b50505062000160565b336001600160a01b038216036200010f5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000070565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6134b380620001706000396000f3fe608060405234801561001057600080fd5b50600436106101775760003560e01c806350e03b16116100d85780638da5cb5b1161008c578063b06e07a711610066578063b06e07a71461038f578063ddbe4f82146103a2578063f2fde38b146103b757600080fd5b80638da5cb5b146103345780639cb7c5f41461035c578063ae3c241c1461037c57600080fd5b806365c14dc7116100bd57806365c14dc7146102f95780636ae5c5911461031957806379ba50971461032c57600080fd5b806350e03b16146102d35780635840cd45146102e657600080fd5b8063235374051161012f57806336b402fb1161011457806336b402fb14610257578063398f37731461029f57806350c946fe146102b257600080fd5b806323537405146102215780632c01a1e81461024457600080fd5b8063125700111161016057806312570011146101a4578063181f5a77146101cc5780631cdf63431461020e57600080fd5b80630c5801e31461017c578063117392ce14610191575b600080fd5b61018f61018a3660046126db565b6103ca565b005b61018f61019f366004612747565b6106db565b6101b76101b236600461275f565b610926565b60405190151581526020015b60405180910390f35b60408051808201909152601881527f4361706162696c697479526567697374727920312e302e30000000000000000060208201525b6040516101c391906127dc565b61018f61021c3660046127ef565b610939565b61023461022f36600461284a565b6109fc565b6040516101c394939291906128a0565b61018f6102523660046127ef565b610c0f565b610291610265366004612958565b604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b6040519081526020016101c3565b61018f6102ad3660046127ef565b610e8d565b6102c56102c036600461275f565b611026565b6040516101c392919061297a565b61018f6102e13660046127ef565b6110e8565b61018f6102f43660046127ef565b6115c9565b61030c61030736600461275f565b611a63565b6040516101c39190612a1c565b61018f610327366004612a6d565b611b49565b61018f611f5d565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101c3565b61036f61036a36600461275f565b61205a565b6040516101c39190612b90565b61018f61038a36600461275f565b612104565b61020161039d366004612b9e565b6121cf565b6103aa612287565b6040516101c39190612bc8565b61018f6103c5366004612c38565b6123cc565b828114610412576040517fab8b67c600000000000000000000000000000000000000000000000000000000815260048101849052602481018290526044015b60405180910390fd5b6000805473ffffffffffffffffffffffffffffffffffffffff16905b848110156106d357600086868381811061044a5761044a612c55565b905060200201359050600085858481811061046757610467612c55565b90506020028101906104799190612c84565b61048290612d8c565b805190915073ffffffffffffffffffffffffffffffffffffffff166104d3576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805173ffffffffffffffffffffffffffffffffffffffff16331480159061051057503373ffffffffffffffffffffffffffffffffffffffff851614155b15610547576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805160008381526007602052604090205473ffffffffffffffffffffffffffffffffffffffff90811691161415806105f9575060208082015160405161058d92016127dc565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201206000868152600783529290922091926105e0926001019101612ea5565b6040516020818303038152906040528051906020012014155b156106c0578051600083815260076020908152604090912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9093169290921782558201516001909101906106669082612f94565b50806000015173ffffffffffffffffffffffffffffffffffffffff167f14c8f513e8a6d86d2d16b0cb64976de4e72386c4f8068eca3b7354373f8fe97a8383602001516040516106b79291906130ae565b60405180910390a25b5050806106cc906130f6565b905061042e565b505050505050565b6106e36123e0565b60408051823560208281019190915280840135828401528251808303840181526060909201909252805191012061071b600382612463565b15610752576040517fe288638f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006107646080840160608501612c38565b73ffffffffffffffffffffffffffffffffffffffff16146108cf5761078f6080830160608401612c38565b73ffffffffffffffffffffffffffffffffffffffff163b158061086f57506107bd6080830160608401612c38565b6040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527f884efe6100000000000000000000000000000000000000000000000000000000600482015273ffffffffffffffffffffffffffffffffffffffff91909116906301ffc9a790602401602060405180830381865afa158015610849573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061086d919061312e565b155b156108cf576108846080830160608401612c38565b6040517fabb5e3fd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401610409565b6108da60038261247e565b50600081815260026020526040902082906108f5828261314b565b505060405181907f65610e5677eedff94555572640e442f89848a109ef8593fa927ac30b2565ff0690600090a25050565b6000610933600583612463565b92915050565b6109416123e0565b60005b818110156109f757600083838381811061096057610960612c55565b60209081029290920135600081815260079093526040832080547fffffffffffffffffffffffff00000000000000000000000000000000000000001681559093509190506109b16001830182612641565b50506040518181527f1e5877d7b3001d1569bf733b76c7eceda58bd6c031e5b8d0b7042308ba2e9d4f9060200160405180910390a1506109f0816130f6565b9050610944565b505050565b63ffffffff81166000908152600960205260408120819060609081908390610a269060030161248a565b90506000815167ffffffffffffffff811115610a4457610a44612cc2565b604051908082528060200260200182016040528015610a8a57816020015b604080518082019091526000815260606020820152815260200190600190039081610a625790505b50905060005b8151811015610bc9576040518060400160405280848381518110610ab657610ab6612c55565b60200260200101518152602001600960008b63ffffffff1663ffffffff1681526020019081526020016000206005016000868581518110610af957610af9612c55565b602002602001015181526020019081526020016000208054610b1a90612e58565b80601f0160208091040260200160405190810160405280929190818152602001828054610b4690612e58565b8015610b935780601f10610b6857610100808354040283529160200191610b93565b820191906000526020600020905b815481529060010190602001808311610b7657829003601f168201915b5050505050815250828281518110610bad57610bad612c55565b602002602001018190525080610bc2906130f6565b9050610a90565b5063ffffffff8781166000908152600960205260409020805491821691640100000000900460ff1690610bfe9060010161248a565b919750955093509150509193509193565b6000805473ffffffffffffffffffffffffffffffffffffffff163314905b82811015610e87576000848483818110610c4957610c49612c55565b60209081029290920135600081815260089093526040909220549192505068010000000000000000900473ffffffffffffffffffffffffffffffffffffffff16151580610cc5576040517f64e2ee9200000000000000000000000000000000000000000000000000000000815260048101839052602401610409565b60008281526008602090815260408083205463ffffffff168352600782528083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff1682526001810180549293919291840191610d1f90612e58565b80601f0160208091040260200160405190810160405280929190818152602001828054610d4b90612e58565b8015610d985780601f10610d6d57610100808354040283529160200191610d98565b820191906000526020600020905b815481529060010190602001808311610d7b57829003601f168201915b505050505081525050905084158015610dc85750805173ffffffffffffffffffffffffffffffffffffffff163314155b15610dff576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526008602052604080822080547fffffffff0000000000000000000000000000000000000000000000000000000016815560010191909155517f5254e609a97bab37b7cc79fe128f85c097bd6015c6e1624ae0ba392eb975320590610e6b9085815260200190565b60405180910390a150505080610e80906130f6565b9050610c2d565b50505050565b610e956123e0565b60005b818110156109f7576000838383818110610eb457610eb4612c55565b9050602002810190610ec69190612c84565b610ecf90612d8c565b805190915073ffffffffffffffffffffffffffffffffffffffff16610f20576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600a54604080518082018252835173ffffffffffffffffffffffffffffffffffffffff908116825260208086015181840190815260008681526007909252939020825181547fffffffffffffffffffffffff000000000000000000000000000000000000000016921691909117815591519091906001820190610fa39082612f94565b50905050600a60008154610fb6906130f6565b909155508151602083015160405173ffffffffffffffffffffffffffffffffffffffff909216917fda6697b182650034bd205cdc2dbfabb06bdb3a0a83a2b45bfefa3c4881284e0b9161100b918591906130ae565b60405180910390a250508061101f906130f6565b9050610e98565b604080516080810182526000808252602082018190529181019190915260608082015260408051608081018252600084815260086020908152838220805463ffffffff808216865273ffffffffffffffffffffffffffffffffffffffff6801000000000000000083041684870152600183015486880152640100000000909104168352600201905291822060608201906110bf9061248a565b905260009384526008602052604090932054929364010000000090930463ffffffff1692915050565b60005b818110156109f757600083838381811061110757611107612c55565b905060200281019061111991906131cd565b61112290613201565b9050600061114560005473ffffffffffffffffffffffffffffffffffffffff1690565b825163ffffffff1660009081526007602090815260408083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff908116835260018201805496909116331496509394919390928401916111a290612e58565b80601f01602080910402602001604051908101604052809291908181526020018280546111ce90612e58565b801561121b5780601f106111f05761010080835404028352916020019161121b565b820191906000526020600020905b8154815290600101906020018083116111fe57829003601f168201915b50505050508152505090508115801561124b5750805173ffffffffffffffffffffffffffffffffffffffff163314155b15611282576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408084015160009081526008602052205468010000000000000000900473ffffffffffffffffffffffffffffffffffffffff161515806112f75783604001516040517f64e2ee9200000000000000000000000000000000000000000000000000000000815260040161040991815260200190565b602084015173ffffffffffffffffffffffffffffffffffffffff16611348576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060840151805160000361138a57806040517f3748d4c600000000000000000000000000000000000000000000000000000000815260040161040991906132dd565b60408581015160009081526008602052208054640100000000900463ffffffff169060046113b7836132f0565b82546101009290920a63ffffffff8181021990931691831602179091556040878101516000908152600860205290812054640100000000900490911691505b82518110156114c25761142c83828151811061141457611414612c55565b6020026020010151600361246390919063ffffffff16565b61146457826040517f3748d4c600000000000000000000000000000000000000000000000000000000815260040161040991906132dd565b6114b183828151811061147957611479612c55565b6020908102919091018101516040808b015160009081526008845281812063ffffffff80891683526002909101909452209161247e16565b506114bb816130f6565b90506113f6565b5085516040808801805160009081526008602090815283822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff968716179055825180835284832060010155808b018051845184529285902080547fffffffff0000000000000000000000000000000000000000ffffffffffffffff166801000000000000000073ffffffffffffffffffffffffffffffffffffffff9586160217905592518b5193518551918252939095169085015216908201527f6bbba867c646be512c2f3241e65fdffdefd5528d7e7939649e06e10ee5addc3e9060600160405180910390a1505050505050806115c2906130f6565b90506110eb565b60005b818110156109f75760008383838181106115e8576115e8612c55565b90506020028101906115fa91906131cd565b61160390613201565b9050600061162660005473ffffffffffffffffffffffffffffffffffffffff1690565b825163ffffffff1660009081526007602090815260408083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff9081168352600182018054969091163314965093949193909284019161168390612e58565b80601f01602080910402602001604051908101604052809291908181526020018280546116af90612e58565b80156116fc5780601f106116d1576101008083540402835291602001916116fc565b820191906000526020600020905b8154815290600101906020018083116116df57829003601f168201915b50505050508152505090508115801561172c5750805173ffffffffffffffffffffffffffffffffffffffff163314155b15611763576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408084015160009081526008602052205468010000000000000000900473ffffffffffffffffffffffffffffffffffffffff16151580806117a757506040840151155b156117e65783604001516040517f64e2ee9200000000000000000000000000000000000000000000000000000000815260040161040991815260200190565b602084015173ffffffffffffffffffffffffffffffffffffffff16611837576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060840151805160000361187957806040517f3748d4c600000000000000000000000000000000000000000000000000000000815260040161040991906132dd565b604085810151600090815260086020522080546004906118a690640100000000900463ffffffff166132f0565b82546101009290920a63ffffffff81810219909316918316021790915560408681015160009081526008602052908120546401000000009004909116905b82518110156119605761190283828151811061141457611414612c55565b61193a57826040517f3748d4c600000000000000000000000000000000000000000000000000000000815260040161040991906132dd565b61194f83828151811061147957611479612c55565b50611959816130f6565b90506118e4565b5085516040808801805160009081526008602090815283822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff968716179055825180835284832060010155808b0151835183529184902080547fffffffff0000000000000000000000000000000000000000ffffffffffffffff166801000000000000000073ffffffffffffffffffffffffffffffffffffffff9094169390930292909217909155905189518351918252909316908301527f5bfe8a52ad26ac6ee7b0cd46d2fd92be04735a31c45ef8aa3d4b7ea1b61bbc1f910160405180910390a150505050505080611a5c906130f6565b90506115cc565b6040805180820190915260008152606060208201526000828152600760209081526040918290208251808401909352805473ffffffffffffffffffffffffffffffffffffffff1683526001810180549192840191611ac090612e58565b80601f0160208091040260200160405190810160405280929190818152602001828054611aec90612e58565b8015611b395780601f10611b0e57610100808354040283529160200191611b39565b820191906000526020600020905b815481529060010190602001808311611b1c57829003601f168201915b5050505050815250509050919050565b611b516123e0565b600b5463ffffffff16600081815260096020526040812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000168317640100000000851515021790555b85811015611c76576000878783818110611bb857611bb8612c55565b905060200201359050611bf581600960008663ffffffff1663ffffffff16815260200190815260200160002060010161246390919063ffffffff16565b15611c3b576040517f636e405700000000000000000000000000000000000000000000000000000000815263ffffffff8416600482015260248101829052604401610409565b63ffffffff8084166000908152600960205260409020611c6391600190910190839061247e16565b505080611c6f906130f6565b9050611b9c565b5060005b83811015611ed45736858583818110611c9557611c95612c55565b9050602002810190611ca79190612c84565b90508035611cb6600382612463565b611cef576040517fe181733f00000000000000000000000000000000000000000000000000000000815260048101829052602401610409565b611cfa600582612463565b15611d34576040517ff7d7a29400000000000000000000000000000000000000000000000000000000815260048101829052602401610409565b63ffffffff8085166000908152600960205260409020611d5c91600390910190839061246316565b15611da2576040517f3927d08000000000000000000000000000000000000000000000000000000000815263ffffffff8516600482015260248101829052604401610409565b60005b88811015611e5a5760008a8a83818110611dc157611dc1612c55565b602090810292909201356000818152600884526040808220805463ffffffff6401000000009091048116845260029091019095529020909350611e0992909150859061246316565b611e49576040517fa7e792500000000000000000000000000000000000000000000000000000000081526004810182905260248101849052604401610409565b50611e53816130f6565b9050611da5565b5063ffffffff8085166000908152600960205260409020611e8391600390910190839061247e16565b50611e916020830183613313565b63ffffffff86166000908152600960209081526040808320868452600501909152902091611ec0919083613378565b50505080611ecd906130f6565b9050611c7a565b50600b8054600090611eeb9063ffffffff166132f0565b91906101000a81548163ffffffff021916908363ffffffff1602179055507fab55f4c8fb4335a586285ae209d1f1e17a7ccb22e1131963624434d98c8546a58183604051611f4d92919063ffffffff9290921682521515602082015260400190565b60405180910390a1505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611fde576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610409565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b604080516080808201835260008083526020808401829052838501829052606084018290528582526002808252918590208551938401865280548452600180820154928501929092529182015493949293919284019160ff16908111156120c3576120c3612af1565b60018111156120d4576120d4612af1565b815260029190910154610100900473ffffffffffffffffffffffffffffffffffffffff1660209091015292915050565b61210c6123e0565b612117600382612463565b612150576040517fe181733f00000000000000000000000000000000000000000000000000000000815260048101829052602401610409565b61215b600582612463565b15612195576040517ff7d7a29400000000000000000000000000000000000000000000000000000000815260048101829052602401610409565b6121a060058261247e565b5060405181907fdcea1b78b6ddc31592a94607d537543fcaafda6cc52d6d5cc7bbfca1422baf2190600090a250565b63ffffffff82166000908152600960209081526040808320848452600501909152902080546060919061220190612e58565b80601f016020809104026020016040519081016040528092919081815260200182805461222d90612e58565b801561227a5780601f1061224f5761010080835404028352916020019161227a565b820191906000526020600020905b81548152906001019060200180831161225d57829003601f168201915b5050505050905092915050565b60606000612295600361248a565b905060006122a36005612497565b82516122af9190613493565b67ffffffffffffffff8111156122c7576122c7612cc2565b60405190808252806020026020018201604052801561233757816020015b6040805160808101825260008082526020808301829052928201819052606082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816122e55790505b5090506000805b83518110156123c357600084828151811061235b5761235b612c55565b6020026020010151905061237981600561246390919063ffffffff16565b6123b2576123868161205a565b84848151811061239857612398612c55565b602002602001018190525082806123ae906130f6565b9350505b506123bc816130f6565b905061233e565b50909392505050565b6123d46123e0565b6123dd816124a1565b50565b60005473ffffffffffffffffffffffffffffffffffffffff163314612461576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610409565b565b600081815260018301602052604081205415155b9392505050565b60006124778383612596565b60606000612477836125e5565b6000610933825490565b3373ffffffffffffffffffffffffffffffffffffffff821603612520576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610409565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60008181526001830160205260408120546125dd57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610933565b506000610933565b60608160000180548060200260200160405190810160405280929190818152602001828054801561263557602002820191906000526020600020905b815481526020019060010190808311612621575b50505050509050919050565b50805461264d90612e58565b6000825580601f1061265d575050565b601f0160209004906000526020600020908101906123dd91905b8082111561268b5760008155600101612677565b5090565b60008083601f8401126126a157600080fd5b50813567ffffffffffffffff8111156126b957600080fd5b6020830191508360208260051b85010111156126d457600080fd5b9250929050565b600080600080604085870312156126f157600080fd5b843567ffffffffffffffff8082111561270957600080fd5b6127158883890161268f565b9096509450602087013591508082111561272e57600080fd5b5061273b8782880161268f565b95989497509550505050565b60006080828403121561275957600080fd5b50919050565b60006020828403121561277157600080fd5b5035919050565b6000815180845260005b8181101561279e57602081850181015186830182015201612782565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815260006124776020830184612778565b6000806020838503121561280257600080fd5b823567ffffffffffffffff81111561281957600080fd5b6128258582860161268f565b90969095509350505050565b803563ffffffff8116811461284557600080fd5b919050565b60006020828403121561285c57600080fd5b61247782612831565b600081518084526020808501945080840160005b8381101561289557815187529582019590820190600101612879565b509495945050505050565b63ffffffff85168152600060208515158184015260406080818501526128c96080850187612865565b8481036060860152855180825283820190600581901b8301850185890160005b83811015612946578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001855281518051845288015188840188905261293388850182612778565b95890195935050908701906001016128e9565b50909c9b505050505050505050505050565b6000806040838503121561296b57600080fd5b50508035926020909101359150565b60408152600060c0820163ffffffff8551166040840152602073ffffffffffffffffffffffffffffffffffffffff81870151166060850152604086015160808501526060860151608060a086015282815180855260e0870191508383019450600092505b808310156129fe57845182529383019360019290920191908301906129de565b5063ffffffff8716838701529350612a139050565b50509392505050565b6020815273ffffffffffffffffffffffffffffffffffffffff825116602082015260006020830151604080840152612a576060840182612778565b949350505050565b80151581146123dd57600080fd5b600080600080600060608688031215612a8557600080fd5b853567ffffffffffffffff80821115612a9d57600080fd5b612aa989838a0161268f565b90975095506020880135915080821115612ac257600080fd5b50612acf8882890161268f565b9094509250506040860135612ae381612a5f565b809150509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8051825260208101516020830152604081015160028110612b6a577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b604083015260609081015173ffffffffffffffffffffffffffffffffffffffff16910152565b608081016109338284612b20565b60008060408385031215612bb157600080fd5b612bba83612831565b946020939093013593505050565b6020808252825182820181905260009190848201906040850190845b81811015612c0a57612bf7838551612b20565b9284019260809290920191600101612be4565b50909695505050505050565b73ffffffffffffffffffffffffffffffffffffffff811681146123dd57600080fd5b600060208284031215612c4a57600080fd5b813561247781612c16565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112612cb857600080fd5b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715612d1457612d14612cc2565b60405290565b6040516080810167ffffffffffffffff81118282101715612d1457612d14612cc2565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715612d8457612d84612cc2565b604052919050565b600060408236031215612d9e57600080fd5b612da6612cf1565b8235612db181612c16565b815260208381013567ffffffffffffffff80821115612dcf57600080fd5b9085019036601f830112612de257600080fd5b813581811115612df457612df4612cc2565b612e24847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601612d3d565b91508082523684828501011115612e3a57600080fd5b80848401858401376000908201840152918301919091525092915050565b600181811c90821680612e6c57607f821691505b602082108103612759577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000602080835260008454612eb981612e58565b80848701526040600180841660008114612eda5760018114612f1257612f40565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008516838a01528284151560051b8a01019550612f40565b896000528660002060005b85811015612f385781548b8201860152908301908801612f1d565b8a0184019650505b509398975050505050505050565b601f8211156109f757600081815260208120601f850160051c81016020861015612f755750805b601f850160051c820191505b818110156106d357828155600101612f81565b815167ffffffffffffffff811115612fae57612fae612cc2565b612fc281612fbc8454612e58565b84612f4e565b602080601f8311600181146130155760008415612fdf5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556106d3565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561306257888601518255948401946001909101908401613043565b508582101561309e57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b828152604060208201526000612a576040830184612778565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613127576131276130c7565b5060010190565b60006020828403121561314057600080fd5b815161247781612a5f565b81358155602082013560018201556002810160408301356002811061316f57600080fd5b8154606085013561317f81612c16565b74ffffffffffffffffffffffffffffffffffffffff008160081b1660ff84167fffffffffffffffffffffff000000000000000000000000000000000000000000841617178455505050505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112612cb857600080fd5b60006080823603121561321357600080fd5b61321b612d1a565b61322483612831565b815260208084013561323581612c16565b8282015260408481013590830152606084013567ffffffffffffffff8082111561325e57600080fd5b9085019036601f83011261327157600080fd5b81358181111561328357613283612cc2565b8060051b9150613294848301612d3d565b81815291830184019184810190368411156132ae57600080fd5b938501935b838510156132cc578435825293850193908501906132b3565b606087015250939695505050505050565b6020815260006124776020830184612865565b600063ffffffff808316818103613309576133096130c7565b6001019392505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261334857600080fd5b83018035915067ffffffffffffffff82111561336357600080fd5b6020019150368190038213156126d457600080fd5b67ffffffffffffffff83111561339057613390612cc2565b6133a48361339e8354612e58565b83612f4e565b6000601f8411600181146133f657600085156133c05750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b17835561348c565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156134455786850135825560209485019460019092019101613425565b5086821015613480577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b81810381811115610933576109336130c756fea164736f6c6343000813000a", } var CapabilityRegistryABI = CapabilityRegistryMetaData.ABI @@ -248,25 +239,28 @@ func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetCapability(hashed return _CapabilityRegistry.Contract.GetCapability(&_CapabilityRegistry.CallOpts, hashedId) } -func (_CapabilityRegistry *CapabilityRegistryCaller) GetDON(opts *bind.CallOpts, donId uint32) (CapabilityRegistryDONInfo, error) { +func (_CapabilityRegistry *CapabilityRegistryCaller) GetDON(opts *bind.CallOpts, donId uint32) (uint32, bool, [][32]byte, []CapabilityRegistryCapabilityConfiguration, error) { var out []interface{} err := _CapabilityRegistry.contract.Call(opts, &out, "getDON", donId) if err != nil { - return *new(CapabilityRegistryDONInfo), err + return *new(uint32), *new(bool), *new([][32]byte), *new([]CapabilityRegistryCapabilityConfiguration), err } - out0 := *abi.ConvertType(out[0], new(CapabilityRegistryDONInfo)).(*CapabilityRegistryDONInfo) + out0 := *abi.ConvertType(out[0], new(uint32)).(*uint32) + out1 := *abi.ConvertType(out[1], new(bool)).(*bool) + out2 := *abi.ConvertType(out[2], new([][32]byte)).(*[][32]byte) + out3 := *abi.ConvertType(out[3], new([]CapabilityRegistryCapabilityConfiguration)).(*[]CapabilityRegistryCapabilityConfiguration) - return out0, err + return out0, out1, out2, out3, err } -func (_CapabilityRegistry *CapabilityRegistrySession) GetDON(donId uint32) (CapabilityRegistryDONInfo, error) { +func (_CapabilityRegistry *CapabilityRegistrySession) GetDON(donId uint32) (uint32, bool, [][32]byte, []CapabilityRegistryCapabilityConfiguration, error) { return _CapabilityRegistry.Contract.GetDON(&_CapabilityRegistry.CallOpts, donId) } -func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetDON(donId uint32) (CapabilityRegistryDONInfo, error) { +func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetDON(donId uint32) (uint32, bool, [][32]byte, []CapabilityRegistryCapabilityConfiguration, error) { return _CapabilityRegistry.Contract.GetDON(&_CapabilityRegistry.CallOpts, donId) } @@ -292,28 +286,6 @@ func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetDONCapabilityConf return _CapabilityRegistry.Contract.GetDONCapabilityConfig(&_CapabilityRegistry.CallOpts, donId, capabilityId) } -func (_CapabilityRegistry *CapabilityRegistryCaller) GetDONs(opts *bind.CallOpts) ([]CapabilityRegistryDONInfo, error) { - var out []interface{} - err := _CapabilityRegistry.contract.Call(opts, &out, "getDONs") - - if err != nil { - return *new([]CapabilityRegistryDONInfo), err - } - - out0 := *abi.ConvertType(out[0], new([]CapabilityRegistryDONInfo)).(*[]CapabilityRegistryDONInfo) - - return out0, err - -} - -func (_CapabilityRegistry *CapabilityRegistrySession) GetDONs() ([]CapabilityRegistryDONInfo, error) { - return _CapabilityRegistry.Contract.GetDONs(&_CapabilityRegistry.CallOpts) -} - -func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetDONs() ([]CapabilityRegistryDONInfo, error) { - return _CapabilityRegistry.Contract.GetDONs(&_CapabilityRegistry.CallOpts) -} - func (_CapabilityRegistry *CapabilityRegistryCaller) GetHashedCapabilityId(opts *bind.CallOpts, labelledName [32]byte, version [32]byte) ([32]byte, error) { var out []interface{} err := _CapabilityRegistry.contract.Call(opts, &out, "getHashedCapabilityId", labelledName, version) @@ -336,26 +308,26 @@ func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetHashedCapabilityI return _CapabilityRegistry.Contract.GetHashedCapabilityId(&_CapabilityRegistry.CallOpts, labelledName, version) } -func (_CapabilityRegistry *CapabilityRegistryCaller) GetNode(opts *bind.CallOpts, p2pId [32]byte) (CapabilityRegistryNodeInfo, uint32, error) { +func (_CapabilityRegistry *CapabilityRegistryCaller) GetNode(opts *bind.CallOpts, p2pId [32]byte) (CapabilityRegistryNodeParams, uint32, error) { var out []interface{} err := _CapabilityRegistry.contract.Call(opts, &out, "getNode", p2pId) if err != nil { - return *new(CapabilityRegistryNodeInfo), *new(uint32), err + return *new(CapabilityRegistryNodeParams), *new(uint32), err } - out0 := *abi.ConvertType(out[0], new(CapabilityRegistryNodeInfo)).(*CapabilityRegistryNodeInfo) + out0 := *abi.ConvertType(out[0], new(CapabilityRegistryNodeParams)).(*CapabilityRegistryNodeParams) out1 := *abi.ConvertType(out[1], new(uint32)).(*uint32) return out0, out1, err } -func (_CapabilityRegistry *CapabilityRegistrySession) GetNode(p2pId [32]byte) (CapabilityRegistryNodeInfo, uint32, error) { +func (_CapabilityRegistry *CapabilityRegistrySession) GetNode(p2pId [32]byte) (CapabilityRegistryNodeParams, uint32, error) { return _CapabilityRegistry.Contract.GetNode(&_CapabilityRegistry.CallOpts, p2pId) } -func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetNode(p2pId [32]byte) (CapabilityRegistryNodeInfo, uint32, error) { +func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetNode(p2pId [32]byte) (CapabilityRegistryNodeParams, uint32, error) { return _CapabilityRegistry.Contract.GetNode(&_CapabilityRegistry.CallOpts, p2pId) } @@ -381,51 +353,6 @@ func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetNodeOperator(node return _CapabilityRegistry.Contract.GetNodeOperator(&_CapabilityRegistry.CallOpts, nodeOperatorId) } -func (_CapabilityRegistry *CapabilityRegistryCaller) GetNodeOperators(opts *bind.CallOpts) ([]CapabilityRegistryNodeOperator, error) { - var out []interface{} - err := _CapabilityRegistry.contract.Call(opts, &out, "getNodeOperators") - - if err != nil { - return *new([]CapabilityRegistryNodeOperator), err - } - - out0 := *abi.ConvertType(out[0], new([]CapabilityRegistryNodeOperator)).(*[]CapabilityRegistryNodeOperator) - - return out0, err - -} - -func (_CapabilityRegistry *CapabilityRegistrySession) GetNodeOperators() ([]CapabilityRegistryNodeOperator, error) { - return _CapabilityRegistry.Contract.GetNodeOperators(&_CapabilityRegistry.CallOpts) -} - -func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetNodeOperators() ([]CapabilityRegistryNodeOperator, error) { - return _CapabilityRegistry.Contract.GetNodeOperators(&_CapabilityRegistry.CallOpts) -} - -func (_CapabilityRegistry *CapabilityRegistryCaller) GetNodes(opts *bind.CallOpts) ([]CapabilityRegistryNodeInfo, []uint32, error) { - var out []interface{} - err := _CapabilityRegistry.contract.Call(opts, &out, "getNodes") - - if err != nil { - return *new([]CapabilityRegistryNodeInfo), *new([]uint32), err - } - - out0 := *abi.ConvertType(out[0], new([]CapabilityRegistryNodeInfo)).(*[]CapabilityRegistryNodeInfo) - out1 := *abi.ConvertType(out[1], new([]uint32)).(*[]uint32) - - return out0, out1, err - -} - -func (_CapabilityRegistry *CapabilityRegistrySession) GetNodes() ([]CapabilityRegistryNodeInfo, []uint32, error) { - return _CapabilityRegistry.Contract.GetNodes(&_CapabilityRegistry.CallOpts) -} - -func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetNodes() ([]CapabilityRegistryNodeInfo, []uint32, error) { - return _CapabilityRegistry.Contract.GetNodes(&_CapabilityRegistry.CallOpts) -} - func (_CapabilityRegistry *CapabilityRegistryCaller) IsCapabilityDeprecated(opts *bind.CallOpts, hashedCapabilityId [32]byte) (bool, error) { var out []interface{} err := _CapabilityRegistry.contract.Call(opts, &out, "isCapabilityDeprecated", hashedCapabilityId) @@ -540,15 +467,15 @@ func (_CapabilityRegistry *CapabilityRegistryTransactorSession) AddNodeOperators return _CapabilityRegistry.Contract.AddNodeOperators(&_CapabilityRegistry.TransactOpts, nodeOperators) } -func (_CapabilityRegistry *CapabilityRegistryTransactor) AddNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistryTransactor) AddNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { return _CapabilityRegistry.contract.Transact(opts, "addNodes", nodes) } -func (_CapabilityRegistry *CapabilityRegistrySession) AddNodes(nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistrySession) AddNodes(nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { return _CapabilityRegistry.Contract.AddNodes(&_CapabilityRegistry.TransactOpts, nodes) } -func (_CapabilityRegistry *CapabilityRegistryTransactorSession) AddNodes(nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistryTransactorSession) AddNodes(nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { return _CapabilityRegistry.Contract.AddNodes(&_CapabilityRegistry.TransactOpts, nodes) } @@ -564,18 +491,6 @@ func (_CapabilityRegistry *CapabilityRegistryTransactorSession) DeprecateCapabil return _CapabilityRegistry.Contract.DeprecateCapability(&_CapabilityRegistry.TransactOpts, hashedCapabilityId) } -func (_CapabilityRegistry *CapabilityRegistryTransactor) RemoveDONs(opts *bind.TransactOpts, donIds []uint32) (*types.Transaction, error) { - return _CapabilityRegistry.contract.Transact(opts, "removeDONs", donIds) -} - -func (_CapabilityRegistry *CapabilityRegistrySession) RemoveDONs(donIds []uint32) (*types.Transaction, error) { - return _CapabilityRegistry.Contract.RemoveDONs(&_CapabilityRegistry.TransactOpts, donIds) -} - -func (_CapabilityRegistry *CapabilityRegistryTransactorSession) RemoveDONs(donIds []uint32) (*types.Transaction, error) { - return _CapabilityRegistry.Contract.RemoveDONs(&_CapabilityRegistry.TransactOpts, donIds) -} - func (_CapabilityRegistry *CapabilityRegistryTransactor) RemoveNodeOperators(opts *bind.TransactOpts, nodeOperatorIds []*big.Int) (*types.Transaction, error) { return _CapabilityRegistry.contract.Transact(opts, "removeNodeOperators", nodeOperatorIds) } @@ -612,18 +527,6 @@ func (_CapabilityRegistry *CapabilityRegistryTransactorSession) TransferOwnershi return _CapabilityRegistry.Contract.TransferOwnership(&_CapabilityRegistry.TransactOpts, to) } -func (_CapabilityRegistry *CapabilityRegistryTransactor) UpdateDON(opts *bind.TransactOpts, donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilityRegistryCapabilityConfiguration, isPublic bool) (*types.Transaction, error) { - return _CapabilityRegistry.contract.Transact(opts, "updateDON", donId, nodes, capabilityConfigurations, isPublic) -} - -func (_CapabilityRegistry *CapabilityRegistrySession) UpdateDON(donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilityRegistryCapabilityConfiguration, isPublic bool) (*types.Transaction, error) { - return _CapabilityRegistry.Contract.UpdateDON(&_CapabilityRegistry.TransactOpts, donId, nodes, capabilityConfigurations, isPublic) -} - -func (_CapabilityRegistry *CapabilityRegistryTransactorSession) UpdateDON(donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilityRegistryCapabilityConfiguration, isPublic bool) (*types.Transaction, error) { - return _CapabilityRegistry.Contract.UpdateDON(&_CapabilityRegistry.TransactOpts, donId, nodes, capabilityConfigurations, isPublic) -} - func (_CapabilityRegistry *CapabilityRegistryTransactor) UpdateNodeOperators(opts *bind.TransactOpts, nodeOperatorIds []*big.Int, nodeOperators []CapabilityRegistryNodeOperator) (*types.Transaction, error) { return _CapabilityRegistry.contract.Transact(opts, "updateNodeOperators", nodeOperatorIds, nodeOperators) } @@ -636,15 +539,15 @@ func (_CapabilityRegistry *CapabilityRegistryTransactorSession) UpdateNodeOperat return _CapabilityRegistry.Contract.UpdateNodeOperators(&_CapabilityRegistry.TransactOpts, nodeOperatorIds, nodeOperators) } -func (_CapabilityRegistry *CapabilityRegistryTransactor) UpdateNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistryTransactor) UpdateNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { return _CapabilityRegistry.contract.Transact(opts, "updateNodes", nodes) } -func (_CapabilityRegistry *CapabilityRegistrySession) UpdateNodes(nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistrySession) UpdateNodes(nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { return _CapabilityRegistry.Contract.UpdateNodes(&_CapabilityRegistry.TransactOpts, nodes) } -func (_CapabilityRegistry *CapabilityRegistryTransactorSession) UpdateNodes(nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistryTransactorSession) UpdateNodes(nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { return _CapabilityRegistry.Contract.UpdateNodes(&_CapabilityRegistry.TransactOpts, nodes) } @@ -902,8 +805,8 @@ func (_CapabilityRegistry *CapabilityRegistryFilterer) ParseCapabilityDeprecated return event, nil } -type CapabilityRegistryConfigSetIterator struct { - Event *CapabilityRegistryConfigSet +type CapabilityRegistryDONAddedIterator struct { + Event *CapabilityRegistryDONAdded contract *bind.BoundContract event string @@ -914,7 +817,7 @@ type CapabilityRegistryConfigSetIterator struct { fail error } -func (it *CapabilityRegistryConfigSetIterator) Next() bool { +func (it *CapabilityRegistryDONAddedIterator) Next() bool { if it.fail != nil { return false @@ -923,7 +826,7 @@ func (it *CapabilityRegistryConfigSetIterator) Next() bool { if it.done { select { case log := <-it.logs: - it.Event = new(CapabilityRegistryConfigSet) + it.Event = new(CapabilityRegistryDONAdded) if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { it.fail = err return false @@ -938,7 +841,7 @@ func (it *CapabilityRegistryConfigSetIterator) Next() bool { select { case log := <-it.logs: - it.Event = new(CapabilityRegistryConfigSet) + it.Event = new(CapabilityRegistryDONAdded) if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { it.fail = err return false @@ -953,33 +856,33 @@ func (it *CapabilityRegistryConfigSetIterator) Next() bool { } } -func (it *CapabilityRegistryConfigSetIterator) Error() error { +func (it *CapabilityRegistryDONAddedIterator) Error() error { return it.fail } -func (it *CapabilityRegistryConfigSetIterator) Close() error { +func (it *CapabilityRegistryDONAddedIterator) Close() error { it.sub.Unsubscribe() return nil } -type CapabilityRegistryConfigSet struct { - DonId uint32 - ConfigCount uint32 - Raw types.Log +type CapabilityRegistryDONAdded struct { + DonId *big.Int + IsPublic bool + Raw types.Log } -func (_CapabilityRegistry *CapabilityRegistryFilterer) FilterConfigSet(opts *bind.FilterOpts) (*CapabilityRegistryConfigSetIterator, error) { +func (_CapabilityRegistry *CapabilityRegistryFilterer) FilterDONAdded(opts *bind.FilterOpts) (*CapabilityRegistryDONAddedIterator, error) { - logs, sub, err := _CapabilityRegistry.contract.FilterLogs(opts, "ConfigSet") + logs, sub, err := _CapabilityRegistry.contract.FilterLogs(opts, "DONAdded") if err != nil { return nil, err } - return &CapabilityRegistryConfigSetIterator{contract: _CapabilityRegistry.contract, event: "ConfigSet", logs: logs, sub: sub}, nil + return &CapabilityRegistryDONAddedIterator{contract: _CapabilityRegistry.contract, event: "DONAdded", logs: logs, sub: sub}, nil } -func (_CapabilityRegistry *CapabilityRegistryFilterer) WatchConfigSet(opts *bind.WatchOpts, sink chan<- *CapabilityRegistryConfigSet) (event.Subscription, error) { +func (_CapabilityRegistry *CapabilityRegistryFilterer) WatchDONAdded(opts *bind.WatchOpts, sink chan<- *CapabilityRegistryDONAdded) (event.Subscription, error) { - logs, sub, err := _CapabilityRegistry.contract.WatchLogs(opts, "ConfigSet") + logs, sub, err := _CapabilityRegistry.contract.WatchLogs(opts, "DONAdded") if err != nil { return nil, err } @@ -989,8 +892,8 @@ func (_CapabilityRegistry *CapabilityRegistryFilterer) WatchConfigSet(opts *bind select { case log := <-logs: - event := new(CapabilityRegistryConfigSet) - if err := _CapabilityRegistry.contract.UnpackLog(event, "ConfigSet", log); err != nil { + event := new(CapabilityRegistryDONAdded) + if err := _CapabilityRegistry.contract.UnpackLog(event, "DONAdded", log); err != nil { return err } event.Raw = log @@ -1011,9 +914,9 @@ func (_CapabilityRegistry *CapabilityRegistryFilterer) WatchConfigSet(opts *bind }), nil } -func (_CapabilityRegistry *CapabilityRegistryFilterer) ParseConfigSet(log types.Log) (*CapabilityRegistryConfigSet, error) { - event := new(CapabilityRegistryConfigSet) - if err := _CapabilityRegistry.contract.UnpackLog(event, "ConfigSet", log); err != nil { +func (_CapabilityRegistry *CapabilityRegistryFilterer) ParseDONAdded(log types.Log) (*CapabilityRegistryDONAdded, error) { + event := new(CapabilityRegistryDONAdded) + if err := _CapabilityRegistry.contract.UnpackLog(event, "DONAdded", log); err != nil { return nil, err } event.Raw = log @@ -1083,7 +986,6 @@ func (it *CapabilityRegistryNodeAddedIterator) Close() error { type CapabilityRegistryNodeAdded struct { P2pId [32]byte NodeOperatorId *big.Int - Signer [32]byte Raw types.Log } @@ -1694,7 +1596,7 @@ func (it *CapabilityRegistryNodeUpdatedIterator) Close() error { type CapabilityRegistryNodeUpdated struct { P2pId [32]byte NodeOperatorId *big.Int - Signer [32]byte + Signer common.Address Raw types.Log } @@ -2028,8 +1930,8 @@ func (_CapabilityRegistry *CapabilityRegistry) ParseLog(log types.Log) (generate return _CapabilityRegistry.ParseCapabilityAdded(log) case _CapabilityRegistry.abi.Events["CapabilityDeprecated"].ID: return _CapabilityRegistry.ParseCapabilityDeprecated(log) - case _CapabilityRegistry.abi.Events["ConfigSet"].ID: - return _CapabilityRegistry.ParseConfigSet(log) + case _CapabilityRegistry.abi.Events["DONAdded"].ID: + return _CapabilityRegistry.ParseDONAdded(log) case _CapabilityRegistry.abi.Events["NodeAdded"].ID: return _CapabilityRegistry.ParseNodeAdded(log) case _CapabilityRegistry.abi.Events["NodeOperatorAdded"].ID: @@ -2060,12 +1962,12 @@ func (CapabilityRegistryCapabilityDeprecated) Topic() common.Hash { return common.HexToHash("0xdcea1b78b6ddc31592a94607d537543fcaafda6cc52d6d5cc7bbfca1422baf21") } -func (CapabilityRegistryConfigSet) Topic() common.Hash { - return common.HexToHash("0xf264aae70bf6a9d90e68e0f9b393f4e7fbea67b063b0f336e0b36c1581703651") +func (CapabilityRegistryDONAdded) Topic() common.Hash { + return common.HexToHash("0xab55f4c8fb4335a586285ae209d1f1e17a7ccb22e1131963624434d98c8546a5") } func (CapabilityRegistryNodeAdded) Topic() common.Hash { - return common.HexToHash("0xc9296aa9b0951d8000e8ed7f2b5be30c5106de8df3dbedf9a57c93f5f9e4d7da") + return common.HexToHash("0x5bfe8a52ad26ac6ee7b0cd46d2fd92be04735a31c45ef8aa3d4b7ea1b61bbc1f") } func (CapabilityRegistryNodeOperatorAdded) Topic() common.Hash { @@ -2085,7 +1987,7 @@ func (CapabilityRegistryNodeRemoved) Topic() common.Hash { } func (CapabilityRegistryNodeUpdated) Topic() common.Hash { - return common.HexToHash("0xf101cfc54994c31624d25789378d71ec4dbdc533e26a4ecc6b7648f4798d0916") + return common.HexToHash("0x6bbba867c646be512c2f3241e65fdffdefd5528d7e7939649e06e10ee5addc3e") } func (CapabilityRegistryOwnershipTransferRequested) Topic() common.Hash { @@ -2105,22 +2007,16 @@ type CapabilityRegistryInterface interface { GetCapability(opts *bind.CallOpts, hashedId [32]byte) (CapabilityRegistryCapability, error) - GetDON(opts *bind.CallOpts, donId uint32) (CapabilityRegistryDONInfo, error) + GetDON(opts *bind.CallOpts, donId uint32) (uint32, bool, [][32]byte, []CapabilityRegistryCapabilityConfiguration, error) GetDONCapabilityConfig(opts *bind.CallOpts, donId uint32, capabilityId [32]byte) ([]byte, error) - GetDONs(opts *bind.CallOpts) ([]CapabilityRegistryDONInfo, error) - GetHashedCapabilityId(opts *bind.CallOpts, labelledName [32]byte, version [32]byte) ([32]byte, error) - GetNode(opts *bind.CallOpts, p2pId [32]byte) (CapabilityRegistryNodeInfo, uint32, error) + GetNode(opts *bind.CallOpts, p2pId [32]byte) (CapabilityRegistryNodeParams, uint32, error) GetNodeOperator(opts *bind.CallOpts, nodeOperatorId *big.Int) (CapabilityRegistryNodeOperator, error) - GetNodeOperators(opts *bind.CallOpts) ([]CapabilityRegistryNodeOperator, error) - - GetNodes(opts *bind.CallOpts) ([]CapabilityRegistryNodeInfo, []uint32, error) - IsCapabilityDeprecated(opts *bind.CallOpts, hashedCapabilityId [32]byte) (bool, error) Owner(opts *bind.CallOpts) (common.Address, error) @@ -2135,23 +2031,19 @@ type CapabilityRegistryInterface interface { AddNodeOperators(opts *bind.TransactOpts, nodeOperators []CapabilityRegistryNodeOperator) (*types.Transaction, error) - AddNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) + AddNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) DeprecateCapability(opts *bind.TransactOpts, hashedCapabilityId [32]byte) (*types.Transaction, error) - RemoveDONs(opts *bind.TransactOpts, donIds []uint32) (*types.Transaction, error) - RemoveNodeOperators(opts *bind.TransactOpts, nodeOperatorIds []*big.Int) (*types.Transaction, error) RemoveNodes(opts *bind.TransactOpts, removedNodeP2PIds [][32]byte) (*types.Transaction, error) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) - UpdateDON(opts *bind.TransactOpts, donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilityRegistryCapabilityConfiguration, isPublic bool) (*types.Transaction, error) - UpdateNodeOperators(opts *bind.TransactOpts, nodeOperatorIds []*big.Int, nodeOperators []CapabilityRegistryNodeOperator) (*types.Transaction, error) - UpdateNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) + UpdateNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) FilterCapabilityAdded(opts *bind.FilterOpts, hashedCapabilityId [][32]byte) (*CapabilityRegistryCapabilityAddedIterator, error) @@ -2165,11 +2057,11 @@ type CapabilityRegistryInterface interface { ParseCapabilityDeprecated(log types.Log) (*CapabilityRegistryCapabilityDeprecated, error) - FilterConfigSet(opts *bind.FilterOpts) (*CapabilityRegistryConfigSetIterator, error) + FilterDONAdded(opts *bind.FilterOpts) (*CapabilityRegistryDONAddedIterator, error) - WatchConfigSet(opts *bind.WatchOpts, sink chan<- *CapabilityRegistryConfigSet) (event.Subscription, error) + WatchDONAdded(opts *bind.WatchOpts, sink chan<- *CapabilityRegistryDONAdded) (event.Subscription, error) - ParseConfigSet(log types.Log) (*CapabilityRegistryConfigSet, error) + ParseDONAdded(log types.Log) (*CapabilityRegistryDONAdded, error) FilterNodeAdded(opts *bind.FilterOpts) (*CapabilityRegistryNodeAddedIterator, error) diff --git a/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 9167c638fbb..54d1355c62f 100644 --- a/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,4 +1,4 @@ GETH_VERSION: 1.13.8 -forwarder: ../../../contracts/solc/v0.8.19/KeystoneForwarder/KeystoneForwarder.abi ../../../contracts/solc/v0.8.19/KeystoneForwarder/KeystoneForwarder.bin 892c6ced16576bebd887eb581147c02139853d5143a0c9b77704efefd4ab7ec7 -keystone_capability_registry: ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.abi ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.bin 0a79d0eba13fd4a4b83d7618bb181c21c42222f3cc6c5a90a09302f685555033 +forwarder: ../../../contracts/solc/v0.8.19/KeystoneForwarder/KeystoneForwarder.abi ../../../contracts/solc/v0.8.19/KeystoneForwarder/KeystoneForwarder.bin ed9164cfe4619dff824b11df46b66f4c6834b2ca072923f10d9ebc57ce508ed8 +keystone_capability_registry: ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.abi ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.bin d4e0661491c2adc7f0d7553287c938fb32664b9f07770f6d57ae6511ce9884cd ocr3_capability: ../../../contracts/solc/v0.8.19/OCR3Capability/OCR3Capability.abi ../../../contracts/solc/v0.8.19/OCR3Capability/OCR3Capability.bin 9dcbdf55bd5729ba266148da3f17733eb592c871c2108ccca546618628fd9ad2 From bf9ac9f5d6aadf642bc672768237432d9daefdf4 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Wed, 5 Jun 2024 21:33:05 +0200 Subject: [PATCH 40/53] reset to develop --- .../keystone/generated/forwarder/forwarder.go | 56 ++--- .../keystone_capability_registry.go | 232 +++++++++++++----- ...rapper-dependency-versions-do-not-edit.txt | 4 +- 3 files changed, 196 insertions(+), 96 deletions(-) diff --git a/core/gethwrappers/keystone/generated/forwarder/forwarder.go b/core/gethwrappers/keystone/generated/forwarder/forwarder.go index fbdd024c64f..05ee44b415b 100644 --- a/core/gethwrappers/keystone/generated/forwarder/forwarder.go +++ b/core/gethwrappers/keystone/generated/forwarder/forwarder.go @@ -15,6 +15,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/event" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" ) @@ -31,8 +32,8 @@ var ( ) var KeystoneForwarderMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"DuplicateSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxSigners\",\"type\":\"uint256\"}],\"name\":\"ExcessSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FaultToleranceMustBePositive\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minSigners\",\"type\":\"uint256\"}],\"name\":\"InsufficientSigners\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"InvalidDonId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"InvalidSignature\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"received\",\"type\":\"uint256\"}],\"name\":\"InvalidSignatureCount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrantCall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"reportId\",\"type\":\"bytes32\"}],\"name\":\"ReportAlreadyProcessed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"workflowOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"result\",\"type\":\"bool\"}],\"name\":\"ReportProcessed\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"}],\"name\":\"getTransmitter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiverAddress\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"rawReport\",\"type\":\"bytes\"},{\"internalType\":\"bytes[]\",\"name\":\"signatures\",\"type\":\"bytes[]\"}],\"name\":\"report\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", - Bin: "0x608060405234801561001057600080fd5b5033806000816100675760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615610097576100978161009f565b505050610148565b336001600160a01b038216036100f75760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161005e565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b61151d806101576000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c806379ba50971161005b57806379ba5097146101175780638da5cb5b1461011f578063c0965dc31461013d578063f2fde38b1461015057600080fd5b8063134a46f014610082578063181f5a7714610097578063390d0b15146100df575b600080fd5b610095610090366004611106565b610163565b005b604080518082018252601781527f4b657973746f6e65466f7277617264657220312e302e30000000000000000000602082015290516100d691906111de565b60405180910390f35b6100f26100ed366004611221565b61057d565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100d6565b61009561060e565b60005473ffffffffffffffffffffffffffffffffffffffff166100f2565b61009561014b36600461124b565b61070b565b61009561015e3660046112fa565b610ded565b60015474010000000000000000000000000000000000000000900460ff16156101b8576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000017905560ff8316600003610234576040517f0743bae600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601f81111561027e576040517f61750f4000000000000000000000000000000000000000000000000000000000815260048101829052601f60248201526044015b60405180910390fd5b610289836003611344565b60ff1681116102e7578061029e846003611344565b6102a9906001611367565b6040517f9dd9e6d8000000000000000000000000000000000000000000000000000000008152600481019290925260ff166024820152604401610275565b60005b63ffffffff85166000908152600260205260409020600101548110156103885763ffffffff8516600090815260026020526040812060010180548390811061033457610334611380565b600091825260208083209091015463ffffffff891683526002808352604080852073ffffffffffffffffffffffffffffffffffffffff9093168552910190915281205550610381816113af565b90506102ea565b5063ffffffff841660009081526002602052604090206103ac906001018383610ffe565b5060005b8181101561050a5760008383838181106103cc576103cc611380565b90506020020160208101906103e191906112fa565b63ffffffff8716600090815260026020818152604080842073ffffffffffffffffffffffffffffffffffffffff8616855290920190529020549091501561046c576040517fe021c4f200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610275565b610477826001611367565b63ffffffff8716600090815260026020818152604080842073ffffffffffffffffffffffffffffffffffffffff909616808552868401835290842060ff959095169094559081526001938401805494850181558252902090910180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169091179055610503816113af565b90506103b0565b50505063ffffffff91909116600090815260026020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff169055565b6000806105df84846040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084901b1660208201526034810182905260009060540160405160208183030381529060405280519060200120905092915050565b60009081526003602052604090205473ffffffffffffffffffffffffffffffffffffffff169150505b92915050565b60015473ffffffffffffffffffffffffffffffffffffffff16331461068f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610275565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b60015474010000000000000000000000000000000000000000900460ff1615610760576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000017905560588310156107da576040517fb55ac75400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060008061081f88888080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610e0192505050565b63ffffffff8316600090815260026020526040812054949850929650909450925060ff9091169003610885576040517fea1b312900000000000000000000000000000000000000000000000000000000815263ffffffff84166004820152602401610275565b604080517fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060608c901b1660208083019190915260348083018690528351808403909101815260549092018352815191810191909120600081815260039092529190205473ffffffffffffffffffffffffffffffffffffffff1615610939576040517f1aac3d2900000000000000000000000000000000000000000000000000000000815260048101829052602401610275565b63ffffffff8416600090815260026020526040902054869061095f9060ff166001611367565b60ff16146109ca5763ffffffff841660009081526002602052604090205461098b9060ff166001611367565b6040517fd6022e8e00000000000000000000000000000000000000000000000000000000815260ff909116600482015260248101879052604401610275565b600089896040516109dc9291906113e7565b604051809103902090506109ee611086565b6000805b89811015610c5c576000806000610a608e8e86818110610a1457610a14611380565b9050602002810190610a2691906113f7565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610e2692505050565b925092509250600060018883868660405160008152602001604052604051610aa4949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa158015610ac6573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015163ffffffff8f1660009081526002602081815284832073ffffffffffffffffffffffffffffffffffffffff851684529091019052918220549850925060ff881690039050610b81576040517fbf18af4300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610275565b610b8c60018761145c565b955060008760ff8816601f8110610ba557610ba5611380565b602002015173ffffffffffffffffffffffffffffffffffffffff1614610c0f576040517fe021c4f200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610275565b80878760ff16601f8110610c2557610c25611380565b73ffffffffffffffffffffffffffffffffffffffff909216602092909202015250610c5592508391506113af9050565b90506109f2565b5050505060008a905060008173ffffffffffffffffffffffffffffffffffffffff1663ff5a027088868e8e6058908092610c9893929190611475565b6040518563ffffffff1660e01b8152600401610cb7949392919061149f565b600060405180830381600087803b158015610cd157600080fd5b505af1925050508015610ce2575060015b15610ceb575060015b604080518082018252338152821515602080830191825260008781526003909152839020915182549151151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00000000000000000000000000000000000000000090921673ffffffffffffffffffffffffffffffffffffffff91821617919091179091559051869186811691908f16907fdae8e752043eb5fc7e4a6eced57ceaf159548b630125ece9ffc41cfc952c208190610daf90861515815260200190565b60405180910390a45050600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16905550505050505050505050565b610df5610e86565b610dfe81610f09565b50565b602081015160408201516044830151606490930151919360e09190911c929160601c90565b60008060006041845114610e6857836040517f2adfdc3000000000000000000000000000000000000000000000000000000000815260040161027591906111de565b50505060208101516040820151606090920151909260009190911a90565b60005473ffffffffffffffffffffffffffffffffffffffff163314610f07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610275565b565b3373ffffffffffffffffffffffffffffffffffffffff821603610f88576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610275565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b828054828255906000526020600020908101928215611076579160200282015b828111156110765781547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84351617825560209092019160019091019061101e565b506110829291506110a5565b5090565b604051806103e00160405280601f906020820280368337509192915050565b5b8082111561108257600081556001016110a6565b60008083601f8401126110cc57600080fd5b50813567ffffffffffffffff8111156110e457600080fd5b6020830191508360208260051b85010111156110ff57600080fd5b9250929050565b6000806000806060858703121561111c57600080fd5b843563ffffffff8116811461113057600080fd5b9350602085013560ff8116811461114657600080fd5b9250604085013567ffffffffffffffff81111561116257600080fd5b61116e878288016110ba565b95989497509550505050565b6000815180845260005b818110156111a057602081850181015186830182015201611184565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815260006111f1602083018461117a565b9392505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461121c57600080fd5b919050565b6000806040838503121561123457600080fd5b61123d836111f8565b946020939093013593505050565b60008060008060006060868803121561126357600080fd5b61126c866111f8565b9450602086013567ffffffffffffffff8082111561128957600080fd5b818801915088601f83011261129d57600080fd5b8135818111156112ac57600080fd5b8960208285010111156112be57600080fd5b6020830196508095505060408801359150808211156112dc57600080fd5b506112e9888289016110ba565b969995985093965092949392505050565b60006020828403121561130c57600080fd5b6111f1826111f8565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60ff818116838216029081169081811461136057611360611315565b5092915050565b60ff818116838216019081111561060857610608611315565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036113e0576113e0611315565b5060010190565b8183823760009101908152919050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261142c57600080fd5b83018035915067ffffffffffffffff82111561144757600080fd5b6020019150368190038213156110ff57600080fd5b60ff828116828216039081111561060857610608611315565b6000808585111561148557600080fd5b8386111561149257600080fd5b5050820193919092039150565b84815273ffffffffffffffffffffffffffffffffffffffff8416602082015260606040820152816060820152818360808301376000818301608090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160101939250505056fea164736f6c6343000813000a", + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"}],\"name\":\"AlreadyProcessed\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"DuplicateSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxSigners\",\"type\":\"uint256\"}],\"name\":\"ExcessSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FaultToleranceMustBePositive\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minSigners\",\"type\":\"uint256\"}],\"name\":\"InsufficientSigners\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"InvalidDonId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"InvalidSignature\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"received\",\"type\":\"uint256\"}],\"name\":\"InvalidSignatureCount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"InvalidVersion\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrantCall\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"result\",\"type\":\"bool\"}],\"name\":\"ReportProcessed\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes2\",\"name\":\"reportId\",\"type\":\"bytes2\"}],\"name\":\"getTransmitter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiverAddress\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"rawReport\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"reportContext\",\"type\":\"bytes\"},{\"internalType\":\"bytes[]\",\"name\":\"signatures\",\"type\":\"bytes[]\"}],\"name\":\"report\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b5033806000816100675760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615610097576100978161009f565b505050610148565b336001600160a01b038216036100f75760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161005e565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6115ec806101576000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c806379ba50971161005b57806379ba5097146100f25780638864b864146100fa5780638da5cb5b146101d3578063f2fde38b146101f157600080fd5b80631128956514610082578063134a46f014610097578063181f5a77146100aa575b600080fd5b61009561009036600461119d565b610204565b005b6100956100a5366004611248565b61093e565b604080518082018252601781527f4b657973746f6e65466f7277617264657220312e302e30000000000000000000602082015290516100e99190611320565b60405180910390f35b610095610c9f565b6101ae61010836600461133a565b6040805160609490941b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660208086019190915260348501939093527fffff000000000000000000000000000000000000000000000000000000000000919091166054840152805160368185030181526056909301815282519282019290922060009081526003909152205473ffffffffffffffffffffffffffffffffffffffff1690565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100e9565b60005473ffffffffffffffffffffffffffffffffffffffff166101ae565b6100956101ff36600461139f565b610d9c565b60015474010000000000000000000000000000000000000000900460ff1615610259576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000179055606d8510156102d3576040517fb55ac75400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080600061031789898080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610db092505050565b63ffffffff831660009081526002602052604081205494975092955093505060ff9091169003610380576040517fea1b312900000000000000000000000000000000000000000000000000000000815263ffffffff831660048201526024015b60405180910390fd5b604080517fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060608d901b16602080830191909152603482018690527fffff000000000000000000000000000000000000000000000000000000000000841660548301528251808303603601815260569092018352815191810191909120600081815260039092529190205473ffffffffffffffffffffffffffffffffffffffff161561045b576040517f1a20d3e600000000000000000000000000000000000000000000000000000000815260048101829052602401610377565b63ffffffff831660009081526002602052604090205485906104819060ff1660016113e9565b60ff16146104ec5763ffffffff83166000908152600260205260409020546104ad9060ff1660016113e9565b6040517fd6022e8e00000000000000000000000000000000000000000000000000000000815260ff909116600482015260248101869052604401610377565b60008a8a6040516104fe929190611408565b604051908190038120610517918b908b90602001611418565b60405160208183030381529060405280519060200120905061053761102a565b6000805b888110156107a55760008060006105a98d8d8681811061055d5761055d611432565b905060200281019061056f9190611461565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610e5292505050565b9194509250905060006001886105c084601b6113e9565b6040805160008152602081018083529390935260ff90911690820152606081018690526080810185905260a0016020604051602081039080840390855afa15801561060f573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015163ffffffff8e1660009081526002602081815284832073ffffffffffffffffffffffffffffffffffffffff851684529091019052918220549850925060ff8816900390506106ca576040517fbf18af4300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610377565b6106d56001876114c6565b955060008760ff8816601f81106106ee576106ee611432565b602002015173ffffffffffffffffffffffffffffffffffffffff1614610758576040517fe021c4f200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610377565b80878760ff16601f811061076e5761076e611432565b73ffffffffffffffffffffffffffffffffffffffff90921660209290920201525061079e92508391506114df9050565b905061053b565b5050505060008b73ffffffffffffffffffffffffffffffffffffffff1663805f21328c8c602d90606d926107db93929190611517565b8e8e606d9080926107ee93929190611517565b6040518563ffffffff1660e01b815260040161080d949392919061158a565b600060405180830381600087803b15801561082757600080fd5b505af1925050508015610838575060015b15610841575060015b604080518082018252338152821515602080830191825260008681526003909152839020915182549151151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00000000000000000000000000000000000000000090921673ffffffffffffffffffffffffffffffffffffffff9182161791909117909155905186918e16907fbe015fd2fd7c1a00158e111095c794ae7030eb413d2a0990e5b78d3114df1d499061090090851515815260200190565b60405180910390a35050600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16905550505050505050505050565b610946610eb2565b8260ff16600003610983576040517f0743bae600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601f8111156109c8576040517f61750f4000000000000000000000000000000000000000000000000000000000815260048101829052601f6024820152604401610377565b6109d38360036115bc565b60ff168111610a3157806109e88460036115bc565b6109f39060016113e9565b6040517f9dd9e6d8000000000000000000000000000000000000000000000000000000008152600481019290925260ff166024820152604401610377565b60005b63ffffffff8516600090815260026020526040902060010154811015610ad25763ffffffff85166000908152600260205260408120600101805483908110610a7e57610a7e611432565b600091825260208083209091015463ffffffff891683526002808352604080852073ffffffffffffffffffffffffffffffffffffffff9093168552910190915281205550610acb816114df565b9050610a34565b5063ffffffff84166000908152600260205260409020610af6906001018383611049565b5060005b81811015610c54576000838383818110610b1657610b16611432565b9050602002016020810190610b2b919061139f565b63ffffffff8716600090815260026020818152604080842073ffffffffffffffffffffffffffffffffffffffff86168552909201905290205490915015610bb6576040517fe021c4f200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610377565b610bc18260016113e9565b63ffffffff8716600090815260026020818152604080842073ffffffffffffffffffffffffffffffffffffffff909616808552868401835290842060ff959095169094559081526001938401805494850181558252902090910180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169091179055610c4d816114df565b9050610afa565b50505063ffffffff91909116600090815260026020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055565b60015473ffffffffffffffffffffffffffffffffffffffff163314610d20576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610377565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610da4610eb2565b610dad81610f35565b50565b60008060008084600081518110610dc957610dc9611432565b60209101015160f81c600114610e2a5784600081518110610dec57610dec611432565b01602001516040517f7207be2000000000000000000000000000000000000000000000000000000000815260f89190911c6004820152602401610377565b50505050602181015160458201516049830151608b90930151919360e091821c9390911c9190565b60008060006041845114610e9457836040517f2adfdc300000000000000000000000000000000000000000000000000000000081526004016103779190611320565b50505060208101516040820151606090920151909260009190911a90565b60005473ffffffffffffffffffffffffffffffffffffffff163314610f33576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610377565b565b3373ffffffffffffffffffffffffffffffffffffffff821603610fb4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610377565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b604051806103e00160405280601f906020820280368337509192915050565b8280548282559060005260206000209081019282156110c1579160200282015b828111156110c15781547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff843516178255602090920191600190910190611069565b506110cd9291506110d1565b5090565b5b808211156110cd57600081556001016110d2565b803573ffffffffffffffffffffffffffffffffffffffff8116811461110a57600080fd5b919050565b60008083601f84011261112157600080fd5b50813567ffffffffffffffff81111561113957600080fd5b60208301915083602082850101111561115157600080fd5b9250929050565b60008083601f84011261116a57600080fd5b50813567ffffffffffffffff81111561118257600080fd5b6020830191508360208260051b850101111561115157600080fd5b60008060008060008060006080888a0312156111b857600080fd5b6111c1886110e6565b9650602088013567ffffffffffffffff808211156111de57600080fd5b6111ea8b838c0161110f565b909850965060408a013591508082111561120357600080fd5b61120f8b838c0161110f565b909650945060608a013591508082111561122857600080fd5b506112358a828b01611158565b989b979a50959850939692959293505050565b6000806000806060858703121561125e57600080fd5b843563ffffffff8116811461127257600080fd5b9350602085013560ff8116811461128857600080fd5b9250604085013567ffffffffffffffff8111156112a457600080fd5b6112b087828801611158565b95989497509550505050565b6000815180845260005b818110156112e2576020818501810151868301820152016112c6565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b60208152600061133360208301846112bc565b9392505050565b60008060006060848603121561134f57600080fd5b611358846110e6565b92506020840135915060408401357fffff0000000000000000000000000000000000000000000000000000000000008116811461139457600080fd5b809150509250925092565b6000602082840312156113b157600080fd5b611333826110e6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60ff8181168382160190811115611402576114026113ba565b92915050565b8183823760009101908152919050565b838152818360208301376000910160200190815292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261149657600080fd5b83018035915067ffffffffffffffff8211156114b157600080fd5b60200191503681900382131561115157600080fd5b60ff8281168282160390811115611402576114026113ba565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203611510576115106113ba565b5060010190565b6000808585111561152757600080fd5b8386111561153457600080fd5b5050820193919092039150565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b60408152600061159e604083018688611541565b82810360208401526115b1818587611541565b979650505050505050565b60ff81811683821602908116908181146115d8576115d86113ba565b509291505056fea164736f6c6343000813000a", } var KeystoneForwarderABI = KeystoneForwarderMetaData.ABI @@ -171,9 +172,9 @@ func (_KeystoneForwarder *KeystoneForwarderTransactorRaw) Transact(opts *bind.Tr return _KeystoneForwarder.Contract.contract.Transact(opts, method, params...) } -func (_KeystoneForwarder *KeystoneForwarderCaller) GetTransmitter(opts *bind.CallOpts, receiver common.Address, workflowExecutionId [32]byte) (common.Address, error) { +func (_KeystoneForwarder *KeystoneForwarderCaller) GetTransmitter(opts *bind.CallOpts, receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (common.Address, error) { var out []interface{} - err := _KeystoneForwarder.contract.Call(opts, &out, "getTransmitter", receiver, workflowExecutionId) + err := _KeystoneForwarder.contract.Call(opts, &out, "getTransmitter", receiver, workflowExecutionId, reportId) if err != nil { return *new(common.Address), err @@ -185,12 +186,12 @@ func (_KeystoneForwarder *KeystoneForwarderCaller) GetTransmitter(opts *bind.Cal } -func (_KeystoneForwarder *KeystoneForwarderSession) GetTransmitter(receiver common.Address, workflowExecutionId [32]byte) (common.Address, error) { - return _KeystoneForwarder.Contract.GetTransmitter(&_KeystoneForwarder.CallOpts, receiver, workflowExecutionId) +func (_KeystoneForwarder *KeystoneForwarderSession) GetTransmitter(receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (common.Address, error) { + return _KeystoneForwarder.Contract.GetTransmitter(&_KeystoneForwarder.CallOpts, receiver, workflowExecutionId, reportId) } -func (_KeystoneForwarder *KeystoneForwarderCallerSession) GetTransmitter(receiver common.Address, workflowExecutionId [32]byte) (common.Address, error) { - return _KeystoneForwarder.Contract.GetTransmitter(&_KeystoneForwarder.CallOpts, receiver, workflowExecutionId) +func (_KeystoneForwarder *KeystoneForwarderCallerSession) GetTransmitter(receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (common.Address, error) { + return _KeystoneForwarder.Contract.GetTransmitter(&_KeystoneForwarder.CallOpts, receiver, workflowExecutionId, reportId) } func (_KeystoneForwarder *KeystoneForwarderCaller) Owner(opts *bind.CallOpts) (common.Address, error) { @@ -249,16 +250,16 @@ func (_KeystoneForwarder *KeystoneForwarderTransactorSession) AcceptOwnership() return _KeystoneForwarder.Contract.AcceptOwnership(&_KeystoneForwarder.TransactOpts) } -func (_KeystoneForwarder *KeystoneForwarderTransactor) Report(opts *bind.TransactOpts, receiverAddress common.Address, rawReport []byte, signatures [][]byte) (*types.Transaction, error) { - return _KeystoneForwarder.contract.Transact(opts, "report", receiverAddress, rawReport, signatures) +func (_KeystoneForwarder *KeystoneForwarderTransactor) Report(opts *bind.TransactOpts, receiverAddress common.Address, rawReport []byte, reportContext []byte, signatures [][]byte) (*types.Transaction, error) { + return _KeystoneForwarder.contract.Transact(opts, "report", receiverAddress, rawReport, reportContext, signatures) } -func (_KeystoneForwarder *KeystoneForwarderSession) Report(receiverAddress common.Address, rawReport []byte, signatures [][]byte) (*types.Transaction, error) { - return _KeystoneForwarder.Contract.Report(&_KeystoneForwarder.TransactOpts, receiverAddress, rawReport, signatures) +func (_KeystoneForwarder *KeystoneForwarderSession) Report(receiverAddress common.Address, rawReport []byte, reportContext []byte, signatures [][]byte) (*types.Transaction, error) { + return _KeystoneForwarder.Contract.Report(&_KeystoneForwarder.TransactOpts, receiverAddress, rawReport, reportContext, signatures) } -func (_KeystoneForwarder *KeystoneForwarderTransactorSession) Report(receiverAddress common.Address, rawReport []byte, signatures [][]byte) (*types.Transaction, error) { - return _KeystoneForwarder.Contract.Report(&_KeystoneForwarder.TransactOpts, receiverAddress, rawReport, signatures) +func (_KeystoneForwarder *KeystoneForwarderTransactorSession) Report(receiverAddress common.Address, rawReport []byte, reportContext []byte, signatures [][]byte) (*types.Transaction, error) { + return _KeystoneForwarder.Contract.Report(&_KeystoneForwarder.TransactOpts, receiverAddress, rawReport, reportContext, signatures) } func (_KeystoneForwarder *KeystoneForwarderTransactor) SetConfig(opts *bind.TransactOpts, donId uint32, f uint8, signers []common.Address) (*types.Transaction, error) { @@ -619,50 +620,41 @@ func (it *KeystoneForwarderReportProcessedIterator) Close() error { type KeystoneForwarderReportProcessed struct { Receiver common.Address - WorkflowOwner common.Address WorkflowExecutionId [32]byte Result bool Raw types.Log } -func (_KeystoneForwarder *KeystoneForwarderFilterer) FilterReportProcessed(opts *bind.FilterOpts, receiver []common.Address, workflowOwner []common.Address, workflowExecutionId [][32]byte) (*KeystoneForwarderReportProcessedIterator, error) { +func (_KeystoneForwarder *KeystoneForwarderFilterer) FilterReportProcessed(opts *bind.FilterOpts, receiver []common.Address, workflowExecutionId [][32]byte) (*KeystoneForwarderReportProcessedIterator, error) { var receiverRule []interface{} for _, receiverItem := range receiver { receiverRule = append(receiverRule, receiverItem) } - var workflowOwnerRule []interface{} - for _, workflowOwnerItem := range workflowOwner { - workflowOwnerRule = append(workflowOwnerRule, workflowOwnerItem) - } var workflowExecutionIdRule []interface{} for _, workflowExecutionIdItem := range workflowExecutionId { workflowExecutionIdRule = append(workflowExecutionIdRule, workflowExecutionIdItem) } - logs, sub, err := _KeystoneForwarder.contract.FilterLogs(opts, "ReportProcessed", receiverRule, workflowOwnerRule, workflowExecutionIdRule) + logs, sub, err := _KeystoneForwarder.contract.FilterLogs(opts, "ReportProcessed", receiverRule, workflowExecutionIdRule) if err != nil { return nil, err } return &KeystoneForwarderReportProcessedIterator{contract: _KeystoneForwarder.contract, event: "ReportProcessed", logs: logs, sub: sub}, nil } -func (_KeystoneForwarder *KeystoneForwarderFilterer) WatchReportProcessed(opts *bind.WatchOpts, sink chan<- *KeystoneForwarderReportProcessed, receiver []common.Address, workflowOwner []common.Address, workflowExecutionId [][32]byte) (event.Subscription, error) { +func (_KeystoneForwarder *KeystoneForwarderFilterer) WatchReportProcessed(opts *bind.WatchOpts, sink chan<- *KeystoneForwarderReportProcessed, receiver []common.Address, workflowExecutionId [][32]byte) (event.Subscription, error) { var receiverRule []interface{} for _, receiverItem := range receiver { receiverRule = append(receiverRule, receiverItem) } - var workflowOwnerRule []interface{} - for _, workflowOwnerItem := range workflowOwner { - workflowOwnerRule = append(workflowOwnerRule, workflowOwnerItem) - } var workflowExecutionIdRule []interface{} for _, workflowExecutionIdItem := range workflowExecutionId { workflowExecutionIdRule = append(workflowExecutionIdRule, workflowExecutionIdItem) } - logs, sub, err := _KeystoneForwarder.contract.WatchLogs(opts, "ReportProcessed", receiverRule, workflowOwnerRule, workflowExecutionIdRule) + logs, sub, err := _KeystoneForwarder.contract.WatchLogs(opts, "ReportProcessed", receiverRule, workflowExecutionIdRule) if err != nil { return nil, err } @@ -726,7 +718,7 @@ func (KeystoneForwarderOwnershipTransferred) Topic() common.Hash { } func (KeystoneForwarderReportProcessed) Topic() common.Hash { - return common.HexToHash("0xdae8e752043eb5fc7e4a6eced57ceaf159548b630125ece9ffc41cfc952c2081") + return common.HexToHash("0xbe015fd2fd7c1a00158e111095c794ae7030eb413d2a0990e5b78d3114df1d49") } func (_KeystoneForwarder *KeystoneForwarder) Address() common.Address { @@ -734,7 +726,7 @@ func (_KeystoneForwarder *KeystoneForwarder) Address() common.Address { } type KeystoneForwarderInterface interface { - GetTransmitter(opts *bind.CallOpts, receiver common.Address, workflowExecutionId [32]byte) (common.Address, error) + GetTransmitter(opts *bind.CallOpts, receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (common.Address, error) Owner(opts *bind.CallOpts) (common.Address, error) @@ -742,7 +734,7 @@ type KeystoneForwarderInterface interface { AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) - Report(opts *bind.TransactOpts, receiverAddress common.Address, rawReport []byte, signatures [][]byte) (*types.Transaction, error) + Report(opts *bind.TransactOpts, receiverAddress common.Address, rawReport []byte, reportContext []byte, signatures [][]byte) (*types.Transaction, error) SetConfig(opts *bind.TransactOpts, donId uint32, f uint8, signers []common.Address) (*types.Transaction, error) @@ -760,9 +752,9 @@ type KeystoneForwarderInterface interface { ParseOwnershipTransferred(log types.Log) (*KeystoneForwarderOwnershipTransferred, error) - FilterReportProcessed(opts *bind.FilterOpts, receiver []common.Address, workflowOwner []common.Address, workflowExecutionId [][32]byte) (*KeystoneForwarderReportProcessedIterator, error) + FilterReportProcessed(opts *bind.FilterOpts, receiver []common.Address, workflowExecutionId [][32]byte) (*KeystoneForwarderReportProcessedIterator, error) - WatchReportProcessed(opts *bind.WatchOpts, sink chan<- *KeystoneForwarderReportProcessed, receiver []common.Address, workflowOwner []common.Address, workflowExecutionId [][32]byte) (event.Subscription, error) + WatchReportProcessed(opts *bind.WatchOpts, sink chan<- *KeystoneForwarderReportProcessed, receiver []common.Address, workflowExecutionId [][32]byte) (event.Subscription, error) ParseReportProcessed(log types.Log) (*KeystoneForwarderReportProcessed, error) diff --git a/core/gethwrappers/keystone/generated/keystone_capability_registry/keystone_capability_registry.go b/core/gethwrappers/keystone/generated/keystone_capability_registry/keystone_capability_registry.go index 3357bf8f5fe..ab4b89d18e1 100644 --- a/core/gethwrappers/keystone/generated/keystone_capability_registry/keystone_capability_registry.go +++ b/core/gethwrappers/keystone/generated/keystone_capability_registry/keystone_capability_registry.go @@ -15,6 +15,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/event" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" ) @@ -42,21 +43,29 @@ type CapabilityRegistryCapabilityConfiguration struct { Config []byte } -type CapabilityRegistryNodeOperator struct { - Admin common.Address - Name string +type CapabilityRegistryDONInfo struct { + Id uint32 + ConfigCount uint32 + IsPublic bool + NodeP2PIds [][32]byte + CapabilityConfigurations []CapabilityRegistryCapabilityConfiguration } -type CapabilityRegistryNodeParams struct { +type CapabilityRegistryNodeInfo struct { NodeOperatorId uint32 - Signer common.Address + Signer [32]byte P2pId [32]byte HashedCapabilityIds [][32]byte } +type CapabilityRegistryNodeOperator struct { + Admin common.Address + Name string +} + var CapabilityRegistryMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"name\":\"AccessForbidden\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CapabilityAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityIsDeprecated\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONCapability\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONNode\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"proposedConfigurationContract\",\"type\":\"address\"}],\"name\":\"InvalidCapabilityConfigurationContractInterface\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"name\":\"InvalidNodeCapabilities\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeOperatorAdmin\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"InvalidNodeP2PId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"lengthOne\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lengthTwo\",\"type\":\"uint256\"}],\"name\":\"LengthMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"NodeDoesNotSupportCapability\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDeprecated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"donId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"}],\"name\":\"DONAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"}],\"name\":\"NodeAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"}],\"name\":\"NodeOperatorRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"NodeRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"NodeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability\",\"name\":\"capability\",\"type\":\"tuple\"}],\"name\":\"addCapability\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"nodes\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"}],\"name\":\"addDON\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"addNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeParams[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"addNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"deprecateCapability\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCapabilities\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedId\",\"type\":\"bytes32\"}],\"name\":\"getCapability\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"getDON\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"},{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"getDONCapabilityConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"}],\"name\":\"getHashedCapabilityId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"getNode\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeParams\",\"name\":\"\",\"type\":\"tuple\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"}],\"name\":\"getNodeOperator\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"isCapabilityDeprecated\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint256[]\"}],\"name\":\"removeNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"removedNodeP2PIds\",\"type\":\"bytes32[]\"}],\"name\":\"removeNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"updateNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeParams[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"updateNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x6080604052600b805463ffffffff191660011790553480156200002157600080fd5b503380600081620000795760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000ac57620000ac81620000b5565b50505062000160565b336001600160a01b038216036200010f5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000070565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6134b380620001706000396000f3fe608060405234801561001057600080fd5b50600436106101775760003560e01c806350e03b16116100d85780638da5cb5b1161008c578063b06e07a711610066578063b06e07a71461038f578063ddbe4f82146103a2578063f2fde38b146103b757600080fd5b80638da5cb5b146103345780639cb7c5f41461035c578063ae3c241c1461037c57600080fd5b806365c14dc7116100bd57806365c14dc7146102f95780636ae5c5911461031957806379ba50971461032c57600080fd5b806350e03b16146102d35780635840cd45146102e657600080fd5b8063235374051161012f57806336b402fb1161011457806336b402fb14610257578063398f37731461029f57806350c946fe146102b257600080fd5b806323537405146102215780632c01a1e81461024457600080fd5b8063125700111161016057806312570011146101a4578063181f5a77146101cc5780631cdf63431461020e57600080fd5b80630c5801e31461017c578063117392ce14610191575b600080fd5b61018f61018a3660046126db565b6103ca565b005b61018f61019f366004612747565b6106db565b6101b76101b236600461275f565b610926565b60405190151581526020015b60405180910390f35b60408051808201909152601881527f4361706162696c697479526567697374727920312e302e30000000000000000060208201525b6040516101c391906127dc565b61018f61021c3660046127ef565b610939565b61023461022f36600461284a565b6109fc565b6040516101c394939291906128a0565b61018f6102523660046127ef565b610c0f565b610291610265366004612958565b604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b6040519081526020016101c3565b61018f6102ad3660046127ef565b610e8d565b6102c56102c036600461275f565b611026565b6040516101c392919061297a565b61018f6102e13660046127ef565b6110e8565b61018f6102f43660046127ef565b6115c9565b61030c61030736600461275f565b611a63565b6040516101c39190612a1c565b61018f610327366004612a6d565b611b49565b61018f611f5d565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101c3565b61036f61036a36600461275f565b61205a565b6040516101c39190612b90565b61018f61038a36600461275f565b612104565b61020161039d366004612b9e565b6121cf565b6103aa612287565b6040516101c39190612bc8565b61018f6103c5366004612c38565b6123cc565b828114610412576040517fab8b67c600000000000000000000000000000000000000000000000000000000815260048101849052602481018290526044015b60405180910390fd5b6000805473ffffffffffffffffffffffffffffffffffffffff16905b848110156106d357600086868381811061044a5761044a612c55565b905060200201359050600085858481811061046757610467612c55565b90506020028101906104799190612c84565b61048290612d8c565b805190915073ffffffffffffffffffffffffffffffffffffffff166104d3576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805173ffffffffffffffffffffffffffffffffffffffff16331480159061051057503373ffffffffffffffffffffffffffffffffffffffff851614155b15610547576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805160008381526007602052604090205473ffffffffffffffffffffffffffffffffffffffff90811691161415806105f9575060208082015160405161058d92016127dc565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201206000868152600783529290922091926105e0926001019101612ea5565b6040516020818303038152906040528051906020012014155b156106c0578051600083815260076020908152604090912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9093169290921782558201516001909101906106669082612f94565b50806000015173ffffffffffffffffffffffffffffffffffffffff167f14c8f513e8a6d86d2d16b0cb64976de4e72386c4f8068eca3b7354373f8fe97a8383602001516040516106b79291906130ae565b60405180910390a25b5050806106cc906130f6565b905061042e565b505050505050565b6106e36123e0565b60408051823560208281019190915280840135828401528251808303840181526060909201909252805191012061071b600382612463565b15610752576040517fe288638f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006107646080840160608501612c38565b73ffffffffffffffffffffffffffffffffffffffff16146108cf5761078f6080830160608401612c38565b73ffffffffffffffffffffffffffffffffffffffff163b158061086f57506107bd6080830160608401612c38565b6040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527f884efe6100000000000000000000000000000000000000000000000000000000600482015273ffffffffffffffffffffffffffffffffffffffff91909116906301ffc9a790602401602060405180830381865afa158015610849573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061086d919061312e565b155b156108cf576108846080830160608401612c38565b6040517fabb5e3fd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401610409565b6108da60038261247e565b50600081815260026020526040902082906108f5828261314b565b505060405181907f65610e5677eedff94555572640e442f89848a109ef8593fa927ac30b2565ff0690600090a25050565b6000610933600583612463565b92915050565b6109416123e0565b60005b818110156109f757600083838381811061096057610960612c55565b60209081029290920135600081815260079093526040832080547fffffffffffffffffffffffff00000000000000000000000000000000000000001681559093509190506109b16001830182612641565b50506040518181527f1e5877d7b3001d1569bf733b76c7eceda58bd6c031e5b8d0b7042308ba2e9d4f9060200160405180910390a1506109f0816130f6565b9050610944565b505050565b63ffffffff81166000908152600960205260408120819060609081908390610a269060030161248a565b90506000815167ffffffffffffffff811115610a4457610a44612cc2565b604051908082528060200260200182016040528015610a8a57816020015b604080518082019091526000815260606020820152815260200190600190039081610a625790505b50905060005b8151811015610bc9576040518060400160405280848381518110610ab657610ab6612c55565b60200260200101518152602001600960008b63ffffffff1663ffffffff1681526020019081526020016000206005016000868581518110610af957610af9612c55565b602002602001015181526020019081526020016000208054610b1a90612e58565b80601f0160208091040260200160405190810160405280929190818152602001828054610b4690612e58565b8015610b935780601f10610b6857610100808354040283529160200191610b93565b820191906000526020600020905b815481529060010190602001808311610b7657829003601f168201915b5050505050815250828281518110610bad57610bad612c55565b602002602001018190525080610bc2906130f6565b9050610a90565b5063ffffffff8781166000908152600960205260409020805491821691640100000000900460ff1690610bfe9060010161248a565b919750955093509150509193509193565b6000805473ffffffffffffffffffffffffffffffffffffffff163314905b82811015610e87576000848483818110610c4957610c49612c55565b60209081029290920135600081815260089093526040909220549192505068010000000000000000900473ffffffffffffffffffffffffffffffffffffffff16151580610cc5576040517f64e2ee9200000000000000000000000000000000000000000000000000000000815260048101839052602401610409565b60008281526008602090815260408083205463ffffffff168352600782528083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff1682526001810180549293919291840191610d1f90612e58565b80601f0160208091040260200160405190810160405280929190818152602001828054610d4b90612e58565b8015610d985780601f10610d6d57610100808354040283529160200191610d98565b820191906000526020600020905b815481529060010190602001808311610d7b57829003601f168201915b505050505081525050905084158015610dc85750805173ffffffffffffffffffffffffffffffffffffffff163314155b15610dff576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526008602052604080822080547fffffffff0000000000000000000000000000000000000000000000000000000016815560010191909155517f5254e609a97bab37b7cc79fe128f85c097bd6015c6e1624ae0ba392eb975320590610e6b9085815260200190565b60405180910390a150505080610e80906130f6565b9050610c2d565b50505050565b610e956123e0565b60005b818110156109f7576000838383818110610eb457610eb4612c55565b9050602002810190610ec69190612c84565b610ecf90612d8c565b805190915073ffffffffffffffffffffffffffffffffffffffff16610f20576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600a54604080518082018252835173ffffffffffffffffffffffffffffffffffffffff908116825260208086015181840190815260008681526007909252939020825181547fffffffffffffffffffffffff000000000000000000000000000000000000000016921691909117815591519091906001820190610fa39082612f94565b50905050600a60008154610fb6906130f6565b909155508151602083015160405173ffffffffffffffffffffffffffffffffffffffff909216917fda6697b182650034bd205cdc2dbfabb06bdb3a0a83a2b45bfefa3c4881284e0b9161100b918591906130ae565b60405180910390a250508061101f906130f6565b9050610e98565b604080516080810182526000808252602082018190529181019190915260608082015260408051608081018252600084815260086020908152838220805463ffffffff808216865273ffffffffffffffffffffffffffffffffffffffff6801000000000000000083041684870152600183015486880152640100000000909104168352600201905291822060608201906110bf9061248a565b905260009384526008602052604090932054929364010000000090930463ffffffff1692915050565b60005b818110156109f757600083838381811061110757611107612c55565b905060200281019061111991906131cd565b61112290613201565b9050600061114560005473ffffffffffffffffffffffffffffffffffffffff1690565b825163ffffffff1660009081526007602090815260408083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff908116835260018201805496909116331496509394919390928401916111a290612e58565b80601f01602080910402602001604051908101604052809291908181526020018280546111ce90612e58565b801561121b5780601f106111f05761010080835404028352916020019161121b565b820191906000526020600020905b8154815290600101906020018083116111fe57829003601f168201915b50505050508152505090508115801561124b5750805173ffffffffffffffffffffffffffffffffffffffff163314155b15611282576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408084015160009081526008602052205468010000000000000000900473ffffffffffffffffffffffffffffffffffffffff161515806112f75783604001516040517f64e2ee9200000000000000000000000000000000000000000000000000000000815260040161040991815260200190565b602084015173ffffffffffffffffffffffffffffffffffffffff16611348576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060840151805160000361138a57806040517f3748d4c600000000000000000000000000000000000000000000000000000000815260040161040991906132dd565b60408581015160009081526008602052208054640100000000900463ffffffff169060046113b7836132f0565b82546101009290920a63ffffffff8181021990931691831602179091556040878101516000908152600860205290812054640100000000900490911691505b82518110156114c25761142c83828151811061141457611414612c55565b6020026020010151600361246390919063ffffffff16565b61146457826040517f3748d4c600000000000000000000000000000000000000000000000000000000815260040161040991906132dd565b6114b183828151811061147957611479612c55565b6020908102919091018101516040808b015160009081526008845281812063ffffffff80891683526002909101909452209161247e16565b506114bb816130f6565b90506113f6565b5085516040808801805160009081526008602090815283822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff968716179055825180835284832060010155808b018051845184529285902080547fffffffff0000000000000000000000000000000000000000ffffffffffffffff166801000000000000000073ffffffffffffffffffffffffffffffffffffffff9586160217905592518b5193518551918252939095169085015216908201527f6bbba867c646be512c2f3241e65fdffdefd5528d7e7939649e06e10ee5addc3e9060600160405180910390a1505050505050806115c2906130f6565b90506110eb565b60005b818110156109f75760008383838181106115e8576115e8612c55565b90506020028101906115fa91906131cd565b61160390613201565b9050600061162660005473ffffffffffffffffffffffffffffffffffffffff1690565b825163ffffffff1660009081526007602090815260408083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff9081168352600182018054969091163314965093949193909284019161168390612e58565b80601f01602080910402602001604051908101604052809291908181526020018280546116af90612e58565b80156116fc5780601f106116d1576101008083540402835291602001916116fc565b820191906000526020600020905b8154815290600101906020018083116116df57829003601f168201915b50505050508152505090508115801561172c5750805173ffffffffffffffffffffffffffffffffffffffff163314155b15611763576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408084015160009081526008602052205468010000000000000000900473ffffffffffffffffffffffffffffffffffffffff16151580806117a757506040840151155b156117e65783604001516040517f64e2ee9200000000000000000000000000000000000000000000000000000000815260040161040991815260200190565b602084015173ffffffffffffffffffffffffffffffffffffffff16611837576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060840151805160000361187957806040517f3748d4c600000000000000000000000000000000000000000000000000000000815260040161040991906132dd565b604085810151600090815260086020522080546004906118a690640100000000900463ffffffff166132f0565b82546101009290920a63ffffffff81810219909316918316021790915560408681015160009081526008602052908120546401000000009004909116905b82518110156119605761190283828151811061141457611414612c55565b61193a57826040517f3748d4c600000000000000000000000000000000000000000000000000000000815260040161040991906132dd565b61194f83828151811061147957611479612c55565b50611959816130f6565b90506118e4565b5085516040808801805160009081526008602090815283822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff968716179055825180835284832060010155808b0151835183529184902080547fffffffff0000000000000000000000000000000000000000ffffffffffffffff166801000000000000000073ffffffffffffffffffffffffffffffffffffffff9094169390930292909217909155905189518351918252909316908301527f5bfe8a52ad26ac6ee7b0cd46d2fd92be04735a31c45ef8aa3d4b7ea1b61bbc1f910160405180910390a150505050505080611a5c906130f6565b90506115cc565b6040805180820190915260008152606060208201526000828152600760209081526040918290208251808401909352805473ffffffffffffffffffffffffffffffffffffffff1683526001810180549192840191611ac090612e58565b80601f0160208091040260200160405190810160405280929190818152602001828054611aec90612e58565b8015611b395780601f10611b0e57610100808354040283529160200191611b39565b820191906000526020600020905b815481529060010190602001808311611b1c57829003601f168201915b5050505050815250509050919050565b611b516123e0565b600b5463ffffffff16600081815260096020526040812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000168317640100000000851515021790555b85811015611c76576000878783818110611bb857611bb8612c55565b905060200201359050611bf581600960008663ffffffff1663ffffffff16815260200190815260200160002060010161246390919063ffffffff16565b15611c3b576040517f636e405700000000000000000000000000000000000000000000000000000000815263ffffffff8416600482015260248101829052604401610409565b63ffffffff8084166000908152600960205260409020611c6391600190910190839061247e16565b505080611c6f906130f6565b9050611b9c565b5060005b83811015611ed45736858583818110611c9557611c95612c55565b9050602002810190611ca79190612c84565b90508035611cb6600382612463565b611cef576040517fe181733f00000000000000000000000000000000000000000000000000000000815260048101829052602401610409565b611cfa600582612463565b15611d34576040517ff7d7a29400000000000000000000000000000000000000000000000000000000815260048101829052602401610409565b63ffffffff8085166000908152600960205260409020611d5c91600390910190839061246316565b15611da2576040517f3927d08000000000000000000000000000000000000000000000000000000000815263ffffffff8516600482015260248101829052604401610409565b60005b88811015611e5a5760008a8a83818110611dc157611dc1612c55565b602090810292909201356000818152600884526040808220805463ffffffff6401000000009091048116845260029091019095529020909350611e0992909150859061246316565b611e49576040517fa7e792500000000000000000000000000000000000000000000000000000000081526004810182905260248101849052604401610409565b50611e53816130f6565b9050611da5565b5063ffffffff8085166000908152600960205260409020611e8391600390910190839061247e16565b50611e916020830183613313565b63ffffffff86166000908152600960209081526040808320868452600501909152902091611ec0919083613378565b50505080611ecd906130f6565b9050611c7a565b50600b8054600090611eeb9063ffffffff166132f0565b91906101000a81548163ffffffff021916908363ffffffff1602179055507fab55f4c8fb4335a586285ae209d1f1e17a7ccb22e1131963624434d98c8546a58183604051611f4d92919063ffffffff9290921682521515602082015260400190565b60405180910390a1505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611fde576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610409565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b604080516080808201835260008083526020808401829052838501829052606084018290528582526002808252918590208551938401865280548452600180820154928501929092529182015493949293919284019160ff16908111156120c3576120c3612af1565b60018111156120d4576120d4612af1565b815260029190910154610100900473ffffffffffffffffffffffffffffffffffffffff1660209091015292915050565b61210c6123e0565b612117600382612463565b612150576040517fe181733f00000000000000000000000000000000000000000000000000000000815260048101829052602401610409565b61215b600582612463565b15612195576040517ff7d7a29400000000000000000000000000000000000000000000000000000000815260048101829052602401610409565b6121a060058261247e565b5060405181907fdcea1b78b6ddc31592a94607d537543fcaafda6cc52d6d5cc7bbfca1422baf2190600090a250565b63ffffffff82166000908152600960209081526040808320848452600501909152902080546060919061220190612e58565b80601f016020809104026020016040519081016040528092919081815260200182805461222d90612e58565b801561227a5780601f1061224f5761010080835404028352916020019161227a565b820191906000526020600020905b81548152906001019060200180831161225d57829003601f168201915b5050505050905092915050565b60606000612295600361248a565b905060006122a36005612497565b82516122af9190613493565b67ffffffffffffffff8111156122c7576122c7612cc2565b60405190808252806020026020018201604052801561233757816020015b6040805160808101825260008082526020808301829052928201819052606082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816122e55790505b5090506000805b83518110156123c357600084828151811061235b5761235b612c55565b6020026020010151905061237981600561246390919063ffffffff16565b6123b2576123868161205a565b84848151811061239857612398612c55565b602002602001018190525082806123ae906130f6565b9350505b506123bc816130f6565b905061233e565b50909392505050565b6123d46123e0565b6123dd816124a1565b50565b60005473ffffffffffffffffffffffffffffffffffffffff163314612461576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610409565b565b600081815260018301602052604081205415155b9392505050565b60006124778383612596565b60606000612477836125e5565b6000610933825490565b3373ffffffffffffffffffffffffffffffffffffffff821603612520576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610409565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60008181526001830160205260408120546125dd57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610933565b506000610933565b60608160000180548060200260200160405190810160405280929190818152602001828054801561263557602002820191906000526020600020905b815481526020019060010190808311612621575b50505050509050919050565b50805461264d90612e58565b6000825580601f1061265d575050565b601f0160209004906000526020600020908101906123dd91905b8082111561268b5760008155600101612677565b5090565b60008083601f8401126126a157600080fd5b50813567ffffffffffffffff8111156126b957600080fd5b6020830191508360208260051b85010111156126d457600080fd5b9250929050565b600080600080604085870312156126f157600080fd5b843567ffffffffffffffff8082111561270957600080fd5b6127158883890161268f565b9096509450602087013591508082111561272e57600080fd5b5061273b8782880161268f565b95989497509550505050565b60006080828403121561275957600080fd5b50919050565b60006020828403121561277157600080fd5b5035919050565b6000815180845260005b8181101561279e57602081850181015186830182015201612782565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815260006124776020830184612778565b6000806020838503121561280257600080fd5b823567ffffffffffffffff81111561281957600080fd5b6128258582860161268f565b90969095509350505050565b803563ffffffff8116811461284557600080fd5b919050565b60006020828403121561285c57600080fd5b61247782612831565b600081518084526020808501945080840160005b8381101561289557815187529582019590820190600101612879565b509495945050505050565b63ffffffff85168152600060208515158184015260406080818501526128c96080850187612865565b8481036060860152855180825283820190600581901b8301850185890160005b83811015612946578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001855281518051845288015188840188905261293388850182612778565b95890195935050908701906001016128e9565b50909c9b505050505050505050505050565b6000806040838503121561296b57600080fd5b50508035926020909101359150565b60408152600060c0820163ffffffff8551166040840152602073ffffffffffffffffffffffffffffffffffffffff81870151166060850152604086015160808501526060860151608060a086015282815180855260e0870191508383019450600092505b808310156129fe57845182529383019360019290920191908301906129de565b5063ffffffff8716838701529350612a139050565b50509392505050565b6020815273ffffffffffffffffffffffffffffffffffffffff825116602082015260006020830151604080840152612a576060840182612778565b949350505050565b80151581146123dd57600080fd5b600080600080600060608688031215612a8557600080fd5b853567ffffffffffffffff80821115612a9d57600080fd5b612aa989838a0161268f565b90975095506020880135915080821115612ac257600080fd5b50612acf8882890161268f565b9094509250506040860135612ae381612a5f565b809150509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8051825260208101516020830152604081015160028110612b6a577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b604083015260609081015173ffffffffffffffffffffffffffffffffffffffff16910152565b608081016109338284612b20565b60008060408385031215612bb157600080fd5b612bba83612831565b946020939093013593505050565b6020808252825182820181905260009190848201906040850190845b81811015612c0a57612bf7838551612b20565b9284019260809290920191600101612be4565b50909695505050505050565b73ffffffffffffffffffffffffffffffffffffffff811681146123dd57600080fd5b600060208284031215612c4a57600080fd5b813561247781612c16565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112612cb857600080fd5b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715612d1457612d14612cc2565b60405290565b6040516080810167ffffffffffffffff81118282101715612d1457612d14612cc2565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715612d8457612d84612cc2565b604052919050565b600060408236031215612d9e57600080fd5b612da6612cf1565b8235612db181612c16565b815260208381013567ffffffffffffffff80821115612dcf57600080fd5b9085019036601f830112612de257600080fd5b813581811115612df457612df4612cc2565b612e24847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601612d3d565b91508082523684828501011115612e3a57600080fd5b80848401858401376000908201840152918301919091525092915050565b600181811c90821680612e6c57607f821691505b602082108103612759577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000602080835260008454612eb981612e58565b80848701526040600180841660008114612eda5760018114612f1257612f40565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008516838a01528284151560051b8a01019550612f40565b896000528660002060005b85811015612f385781548b8201860152908301908801612f1d565b8a0184019650505b509398975050505050505050565b601f8211156109f757600081815260208120601f850160051c81016020861015612f755750805b601f850160051c820191505b818110156106d357828155600101612f81565b815167ffffffffffffffff811115612fae57612fae612cc2565b612fc281612fbc8454612e58565b84612f4e565b602080601f8311600181146130155760008415612fdf5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556106d3565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561306257888601518255948401946001909101908401613043565b508582101561309e57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b828152604060208201526000612a576040830184612778565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613127576131276130c7565b5060010190565b60006020828403121561314057600080fd5b815161247781612a5f565b81358155602082013560018201556002810160408301356002811061316f57600080fd5b8154606085013561317f81612c16565b74ffffffffffffffffffffffffffffffffffffffff008160081b1660ff84167fffffffffffffffffffffff000000000000000000000000000000000000000000841617178455505050505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112612cb857600080fd5b60006080823603121561321357600080fd5b61321b612d1a565b61322483612831565b815260208084013561323581612c16565b8282015260408481013590830152606084013567ffffffffffffffff8082111561325e57600080fd5b9085019036601f83011261327157600080fd5b81358181111561328357613283612cc2565b8060051b9150613294848301612d3d565b81815291830184019184810190368411156132ae57600080fd5b938501935b838510156132cc578435825293850193908501906132b3565b606087015250939695505050505050565b6020815260006124776020830184612865565b600063ffffffff808316818103613309576133096130c7565b6001019392505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261334857600080fd5b83018035915067ffffffffffffffff82111561336357600080fd5b6020019150368190038213156126d457600080fd5b67ffffffffffffffff83111561339057613390612cc2565b6133a48361339e8354612e58565b83612f4e565b6000601f8411600181146133f657600085156133c05750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b17835561348c565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156134455786850135825560209485019460019092019101613425565b5086821015613480577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b81810381811115610933576109336130c756fea164736f6c6343000813000a", + ABI: "[{\"inputs\":[],\"name\":\"AccessForbidden\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CapabilityAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityIsDeprecated\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"DONDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONCapability\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONNode\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"proposedConfigurationContract\",\"type\":\"address\"}],\"name\":\"InvalidCapabilityConfigurationContractInterface\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"name\":\"InvalidNodeCapabilities\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeOperatorAdmin\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"InvalidNodeP2PId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"lengthOne\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lengthTwo\",\"type\":\"uint256\"}],\"name\":\"LengthMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"NodeDoesNotSupportCapability\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDeprecated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"}],\"name\":\"NodeAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"}],\"name\":\"NodeOperatorRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"NodeRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"}],\"name\":\"NodeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability\",\"name\":\"capability\",\"type\":\"tuple\"}],\"name\":\"addCapability\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"nodes\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"}],\"name\":\"addDON\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"addNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeInfo[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"addNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"deprecateCapability\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCapabilities\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedId\",\"type\":\"bytes32\"}],\"name\":\"getCapability\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"enumCapabilityRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilityRegistry.Capability\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"getDON\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"id\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodeP2PIds\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"}],\"internalType\":\"structCapabilityRegistry.DONInfo\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"getDONCapabilityConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDONs\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"id\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodeP2PIds\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"}],\"internalType\":\"structCapabilityRegistry.DONInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"labelledName\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"}],\"name\":\"getHashedCapabilityId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"getNode\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeInfo\",\"name\":\"\",\"type\":\"tuple\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"nodeOperatorId\",\"type\":\"uint256\"}],\"name\":\"getNodeOperator\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getNodeOperators\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getNodes\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"},{\"internalType\":\"uint32[]\",\"name\":\"\",\"type\":\"uint32[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"isCapabilityDeprecated\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32[]\",\"name\":\"donIds\",\"type\":\"uint32[]\"}],\"name\":\"removeDONs\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint256[]\"}],\"name\":\"removeNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"removedNodeP2PIds\",\"type\":\"bytes32[]\"}],\"name\":\"removeNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodes\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilityRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"}],\"name\":\"updateDON\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilityRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"updateNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilityRegistry.NodeInfo[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"updateNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x6080604052601280546001600160401b0319166401000000011790553480156200002857600080fd5b503380600081620000805760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000b357620000b381620000bc565b50505062000167565b336001600160a01b03821603620001165760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000077565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b61437580620001776000396000f3fe608060405234801561001057600080fd5b50600436106101ae5760003560e01c806365c14dc7116100ee5780639cb7c5f411610097578063c63239c511610071578063c63239c514610413578063ddbe4f8214610426578063e29581aa1461043b578063f2fde38b1461045157600080fd5b80639cb7c5f4146103cd578063ae3c241c146103ed578063b06e07a71461040057600080fd5b806373ac22b4116100c857806373ac22b41461038a57806379ba50971461039d5780638da5cb5b146103a557600080fd5b806365c14dc71461034257806366acaa33146103625780636ae5c5911461037757600080fd5b8063214502431161015b57806336b402fb1161013557806336b402fb146102b3578063398f3773146102fb57806350c946fe1461030e5780635e65e3091461032f57600080fd5b8063214502431461026b57806323537405146102805780632c01a1e8146102a057600080fd5b8063181f5a771161018c578063181f5a77146102035780631cdf6343146102455780631d05394c1461025857600080fd5b80630c5801e3146101b3578063117392ce146101c857806312570011146101db575b600080fd5b6101c66101c136600461328f565b610464565b005b6101c66101d63660046132fb565b610775565b6101ee6101e9366004613313565b6109c0565b60405190151581526020015b60405180910390f35b60408051808201909152601881527f4361706162696c697479526567697374727920312e302e30000000000000000060208201525b6040516101fa9190613390565b6101c66102533660046133a3565b6109d3565b6101c66102663660046133a3565b610aa3565b610273610be3565b6040516101fa91906134f5565b61029361028e36600461358e565b610d32565b6040516101fa91906135a9565b6101c66102ae3660046133a3565b610d65565b6102ed6102c13660046135bc565b604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b6040519081526020016101fa565b6101c66103093660046133a3565b611009565b61032161031c366004613313565b6111cc565b6040516101fa92919061361f565b6101c661033d3660046133a3565b611201565b610355610350366004613313565b611704565b6040516101fa919061367c565b61036a6117ea565b6040516101fa919061368f565b6101c6610385366004613710565b6119a8565b6101c66103983660046133a3565b611a4b565b6101c6611eb1565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101fa565b6103e06103db366004613313565b611fae565b6040516101fa9190613833565b6101c66103fb366004613313565b612058565b61023861040e366004613841565b612123565b6101c661042136600461386b565b6121f8565b61042e612287565b6040516101fa91906138fe565b61044361244d565b6040516101fa92919061394c565b6101c661045f366004613a2d565b6125d8565b8281146104ac576040517fab8b67c600000000000000000000000000000000000000000000000000000000815260048101849052602481018290526044015b60405180910390fd5b6000805473ffffffffffffffffffffffffffffffffffffffff16905b8481101561076d5760008686838181106104e4576104e4613a4a565b905060200201359050600085858481811061050157610501613a4a565b90506020028101906105139190613a79565b61051c90613b81565b805190915073ffffffffffffffffffffffffffffffffffffffff1661056d576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805173ffffffffffffffffffffffffffffffffffffffff1633148015906105aa57503373ffffffffffffffffffffffffffffffffffffffff851614155b156105e1576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516000838152600f602052604090205473ffffffffffffffffffffffffffffffffffffffff908116911614158061069357506020808201516040516106279201613390565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201206000868152600f835292909220919261067a926001019101613c9a565b6040516020818303038152906040528051906020012014155b1561075a5780516000838152600f6020908152604090912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9093169290921782558201516001909101906107009082613d89565b50806000015173ffffffffffffffffffffffffffffffffffffffff167f14c8f513e8a6d86d2d16b0cb64976de4e72386c4f8068eca3b7354373f8fe97a838360200151604051610751929190613ea3565b60405180910390a25b50508061076690613eeb565b90506104c8565b505050505050565b61077d6125ec565b6040805182356020828101919091528084013582840152825180830384018152606090920190925280519101206107b560038261266f565b156107ec576040517fe288638f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006107fe6080840160608501613a2d565b73ffffffffffffffffffffffffffffffffffffffff1614610969576108296080830160608401613a2d565b73ffffffffffffffffffffffffffffffffffffffff163b158061090957506108576080830160608401613a2d565b6040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527f73e8b41d00000000000000000000000000000000000000000000000000000000600482015273ffffffffffffffffffffffffffffffffffffffff91909116906301ffc9a790602401602060405180830381865afa1580156108e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109079190613f23565b155b156109695761091e6080830160608401613a2d565b6040517fabb5e3fd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016104a3565b61097460038261268a565b506000818152600260205260409020829061098f8282613f40565b505060405181907f65610e5677eedff94555572640e442f89848a109ef8593fa927ac30b2565ff0690600090a25050565b60006109cd60058361266f565b92915050565b6109db6125ec565b60005b81811015610a9e5760008383838181106109fa576109fa613a4a565b602090810292909201356000818152600f9093526040832080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168155909350919050610a4b60018301826131f5565b50610a59905060078261268a565b506040518181527f1e5877d7b3001d1569bf733b76c7eceda58bd6c031e5b8d0b7042308ba2e9d4f9060200160405180910390a150610a9781613eeb565b90506109de565b505050565b610aab6125ec565b60005b81811015610a9e576000838383818110610aca57610aca613a4a565b9050602002016020810190610adf919061358e565b63ffffffff808216600090815260116020526040812080549394509264010000000090049091169003610b46576040517f2b62be9b00000000000000000000000000000000000000000000000000000000815263ffffffff831660048201526024016104a3565b63ffffffff808316600081815260116020526040902080547fffffffffffffffffffffffffffffffffffffffffffffff000000000000000000169055610b90916009919061268a16565b506040805163ffffffff84168152600060208201527ff264aae70bf6a9d90e68e0f9b393f4e7fbea67b063b0f336e0b36c1581703651910160405180910390a1505080610bdc90613eeb565b9050610aae565b601254606090640100000000900463ffffffff1660006001610c056009612696565b601254610c209190640100000000900463ffffffff16613fc2565b610c2a9190613fc2565b67ffffffffffffffff811115610c4257610c42613ab7565b604051908082528060200260200182016040528015610cb857816020015b6040805160a08101825260008082526020808301829052928201526060808201819052608082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909201910181610c605790505b509050600060015b838163ffffffff161015610d2957610ce2600963ffffffff8084169061266f16565b610d1957610cef816126a0565b838381518110610d0157610d01613a4a565b602002602001018190525081610d1690613eeb565b91505b610d2281613fd5565b9050610cc0565b50909392505050565b6040805160a08101825260008082526020820181905291810191909152606080820181905260808201526109cd826126a0565b6000805473ffffffffffffffffffffffffffffffffffffffff163314905b82811015611003576000848483818110610d9f57610d9f613a4a565b602090810292909201356000818152601090935260409092206001015491925050151580610dfc576040517f64e2ee92000000000000000000000000000000000000000000000000000000008152600481018390526024016104a3565b60008281526010602090815260408083205463ffffffff168352600f82528083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff1682526001810180549293919291840191610e5690613c4d565b80601f0160208091040260200160405190810160405280929190818152602001828054610e8290613c4d565b8015610ecf5780601f10610ea457610100808354040283529160200191610ecf565b820191906000526020600020905b815481529060010190602001808311610eb257829003601f168201915b505050505081525050905084158015610eff5750805173ffffffffffffffffffffffffffffffffffffffff163314155b15610f36576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260106020526040902060010154610f5490600b90612939565b50600083815260106020526040902060020154610f7390600d90612939565b5060008381526010602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001681556001810183905560020191909155517f5254e609a97bab37b7cc79fe128f85c097bd6015c6e1624ae0ba392eb975320590610fe79085815260200190565b60405180910390a150505080610ffc90613eeb565b9050610d83565b50505050565b6110116125ec565b60005b81811015610a9e57600083838381811061103057611030613a4a565b90506020028101906110429190613a79565b61104b90613b81565b805190915073ffffffffffffffffffffffffffffffffffffffff1661109c576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601254604080518082018252835173ffffffffffffffffffffffffffffffffffffffff908116825260208086015181840190815263ffffffff9095166000818152600f909252939020825181547fffffffffffffffffffffffff000000000000000000000000000000000000000016921691909117815592519192909160018201906111289082613d89565b5050601280549091506000906111439063ffffffff16613fd5565b91906101000a81548163ffffffff021916908363ffffffff160217905550816000015173ffffffffffffffffffffffffffffffffffffffff167fda6697b182650034bd205cdc2dbfabb06bdb3a0a83a2b45bfefa3c4881284e0b8284602001516040516111b1929190613ea3565b60405180910390a25050806111c590613eeb565b9050611014565b60408051608081018252600080825260208201819052918101829052606080820152906111f883612945565b91509150915091565b60005b81811015610a9e57600083838381811061122057611220613a4a565b90506020028101906112329190613ff8565b61123b9061402c565b9050600061125e60005473ffffffffffffffffffffffffffffffffffffffff1690565b825163ffffffff166000908152600f602090815260408083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff908116835260018201805496909116331496509394919390928401916112bb90613c4d565b80601f01602080910402602001604051908101604052809291908181526020018280546112e790613c4d565b80156113345780601f1061130957610100808354040283529160200191611334565b820191906000526020600020905b81548152906001019060200180831161131757829003601f168201915b5050505050815250509050811580156113645750805173ffffffffffffffffffffffffffffffffffffffff163314155b1561139b576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040808401516000908152601060205220600101541515806113f15783604001516040517f64e2ee920000000000000000000000000000000000000000000000000000000081526004016104a391815260200190565b6020840151158061143757508360200151601060008660400151815260200190815260200160002060010154141580156114375750602084015161143790600b9061266f565b1561146e576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606084015180516000036114b057806040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016104a391906140ff565b60408581015160009081526010602052208054640100000000900463ffffffff169060046114dd83613fd5565b82546101009290920a63ffffffff8181021990931691831602179091556040878101516000908152601060205290812054640100000000900490911691505b82518110156115e85761155283828151811061153a5761153a613a4a565b6020026020010151600361266f90919063ffffffff16565b61158a57826040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016104a391906140ff565b6115d783828151811061159f5761159f613a4a565b6020908102919091018101516040808b015160009081526010845281812063ffffffff80891683526003909101909452209161268a16565b506115e181613eeb565b905061151c565b5085516040808801805160009081526010602090815283822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff9096169590951790945581518082528382206002015581518152828120600190810154948b015192518252929020909101541461169d5761166c600b82612939565b50602080880180516040808b015160009081526010909452909220600101919091555161169b90600b9061268a565b505b60408781015188516020808b0151845193845263ffffffff909216908301528183015290517ff101cfc54994c31624d25789378d71ec4dbdc533e26a4ecc6b7648f4798d09169181900360600190a150505050505050806116fd90613eeb565b9050611204565b6040805180820190915260008152606060208201526000828152600f60209081526040918290208251808401909352805473ffffffffffffffffffffffffffffffffffffffff168352600181018054919284019161176190613c4d565b80601f016020809104026020016040519081016040528092919081815260200182805461178d90613c4d565b80156117da5780601f106117af576101008083540402835291602001916117da565b820191906000526020600020905b8154815290600101906020018083116117bd57829003601f168201915b5050505050815250509050919050565b60125460609063ffffffff16600060016118046007612696565b601254611817919063ffffffff16613fc2565b6118219190613fc2565b67ffffffffffffffff81111561183957611839613ab7565b60405190808252806020026020018201604052801561187f57816020015b6040805180820190915260008152606060208201528152602001906001900390816118575790505b509050600060015b8363ffffffff16811015610d29576118a060078261266f565b611998576000818152600f60209081526040918290208251808401909352805473ffffffffffffffffffffffffffffffffffffffff16835260018101805491928401916118ec90613c4d565b80601f016020809104026020016040519081016040528092919081815260200182805461191890613c4d565b80156119655780601f1061193a57610100808354040283529160200191611965565b820191906000526020600020905b81548152906001019060200180831161194857829003601f168201915b50505050508152505083838151811061198057611980613a4a565b60200260200101819052508161199590613eeb565b91505b6119a181613eeb565b9050611887565b6119b06125ec565b601254640100000000900463ffffffff16600081815260116020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001682179055611a0781600188888888886129ea565b60128054600490611a2590640100000000900463ffffffff16613fd5565b91906101000a81548163ffffffff021916908363ffffffff160217905550505050505050565b60005b81811015610a9e576000838383818110611a6a57611a6a613a4a565b9050602002810190611a7c9190613ff8565b611a859061402c565b90506000611aa860005473ffffffffffffffffffffffffffffffffffffffff1690565b825163ffffffff166000908152600f602090815260408083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff90811683526001820180549690911633149650939491939092840191611b0590613c4d565b80601f0160208091040260200160405190810160405280929190818152602001828054611b3190613c4d565b8015611b7e5780601f10611b5357610100808354040283529160200191611b7e565b820191906000526020600020905b815481529060010190602001808311611b6157829003601f168201915b505050505081525050905081158015611bae5750805173ffffffffffffffffffffffffffffffffffffffff163314155b15611be5576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408084015160009081526010602052206001015415158080611c0a57506040840151155b15611c495783604001516040517f64e2ee920000000000000000000000000000000000000000000000000000000081526004016104a391815260200190565b60208401511580611c6657506020840151611c6690600b9061266f565b15611c9d576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60608401518051600003611cdf57806040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016104a391906140ff565b60408581015160009081526010602052208054600490611d0c90640100000000900463ffffffff16613fd5565b82546101009290920a63ffffffff81810219909316918316021790915560408681015160009081526010602052908120546401000000009004909116905b8251811015611dc657611d6883828151811061153a5761153a613a4a565b611da057826040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016104a391906140ff565b611db583828151811061159f5761159f613a4a565b50611dbf81613eeb565b9050611d4a565b5085516040808801805160009081526010602090815283822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff9687161790558251808352848320600201558a018051925182529290206001015551611e3891600b919061268a16565b506040860151611e4a90600d9061268a565b5060408681015187516020808a0151845193845263ffffffff909216908301528183015290517fc9296aa9b0951d8000e8ed7f2b5be30c5106de8df3dbedf9a57c93f5f9e4d7da9181900360600190a150505050505080611eaa90613eeb565b9050611a4e565b60015473ffffffffffffffffffffffffffffffffffffffff163314611f32576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016104a3565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b604080516080808201835260008083526020808401829052838501829052606084018290528582526002808252918590208551938401865280548452600180820154928501929092529182015493949293919284019160ff169081111561201757612017613794565b600181111561202857612028613794565b815260029190910154610100900473ffffffffffffffffffffffffffffffffffffffff1660209091015292915050565b6120606125ec565b61206b60038261266f565b6120a4576040517fe181733f000000000000000000000000000000000000000000000000000000008152600481018290526024016104a3565b6120af60058261266f565b156120e9576040517ff7d7a294000000000000000000000000000000000000000000000000000000008152600481018290526024016104a3565b6120f460058261268a565b5060405181907fdcea1b78b6ddc31592a94607d537543fcaafda6cc52d6d5cc7bbfca1422baf2190600090a250565b63ffffffff8083166000908152601160209081526040808320805464010000000090049094168084526001909401825280832085845260030190915290208054606092919061217190613c4d565b80601f016020809104026020016040519081016040528092919081815260200182805461219d90613c4d565b80156121ea5780601f106121bf576101008083540402835291602001916121ea565b820191906000526020600020905b8154815290600101906020018083116121cd57829003601f168201915b505050505091505092915050565b6122006125ec565b63ffffffff808716600090815260116020526040812054640100000000900490911690819003612264576040517f2b62be9b00000000000000000000000000000000000000000000000000000000815263ffffffff881660048201526024016104a3565b61227e8761227183613fd5565b92508288888888886129ea565b50505050505050565b606060006122956003612e76565b905060006122a36005612696565b82516122af9190613fc2565b67ffffffffffffffff8111156122c7576122c7613ab7565b60405190808252806020026020018201604052801561233757816020015b6040805160808101825260008082526020808301829052928201819052606082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816122e55790505b5090506000805b8351811015610d2957600084828151811061235b5761235b613a4a565b6020026020010151905061237981600561266f90919063ffffffff16565b61243c576002600082815260200190815260200160002060405180608001604052908160008201548152602001600182015481526020016002820160009054906101000a900460ff1660018111156123d3576123d3613794565b60018111156123e4576123e4613794565b815260029190910154610100900473ffffffffffffffffffffffffffffffffffffffff16602090910152845185908590811061242257612422613a4a565b6020026020010181905250828061243890613eeb565b9350505b5061244681613eeb565b905061233e565b606080600061245c600d612e76565b90506000815167ffffffffffffffff81111561247a5761247a613ab7565b6040519080825280602002602001820160405280156124e957816020015b60408051608081018252600080825260208083018290529282015260608082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816124985790505b5090506000825167ffffffffffffffff81111561250857612508613ab7565b604051908082528060200260200182016040528015612531578160200160208202803683370190505b50905060005b83518110156125cd57600084828151811061255457612554613a4a565b6020026020010151905060008061256a83612945565b915091508186858151811061258157612581613a4a565b60200260200101819052508085858151811061259f5761259f613a4a565b602002602001019063ffffffff16908163ffffffff1681525050505050806125c690613eeb565b9050612537565b509094909350915050565b6125e06125ec565b6125e981612e83565b50565b60005473ffffffffffffffffffffffffffffffffffffffff16331461266d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016104a3565b565b600081815260018301602052604081205415155b9392505050565b60006126838383612f78565b60006109cd825490565b6040805160a081018252600080825260208083018290528284018290526060808401819052608084015263ffffffff85811683526011825284832080546401000000009004909116808452600190910182528483206002810180548751818602810186019098528088529596929591949390919083018282801561274357602002820191906000526020600020905b81548152602001906001019080831161272f575b505050505090506000815167ffffffffffffffff81111561276657612766613ab7565b6040519080825280602002602001820160405280156127ac57816020015b6040805180820190915260008152606060208201528152602001906001900390816127845790505b50905060005b81518110156128cd5760405180604001604052808483815181106127d8576127d8613a4a565b602002602001015181526020018560030160008685815181106127fd576127fd613a4a565b60200260200101518152602001908152602001600020805461281e90613c4d565b80601f016020809104026020016040519081016040528092919081815260200182805461284a90613c4d565b80156128975780601f1061286c57610100808354040283529160200191612897565b820191906000526020600020905b81548152906001019060200180831161287a57829003601f168201915b50505050508152508282815181106128b1576128b1613a4a565b6020026020010181905250806128c690613eeb565b90506127b2565b506040805160a08101825263ffffffff888116600081815260116020818152868320548086168752948b168187015292909152905268010000000000000000900460ff161515918101919091526060810161292785612e76565b81526020019190915295945050505050565b60006126838383612fc7565b604080516080810182526000808252602082018190529181019190915260608082015260408051608081018252600084815260106020908152838220805463ffffffff8082168652600183015484870152600283015486880152640100000000909104168352600301905291822060608201906129c190612e76565b905260009384526010602052604090932054929364010000000090930463ffffffff1692915050565b63ffffffff8088166000908152601160209081526040808320938a16835260019093019052908120905b85811015612ad857612a41878783818110612a3157612a31613a4a565b859260209091020135905061266f565b15612aa25788878783818110612a5957612a59613a4a565b6040517f636e405700000000000000000000000000000000000000000000000000000000815263ffffffff909416600485015260200291909101356024830152506044016104a3565b612ac7878783818110612ab757612ab7613a4a565b859260209091020135905061268a565b50612ad181613eeb565b9050612a14565b5060005b83811015612db75736858583818110612af757612af7613a4a565b9050602002810190612b099190613a79565b9050612b176003823561266f565b612b50576040517fe181733f000000000000000000000000000000000000000000000000000000008152813560048201526024016104a3565b612b5c6005823561266f565b15612b96576040517ff7d7a294000000000000000000000000000000000000000000000000000000008152813560048201526024016104a3565b8035600090815260038401602052604081208054612bb390613c4d565b90501115612bfc576040517f3927d08000000000000000000000000000000000000000000000000000000000815263ffffffff8b166004820152813560248201526044016104a3565b60005b87811015612d0e57612ca38235601060008c8c86818110612c2257612c22613a4a565b9050602002013581526020019081526020016000206003016000601060008e8e88818110612c5257612c52613a4a565b90506020020135815260200190815260200160002060000160049054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200190815260200160002061266f90919063ffffffff16565b612cfe57888882818110612cb957612cb9613a4a565b6040517fa7e7925000000000000000000000000000000000000000000000000000000000815260209091029290920135600483015250823560248201526044016104a3565b612d0781613eeb565b9050612bff565b5060028301805460018101825560009182526020918290208335910155612d3790820182614137565b82356000908152600386016020526040902091612d5591908361419c565b50612da68a8a83358b8b612d6c6020880188614137565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506130ba92505050565b50612db081613eeb565b9050612adc565b5063ffffffff88811660008181526011602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff1668010000000000000000881515027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff1617640100000000958d1695860217905581519283528201929092527ff264aae70bf6a9d90e68e0f9b393f4e7fbea67b063b0f336e0b36c1581703651910160405180910390a15050505050505050565b6060600061268383613199565b3373ffffffffffffffffffffffffffffffffffffffff821603612f02576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016104a3565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000818152600183016020526040812054612fbf575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556109cd565b5060006109cd565b600081815260018301602052604081205480156130b0576000612feb600183613fc2565b8554909150600090612fff90600190613fc2565b905081811461306457600086600001828154811061301f5761301f613a4a565b906000526020600020015490508087600001848154811061304257613042613a4a565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080613075576130756142b7565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506109cd565b60009150506109cd565b60008481526002602081905260409091200154610100900473ffffffffffffffffffffffffffffffffffffffff161561076d57600084815260026020819052604091829020015490517ffba64a7c00000000000000000000000000000000000000000000000000000000815261010090910473ffffffffffffffffffffffffffffffffffffffff169063fba64a7c9061315f908690869086908b908d906004016142e6565b600060405180830381600087803b15801561317957600080fd5b505af115801561318d573d6000803e3d6000fd5b50505050505050505050565b6060816000018054806020026020016040519081016040528092919081815260200182805480156131e957602002820191906000526020600020905b8154815260200190600101908083116131d5575b50505050509050919050565b50805461320190613c4d565b6000825580601f10613211575050565b601f0160209004906000526020600020908101906125e991905b8082111561323f576000815560010161322b565b5090565b60008083601f84011261325557600080fd5b50813567ffffffffffffffff81111561326d57600080fd5b6020830191508360208260051b850101111561328857600080fd5b9250929050565b600080600080604085870312156132a557600080fd5b843567ffffffffffffffff808211156132bd57600080fd5b6132c988838901613243565b909650945060208701359150808211156132e257600080fd5b506132ef87828801613243565b95989497509550505050565b60006080828403121561330d57600080fd5b50919050565b60006020828403121561332557600080fd5b5035919050565b6000815180845260005b8181101561335257602081850181015186830182015201613336565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081526000612683602083018461332c565b600080602083850312156133b657600080fd5b823567ffffffffffffffff8111156133cd57600080fd5b6133d985828601613243565b90969095509350505050565b600081518084526020808501945080840160005b83811015613415578151875295820195908201906001016133f9565b509495945050505050565b600063ffffffff8083511684526020818185015116818601526040915081840151151582860152606084015160a0606087015261346060a08701826133e5565b9050608085015186820360808801528181518084528484019150848160051b850101858401935060005b828110156134e7578582037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00184528451805183528701518783018990526134d48984018261332c565b958801959488019492505060010161348a565b509998505050505050505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015613568577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452613556858351613420565b9450928501929085019060010161351c565b5092979650505050505050565b803563ffffffff8116811461358957600080fd5b919050565b6000602082840312156135a057600080fd5b61268382613575565b6020815260006126836020830184613420565b600080604083850312156135cf57600080fd5b50508035926020909101359150565b63ffffffff81511682526020810151602083015260408101516040830152600060608201516080606085015261361760808501826133e5565b949350505050565b60408152600061363260408301856135de565b905063ffffffff831660208301529392505050565b73ffffffffffffffffffffffffffffffffffffffff81511682526000602082015160406020850152613617604085018261332c565b6020815260006126836020830184613647565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015613568577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526136f0858351613647565b945092850192908501906001016136b6565b80151581146125e957600080fd5b60008060008060006060868803121561372857600080fd5b853567ffffffffffffffff8082111561374057600080fd5b61374c89838a01613243565b9097509550602088013591508082111561376557600080fd5b5061377288828901613243565b909450925050604086013561378681613702565b809150509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b805182526020810151602083015260408101516002811061380d577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b604083015260609081015173ffffffffffffffffffffffffffffffffffffffff16910152565b608081016109cd82846137c3565b6000806040838503121561385457600080fd5b61385d83613575565b946020939093013593505050565b6000806000806000806080878903121561388457600080fd5b61388d87613575565b9550602087013567ffffffffffffffff808211156138aa57600080fd5b6138b68a838b01613243565b909750955060408901359150808211156138cf57600080fd5b506138dc89828a01613243565b90945092505060608701356138f081613702565b809150509295509295509295565b6020808252825182820181905260009190848201906040850190845b818110156139405761392d8385516137c3565b928401926080929092019160010161391a565b50909695505050505050565b6000604082016040835280855180835260608501915060608160051b8601019250602080880160005b838110156139c1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08887030185526139af8683516135de565b95509382019390820190600101613975565b50508584038187015286518085528782019482019350915060005b828110156139fe57845163ffffffff16845293810193928101926001016139dc565b5091979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff811681146125e957600080fd5b600060208284031215613a3f57600080fd5b813561268381613a0b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112613aad57600080fd5b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715613b0957613b09613ab7565b60405290565b6040516080810167ffffffffffffffff81118282101715613b0957613b09613ab7565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613b7957613b79613ab7565b604052919050565b600060408236031215613b9357600080fd5b613b9b613ae6565b8235613ba681613a0b565b815260208381013567ffffffffffffffff80821115613bc457600080fd5b9085019036601f830112613bd757600080fd5b813581811115613be957613be9613ab7565b613c19847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613b32565b91508082523684828501011115613c2f57600080fd5b80848401858401376000908201840152918301919091525092915050565b600181811c90821680613c6157607f821691505b60208210810361330d577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000602080835260008454613cae81613c4d565b80848701526040600180841660008114613ccf5760018114613d0757613d35565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008516838a01528284151560051b8a01019550613d35565b896000528660002060005b85811015613d2d5781548b8201860152908301908801613d12565b8a0184019650505b509398975050505050505050565b601f821115610a9e57600081815260208120601f850160051c81016020861015613d6a5750805b601f850160051c820191505b8181101561076d57828155600101613d76565b815167ffffffffffffffff811115613da357613da3613ab7565b613db781613db18454613c4d565b84613d43565b602080601f831160018114613e0a5760008415613dd45750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b17855561076d565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015613e5757888601518255948401946001909101908401613e38565b5085821015613e9357878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b828152604060208201526000613617604083018461332c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613f1c57613f1c613ebc565b5060010190565b600060208284031215613f3557600080fd5b815161268381613702565b813581556020820135600182015560028101604083013560028110613f6457600080fd5b81546060850135613f7481613a0b565b74ffffffffffffffffffffffffffffffffffffffff008160081b1660ff84167fffffffffffffffffffffff000000000000000000000000000000000000000000841617178455505050505050565b818103818111156109cd576109cd613ebc565b600063ffffffff808316818103613fee57613fee613ebc565b6001019392505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112613aad57600080fd5b60006080823603121561403e57600080fd5b614046613b0f565b61404f83613575565b81526020808401358183015260408401356040830152606084013567ffffffffffffffff8082111561408057600080fd5b9085019036601f83011261409357600080fd5b8135818111156140a5576140a5613ab7565b8060051b91506140b6848301613b32565b81815291830184019184810190368411156140d057600080fd5b938501935b838510156140ee578435825293850193908501906140d5565b606087015250939695505050505050565b6020808252825182820181905260009190848201906040850190845b818110156139405783518352928401929184019160010161411b565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261416c57600080fd5b83018035915067ffffffffffffffff82111561418757600080fd5b60200191503681900382131561328857600080fd5b67ffffffffffffffff8311156141b4576141b4613ab7565b6141c8836141c28354613c4d565b83613d43565b6000601f84116001811461421a57600085156141e45750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b1783556142b0565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156142695786850135825560209485019460019092019101614249565b50868210156142a4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b6080815284608082015260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86111561431f57600080fd5b8560051b808860a0850137820182810360a090810160208501526143459082018761332c565b91505063ffffffff8085166040840152808416606084015250969550505050505056fea164736f6c6343000813000a", } var CapabilityRegistryABI = CapabilityRegistryMetaData.ABI @@ -239,28 +248,25 @@ func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetCapability(hashed return _CapabilityRegistry.Contract.GetCapability(&_CapabilityRegistry.CallOpts, hashedId) } -func (_CapabilityRegistry *CapabilityRegistryCaller) GetDON(opts *bind.CallOpts, donId uint32) (uint32, bool, [][32]byte, []CapabilityRegistryCapabilityConfiguration, error) { +func (_CapabilityRegistry *CapabilityRegistryCaller) GetDON(opts *bind.CallOpts, donId uint32) (CapabilityRegistryDONInfo, error) { var out []interface{} err := _CapabilityRegistry.contract.Call(opts, &out, "getDON", donId) if err != nil { - return *new(uint32), *new(bool), *new([][32]byte), *new([]CapabilityRegistryCapabilityConfiguration), err + return *new(CapabilityRegistryDONInfo), err } - out0 := *abi.ConvertType(out[0], new(uint32)).(*uint32) - out1 := *abi.ConvertType(out[1], new(bool)).(*bool) - out2 := *abi.ConvertType(out[2], new([][32]byte)).(*[][32]byte) - out3 := *abi.ConvertType(out[3], new([]CapabilityRegistryCapabilityConfiguration)).(*[]CapabilityRegistryCapabilityConfiguration) + out0 := *abi.ConvertType(out[0], new(CapabilityRegistryDONInfo)).(*CapabilityRegistryDONInfo) - return out0, out1, out2, out3, err + return out0, err } -func (_CapabilityRegistry *CapabilityRegistrySession) GetDON(donId uint32) (uint32, bool, [][32]byte, []CapabilityRegistryCapabilityConfiguration, error) { +func (_CapabilityRegistry *CapabilityRegistrySession) GetDON(donId uint32) (CapabilityRegistryDONInfo, error) { return _CapabilityRegistry.Contract.GetDON(&_CapabilityRegistry.CallOpts, donId) } -func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetDON(donId uint32) (uint32, bool, [][32]byte, []CapabilityRegistryCapabilityConfiguration, error) { +func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetDON(donId uint32) (CapabilityRegistryDONInfo, error) { return _CapabilityRegistry.Contract.GetDON(&_CapabilityRegistry.CallOpts, donId) } @@ -286,6 +292,28 @@ func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetDONCapabilityConf return _CapabilityRegistry.Contract.GetDONCapabilityConfig(&_CapabilityRegistry.CallOpts, donId, capabilityId) } +func (_CapabilityRegistry *CapabilityRegistryCaller) GetDONs(opts *bind.CallOpts) ([]CapabilityRegistryDONInfo, error) { + var out []interface{} + err := _CapabilityRegistry.contract.Call(opts, &out, "getDONs") + + if err != nil { + return *new([]CapabilityRegistryDONInfo), err + } + + out0 := *abi.ConvertType(out[0], new([]CapabilityRegistryDONInfo)).(*[]CapabilityRegistryDONInfo) + + return out0, err + +} + +func (_CapabilityRegistry *CapabilityRegistrySession) GetDONs() ([]CapabilityRegistryDONInfo, error) { + return _CapabilityRegistry.Contract.GetDONs(&_CapabilityRegistry.CallOpts) +} + +func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetDONs() ([]CapabilityRegistryDONInfo, error) { + return _CapabilityRegistry.Contract.GetDONs(&_CapabilityRegistry.CallOpts) +} + func (_CapabilityRegistry *CapabilityRegistryCaller) GetHashedCapabilityId(opts *bind.CallOpts, labelledName [32]byte, version [32]byte) ([32]byte, error) { var out []interface{} err := _CapabilityRegistry.contract.Call(opts, &out, "getHashedCapabilityId", labelledName, version) @@ -308,26 +336,26 @@ func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetHashedCapabilityI return _CapabilityRegistry.Contract.GetHashedCapabilityId(&_CapabilityRegistry.CallOpts, labelledName, version) } -func (_CapabilityRegistry *CapabilityRegistryCaller) GetNode(opts *bind.CallOpts, p2pId [32]byte) (CapabilityRegistryNodeParams, uint32, error) { +func (_CapabilityRegistry *CapabilityRegistryCaller) GetNode(opts *bind.CallOpts, p2pId [32]byte) (CapabilityRegistryNodeInfo, uint32, error) { var out []interface{} err := _CapabilityRegistry.contract.Call(opts, &out, "getNode", p2pId) if err != nil { - return *new(CapabilityRegistryNodeParams), *new(uint32), err + return *new(CapabilityRegistryNodeInfo), *new(uint32), err } - out0 := *abi.ConvertType(out[0], new(CapabilityRegistryNodeParams)).(*CapabilityRegistryNodeParams) + out0 := *abi.ConvertType(out[0], new(CapabilityRegistryNodeInfo)).(*CapabilityRegistryNodeInfo) out1 := *abi.ConvertType(out[1], new(uint32)).(*uint32) return out0, out1, err } -func (_CapabilityRegistry *CapabilityRegistrySession) GetNode(p2pId [32]byte) (CapabilityRegistryNodeParams, uint32, error) { +func (_CapabilityRegistry *CapabilityRegistrySession) GetNode(p2pId [32]byte) (CapabilityRegistryNodeInfo, uint32, error) { return _CapabilityRegistry.Contract.GetNode(&_CapabilityRegistry.CallOpts, p2pId) } -func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetNode(p2pId [32]byte) (CapabilityRegistryNodeParams, uint32, error) { +func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetNode(p2pId [32]byte) (CapabilityRegistryNodeInfo, uint32, error) { return _CapabilityRegistry.Contract.GetNode(&_CapabilityRegistry.CallOpts, p2pId) } @@ -353,6 +381,51 @@ func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetNodeOperator(node return _CapabilityRegistry.Contract.GetNodeOperator(&_CapabilityRegistry.CallOpts, nodeOperatorId) } +func (_CapabilityRegistry *CapabilityRegistryCaller) GetNodeOperators(opts *bind.CallOpts) ([]CapabilityRegistryNodeOperator, error) { + var out []interface{} + err := _CapabilityRegistry.contract.Call(opts, &out, "getNodeOperators") + + if err != nil { + return *new([]CapabilityRegistryNodeOperator), err + } + + out0 := *abi.ConvertType(out[0], new([]CapabilityRegistryNodeOperator)).(*[]CapabilityRegistryNodeOperator) + + return out0, err + +} + +func (_CapabilityRegistry *CapabilityRegistrySession) GetNodeOperators() ([]CapabilityRegistryNodeOperator, error) { + return _CapabilityRegistry.Contract.GetNodeOperators(&_CapabilityRegistry.CallOpts) +} + +func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetNodeOperators() ([]CapabilityRegistryNodeOperator, error) { + return _CapabilityRegistry.Contract.GetNodeOperators(&_CapabilityRegistry.CallOpts) +} + +func (_CapabilityRegistry *CapabilityRegistryCaller) GetNodes(opts *bind.CallOpts) ([]CapabilityRegistryNodeInfo, []uint32, error) { + var out []interface{} + err := _CapabilityRegistry.contract.Call(opts, &out, "getNodes") + + if err != nil { + return *new([]CapabilityRegistryNodeInfo), *new([]uint32), err + } + + out0 := *abi.ConvertType(out[0], new([]CapabilityRegistryNodeInfo)).(*[]CapabilityRegistryNodeInfo) + out1 := *abi.ConvertType(out[1], new([]uint32)).(*[]uint32) + + return out0, out1, err + +} + +func (_CapabilityRegistry *CapabilityRegistrySession) GetNodes() ([]CapabilityRegistryNodeInfo, []uint32, error) { + return _CapabilityRegistry.Contract.GetNodes(&_CapabilityRegistry.CallOpts) +} + +func (_CapabilityRegistry *CapabilityRegistryCallerSession) GetNodes() ([]CapabilityRegistryNodeInfo, []uint32, error) { + return _CapabilityRegistry.Contract.GetNodes(&_CapabilityRegistry.CallOpts) +} + func (_CapabilityRegistry *CapabilityRegistryCaller) IsCapabilityDeprecated(opts *bind.CallOpts, hashedCapabilityId [32]byte) (bool, error) { var out []interface{} err := _CapabilityRegistry.contract.Call(opts, &out, "isCapabilityDeprecated", hashedCapabilityId) @@ -467,15 +540,15 @@ func (_CapabilityRegistry *CapabilityRegistryTransactorSession) AddNodeOperators return _CapabilityRegistry.Contract.AddNodeOperators(&_CapabilityRegistry.TransactOpts, nodeOperators) } -func (_CapabilityRegistry *CapabilityRegistryTransactor) AddNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistryTransactor) AddNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { return _CapabilityRegistry.contract.Transact(opts, "addNodes", nodes) } -func (_CapabilityRegistry *CapabilityRegistrySession) AddNodes(nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistrySession) AddNodes(nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { return _CapabilityRegistry.Contract.AddNodes(&_CapabilityRegistry.TransactOpts, nodes) } -func (_CapabilityRegistry *CapabilityRegistryTransactorSession) AddNodes(nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistryTransactorSession) AddNodes(nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { return _CapabilityRegistry.Contract.AddNodes(&_CapabilityRegistry.TransactOpts, nodes) } @@ -491,6 +564,18 @@ func (_CapabilityRegistry *CapabilityRegistryTransactorSession) DeprecateCapabil return _CapabilityRegistry.Contract.DeprecateCapability(&_CapabilityRegistry.TransactOpts, hashedCapabilityId) } +func (_CapabilityRegistry *CapabilityRegistryTransactor) RemoveDONs(opts *bind.TransactOpts, donIds []uint32) (*types.Transaction, error) { + return _CapabilityRegistry.contract.Transact(opts, "removeDONs", donIds) +} + +func (_CapabilityRegistry *CapabilityRegistrySession) RemoveDONs(donIds []uint32) (*types.Transaction, error) { + return _CapabilityRegistry.Contract.RemoveDONs(&_CapabilityRegistry.TransactOpts, donIds) +} + +func (_CapabilityRegistry *CapabilityRegistryTransactorSession) RemoveDONs(donIds []uint32) (*types.Transaction, error) { + return _CapabilityRegistry.Contract.RemoveDONs(&_CapabilityRegistry.TransactOpts, donIds) +} + func (_CapabilityRegistry *CapabilityRegistryTransactor) RemoveNodeOperators(opts *bind.TransactOpts, nodeOperatorIds []*big.Int) (*types.Transaction, error) { return _CapabilityRegistry.contract.Transact(opts, "removeNodeOperators", nodeOperatorIds) } @@ -527,6 +612,18 @@ func (_CapabilityRegistry *CapabilityRegistryTransactorSession) TransferOwnershi return _CapabilityRegistry.Contract.TransferOwnership(&_CapabilityRegistry.TransactOpts, to) } +func (_CapabilityRegistry *CapabilityRegistryTransactor) UpdateDON(opts *bind.TransactOpts, donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilityRegistryCapabilityConfiguration, isPublic bool) (*types.Transaction, error) { + return _CapabilityRegistry.contract.Transact(opts, "updateDON", donId, nodes, capabilityConfigurations, isPublic) +} + +func (_CapabilityRegistry *CapabilityRegistrySession) UpdateDON(donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilityRegistryCapabilityConfiguration, isPublic bool) (*types.Transaction, error) { + return _CapabilityRegistry.Contract.UpdateDON(&_CapabilityRegistry.TransactOpts, donId, nodes, capabilityConfigurations, isPublic) +} + +func (_CapabilityRegistry *CapabilityRegistryTransactorSession) UpdateDON(donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilityRegistryCapabilityConfiguration, isPublic bool) (*types.Transaction, error) { + return _CapabilityRegistry.Contract.UpdateDON(&_CapabilityRegistry.TransactOpts, donId, nodes, capabilityConfigurations, isPublic) +} + func (_CapabilityRegistry *CapabilityRegistryTransactor) UpdateNodeOperators(opts *bind.TransactOpts, nodeOperatorIds []*big.Int, nodeOperators []CapabilityRegistryNodeOperator) (*types.Transaction, error) { return _CapabilityRegistry.contract.Transact(opts, "updateNodeOperators", nodeOperatorIds, nodeOperators) } @@ -539,15 +636,15 @@ func (_CapabilityRegistry *CapabilityRegistryTransactorSession) UpdateNodeOperat return _CapabilityRegistry.Contract.UpdateNodeOperators(&_CapabilityRegistry.TransactOpts, nodeOperatorIds, nodeOperators) } -func (_CapabilityRegistry *CapabilityRegistryTransactor) UpdateNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistryTransactor) UpdateNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { return _CapabilityRegistry.contract.Transact(opts, "updateNodes", nodes) } -func (_CapabilityRegistry *CapabilityRegistrySession) UpdateNodes(nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistrySession) UpdateNodes(nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { return _CapabilityRegistry.Contract.UpdateNodes(&_CapabilityRegistry.TransactOpts, nodes) } -func (_CapabilityRegistry *CapabilityRegistryTransactorSession) UpdateNodes(nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) { +func (_CapabilityRegistry *CapabilityRegistryTransactorSession) UpdateNodes(nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) { return _CapabilityRegistry.Contract.UpdateNodes(&_CapabilityRegistry.TransactOpts, nodes) } @@ -805,8 +902,8 @@ func (_CapabilityRegistry *CapabilityRegistryFilterer) ParseCapabilityDeprecated return event, nil } -type CapabilityRegistryDONAddedIterator struct { - Event *CapabilityRegistryDONAdded +type CapabilityRegistryConfigSetIterator struct { + Event *CapabilityRegistryConfigSet contract *bind.BoundContract event string @@ -817,7 +914,7 @@ type CapabilityRegistryDONAddedIterator struct { fail error } -func (it *CapabilityRegistryDONAddedIterator) Next() bool { +func (it *CapabilityRegistryConfigSetIterator) Next() bool { if it.fail != nil { return false @@ -826,7 +923,7 @@ func (it *CapabilityRegistryDONAddedIterator) Next() bool { if it.done { select { case log := <-it.logs: - it.Event = new(CapabilityRegistryDONAdded) + it.Event = new(CapabilityRegistryConfigSet) if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { it.fail = err return false @@ -841,7 +938,7 @@ func (it *CapabilityRegistryDONAddedIterator) Next() bool { select { case log := <-it.logs: - it.Event = new(CapabilityRegistryDONAdded) + it.Event = new(CapabilityRegistryConfigSet) if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { it.fail = err return false @@ -856,33 +953,33 @@ func (it *CapabilityRegistryDONAddedIterator) Next() bool { } } -func (it *CapabilityRegistryDONAddedIterator) Error() error { +func (it *CapabilityRegistryConfigSetIterator) Error() error { return it.fail } -func (it *CapabilityRegistryDONAddedIterator) Close() error { +func (it *CapabilityRegistryConfigSetIterator) Close() error { it.sub.Unsubscribe() return nil } -type CapabilityRegistryDONAdded struct { - DonId *big.Int - IsPublic bool - Raw types.Log +type CapabilityRegistryConfigSet struct { + DonId uint32 + ConfigCount uint32 + Raw types.Log } -func (_CapabilityRegistry *CapabilityRegistryFilterer) FilterDONAdded(opts *bind.FilterOpts) (*CapabilityRegistryDONAddedIterator, error) { +func (_CapabilityRegistry *CapabilityRegistryFilterer) FilterConfigSet(opts *bind.FilterOpts) (*CapabilityRegistryConfigSetIterator, error) { - logs, sub, err := _CapabilityRegistry.contract.FilterLogs(opts, "DONAdded") + logs, sub, err := _CapabilityRegistry.contract.FilterLogs(opts, "ConfigSet") if err != nil { return nil, err } - return &CapabilityRegistryDONAddedIterator{contract: _CapabilityRegistry.contract, event: "DONAdded", logs: logs, sub: sub}, nil + return &CapabilityRegistryConfigSetIterator{contract: _CapabilityRegistry.contract, event: "ConfigSet", logs: logs, sub: sub}, nil } -func (_CapabilityRegistry *CapabilityRegistryFilterer) WatchDONAdded(opts *bind.WatchOpts, sink chan<- *CapabilityRegistryDONAdded) (event.Subscription, error) { +func (_CapabilityRegistry *CapabilityRegistryFilterer) WatchConfigSet(opts *bind.WatchOpts, sink chan<- *CapabilityRegistryConfigSet) (event.Subscription, error) { - logs, sub, err := _CapabilityRegistry.contract.WatchLogs(opts, "DONAdded") + logs, sub, err := _CapabilityRegistry.contract.WatchLogs(opts, "ConfigSet") if err != nil { return nil, err } @@ -892,8 +989,8 @@ func (_CapabilityRegistry *CapabilityRegistryFilterer) WatchDONAdded(opts *bind. select { case log := <-logs: - event := new(CapabilityRegistryDONAdded) - if err := _CapabilityRegistry.contract.UnpackLog(event, "DONAdded", log); err != nil { + event := new(CapabilityRegistryConfigSet) + if err := _CapabilityRegistry.contract.UnpackLog(event, "ConfigSet", log); err != nil { return err } event.Raw = log @@ -914,9 +1011,9 @@ func (_CapabilityRegistry *CapabilityRegistryFilterer) WatchDONAdded(opts *bind. }), nil } -func (_CapabilityRegistry *CapabilityRegistryFilterer) ParseDONAdded(log types.Log) (*CapabilityRegistryDONAdded, error) { - event := new(CapabilityRegistryDONAdded) - if err := _CapabilityRegistry.contract.UnpackLog(event, "DONAdded", log); err != nil { +func (_CapabilityRegistry *CapabilityRegistryFilterer) ParseConfigSet(log types.Log) (*CapabilityRegistryConfigSet, error) { + event := new(CapabilityRegistryConfigSet) + if err := _CapabilityRegistry.contract.UnpackLog(event, "ConfigSet", log); err != nil { return nil, err } event.Raw = log @@ -986,6 +1083,7 @@ func (it *CapabilityRegistryNodeAddedIterator) Close() error { type CapabilityRegistryNodeAdded struct { P2pId [32]byte NodeOperatorId *big.Int + Signer [32]byte Raw types.Log } @@ -1596,7 +1694,7 @@ func (it *CapabilityRegistryNodeUpdatedIterator) Close() error { type CapabilityRegistryNodeUpdated struct { P2pId [32]byte NodeOperatorId *big.Int - Signer common.Address + Signer [32]byte Raw types.Log } @@ -1930,8 +2028,8 @@ func (_CapabilityRegistry *CapabilityRegistry) ParseLog(log types.Log) (generate return _CapabilityRegistry.ParseCapabilityAdded(log) case _CapabilityRegistry.abi.Events["CapabilityDeprecated"].ID: return _CapabilityRegistry.ParseCapabilityDeprecated(log) - case _CapabilityRegistry.abi.Events["DONAdded"].ID: - return _CapabilityRegistry.ParseDONAdded(log) + case _CapabilityRegistry.abi.Events["ConfigSet"].ID: + return _CapabilityRegistry.ParseConfigSet(log) case _CapabilityRegistry.abi.Events["NodeAdded"].ID: return _CapabilityRegistry.ParseNodeAdded(log) case _CapabilityRegistry.abi.Events["NodeOperatorAdded"].ID: @@ -1962,12 +2060,12 @@ func (CapabilityRegistryCapabilityDeprecated) Topic() common.Hash { return common.HexToHash("0xdcea1b78b6ddc31592a94607d537543fcaafda6cc52d6d5cc7bbfca1422baf21") } -func (CapabilityRegistryDONAdded) Topic() common.Hash { - return common.HexToHash("0xab55f4c8fb4335a586285ae209d1f1e17a7ccb22e1131963624434d98c8546a5") +func (CapabilityRegistryConfigSet) Topic() common.Hash { + return common.HexToHash("0xf264aae70bf6a9d90e68e0f9b393f4e7fbea67b063b0f336e0b36c1581703651") } func (CapabilityRegistryNodeAdded) Topic() common.Hash { - return common.HexToHash("0x5bfe8a52ad26ac6ee7b0cd46d2fd92be04735a31c45ef8aa3d4b7ea1b61bbc1f") + return common.HexToHash("0xc9296aa9b0951d8000e8ed7f2b5be30c5106de8df3dbedf9a57c93f5f9e4d7da") } func (CapabilityRegistryNodeOperatorAdded) Topic() common.Hash { @@ -1987,7 +2085,7 @@ func (CapabilityRegistryNodeRemoved) Topic() common.Hash { } func (CapabilityRegistryNodeUpdated) Topic() common.Hash { - return common.HexToHash("0x6bbba867c646be512c2f3241e65fdffdefd5528d7e7939649e06e10ee5addc3e") + return common.HexToHash("0xf101cfc54994c31624d25789378d71ec4dbdc533e26a4ecc6b7648f4798d0916") } func (CapabilityRegistryOwnershipTransferRequested) Topic() common.Hash { @@ -2007,16 +2105,22 @@ type CapabilityRegistryInterface interface { GetCapability(opts *bind.CallOpts, hashedId [32]byte) (CapabilityRegistryCapability, error) - GetDON(opts *bind.CallOpts, donId uint32) (uint32, bool, [][32]byte, []CapabilityRegistryCapabilityConfiguration, error) + GetDON(opts *bind.CallOpts, donId uint32) (CapabilityRegistryDONInfo, error) GetDONCapabilityConfig(opts *bind.CallOpts, donId uint32, capabilityId [32]byte) ([]byte, error) + GetDONs(opts *bind.CallOpts) ([]CapabilityRegistryDONInfo, error) + GetHashedCapabilityId(opts *bind.CallOpts, labelledName [32]byte, version [32]byte) ([32]byte, error) - GetNode(opts *bind.CallOpts, p2pId [32]byte) (CapabilityRegistryNodeParams, uint32, error) + GetNode(opts *bind.CallOpts, p2pId [32]byte) (CapabilityRegistryNodeInfo, uint32, error) GetNodeOperator(opts *bind.CallOpts, nodeOperatorId *big.Int) (CapabilityRegistryNodeOperator, error) + GetNodeOperators(opts *bind.CallOpts) ([]CapabilityRegistryNodeOperator, error) + + GetNodes(opts *bind.CallOpts) ([]CapabilityRegistryNodeInfo, []uint32, error) + IsCapabilityDeprecated(opts *bind.CallOpts, hashedCapabilityId [32]byte) (bool, error) Owner(opts *bind.CallOpts) (common.Address, error) @@ -2031,19 +2135,23 @@ type CapabilityRegistryInterface interface { AddNodeOperators(opts *bind.TransactOpts, nodeOperators []CapabilityRegistryNodeOperator) (*types.Transaction, error) - AddNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) + AddNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) DeprecateCapability(opts *bind.TransactOpts, hashedCapabilityId [32]byte) (*types.Transaction, error) + RemoveDONs(opts *bind.TransactOpts, donIds []uint32) (*types.Transaction, error) + RemoveNodeOperators(opts *bind.TransactOpts, nodeOperatorIds []*big.Int) (*types.Transaction, error) RemoveNodes(opts *bind.TransactOpts, removedNodeP2PIds [][32]byte) (*types.Transaction, error) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + UpdateDON(opts *bind.TransactOpts, donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilityRegistryCapabilityConfiguration, isPublic bool) (*types.Transaction, error) + UpdateNodeOperators(opts *bind.TransactOpts, nodeOperatorIds []*big.Int, nodeOperators []CapabilityRegistryNodeOperator) (*types.Transaction, error) - UpdateNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeParams) (*types.Transaction, error) + UpdateNodes(opts *bind.TransactOpts, nodes []CapabilityRegistryNodeInfo) (*types.Transaction, error) FilterCapabilityAdded(opts *bind.FilterOpts, hashedCapabilityId [][32]byte) (*CapabilityRegistryCapabilityAddedIterator, error) @@ -2057,11 +2165,11 @@ type CapabilityRegistryInterface interface { ParseCapabilityDeprecated(log types.Log) (*CapabilityRegistryCapabilityDeprecated, error) - FilterDONAdded(opts *bind.FilterOpts) (*CapabilityRegistryDONAddedIterator, error) + FilterConfigSet(opts *bind.FilterOpts) (*CapabilityRegistryConfigSetIterator, error) - WatchDONAdded(opts *bind.WatchOpts, sink chan<- *CapabilityRegistryDONAdded) (event.Subscription, error) + WatchConfigSet(opts *bind.WatchOpts, sink chan<- *CapabilityRegistryConfigSet) (event.Subscription, error) - ParseDONAdded(log types.Log) (*CapabilityRegistryDONAdded, error) + ParseConfigSet(log types.Log) (*CapabilityRegistryConfigSet, error) FilterNodeAdded(opts *bind.FilterOpts) (*CapabilityRegistryNodeAddedIterator, error) diff --git a/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 54d1355c62f..9167c638fbb 100644 --- a/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,4 +1,4 @@ GETH_VERSION: 1.13.8 -forwarder: ../../../contracts/solc/v0.8.19/KeystoneForwarder/KeystoneForwarder.abi ../../../contracts/solc/v0.8.19/KeystoneForwarder/KeystoneForwarder.bin ed9164cfe4619dff824b11df46b66f4c6834b2ca072923f10d9ebc57ce508ed8 -keystone_capability_registry: ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.abi ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.bin d4e0661491c2adc7f0d7553287c938fb32664b9f07770f6d57ae6511ce9884cd +forwarder: ../../../contracts/solc/v0.8.19/KeystoneForwarder/KeystoneForwarder.abi ../../../contracts/solc/v0.8.19/KeystoneForwarder/KeystoneForwarder.bin 892c6ced16576bebd887eb581147c02139853d5143a0c9b77704efefd4ab7ec7 +keystone_capability_registry: ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.abi ../../../contracts/solc/v0.8.19/CapabilityRegistry/CapabilityRegistry.bin 0a79d0eba13fd4a4b83d7618bb181c21c42222f3cc6c5a90a09302f685555033 ocr3_capability: ../../../contracts/solc/v0.8.19/OCR3Capability/OCR3Capability.abi ../../../contracts/solc/v0.8.19/OCR3Capability/OCR3Capability.bin 9dcbdf55bd5729ba266148da3f17733eb592c871c2108ccca546618628fd9ad2 From 9a990ddb624614cd24e4ffc9f5045ee01aa10f9b Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Wed, 5 Jun 2024 21:35:06 +0200 Subject: [PATCH 41/53] ungoimport --- core/gethwrappers/keystone/generated/forwarder/forwarder.go | 1 - .../keystone_capability_registry/keystone_capability_registry.go | 1 - 2 files changed, 2 deletions(-) diff --git a/core/gethwrappers/keystone/generated/forwarder/forwarder.go b/core/gethwrappers/keystone/generated/forwarder/forwarder.go index 05ee44b415b..6fd963d7886 100644 --- a/core/gethwrappers/keystone/generated/forwarder/forwarder.go +++ b/core/gethwrappers/keystone/generated/forwarder/forwarder.go @@ -15,7 +15,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/event" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" ) diff --git a/core/gethwrappers/keystone/generated/keystone_capability_registry/keystone_capability_registry.go b/core/gethwrappers/keystone/generated/keystone_capability_registry/keystone_capability_registry.go index ab4b89d18e1..1725189ced6 100644 --- a/core/gethwrappers/keystone/generated/keystone_capability_registry/keystone_capability_registry.go +++ b/core/gethwrappers/keystone/generated/keystone_capability_registry/keystone_capability_registry.go @@ -15,7 +15,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/event" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" ) From 41f1d4b86d4f6747cba127adca614f49eb8b4872 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Thu, 6 Jun 2024 19:37:12 +0200 Subject: [PATCH 42/53] move newChainIDSubForwarder into the test --- core/chains/evm/client/sub_forwarder.go | 12 ------------ core/chains/evm/client/sub_forwarder_test.go | 7 +++++++ 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/core/chains/evm/client/sub_forwarder.go b/core/chains/evm/client/sub_forwarder.go index c33eaf15039..93e9b106b4a 100644 --- a/core/chains/evm/client/sub_forwarder.go +++ b/core/chains/evm/client/sub_forwarder.go @@ -1,23 +1,11 @@ package client import ( - "math/big" - "github.com/ethereum/go-ethereum" - - evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) var _ ethereum.Subscription = &subForwarder[any]{} -func newChainIDSubForwarder(chainID *big.Int, ch chan<- *evmtypes.Head) *subForwarder[*evmtypes.Head] { - return newSubForwarder(ch, func(head *evmtypes.Head) *evmtypes.Head { - head.EVMChainID = ubig.New(chainID) - return head - }, nil) -} - // subForwarder wraps a subscription in order to intercept and augment each result before forwarding. type subForwarder[T any] struct { destCh chan<- T diff --git a/core/chains/evm/client/sub_forwarder_test.go b/core/chains/evm/client/sub_forwarder_test.go index d249793a567..1bc0122603b 100644 --- a/core/chains/evm/client/sub_forwarder_test.go +++ b/core/chains/evm/client/sub_forwarder_test.go @@ -20,6 +20,13 @@ import ( func TestChainIDSubForwarder(t *testing.T) { t.Parallel() + newChainIDSubForwarder := func(chainID *big.Int, ch chan<- *evmtypes.Head) *subForwarder[*evmtypes.Head] { + return newSubForwarder(ch, func(head *evmtypes.Head) *evmtypes.Head { + head.EVMChainID = ubig.New(chainID) + return head + }, nil) + } + chainID := big.NewInt(123) t.Run("unsubscribe forwarder", func(t *testing.T) { From 724b472fc783457c4c26d32e5ac09ec64dbf568f Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Fri, 7 Jun 2024 13:08:24 +0200 Subject: [PATCH 43/53] fix typo --- core/chains/evm/client/rpc_client.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index 4848cfdfe10..39bc41fc84a 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -420,7 +420,7 @@ func (r *rpcClient) SubscribeNewHead(ctx context.Context, channel chan<- *evmtyp }() subForwarder := newSubForwarder(channel, func(head *evmtypes.Head) *evmtypes.Head { head.EVMChainID = ubig.New(r.chainID) - r.oneNewHead(ctx, chStopInFlight, head) + r.onNewHead(ctx, chStopInFlight, head) return head }, r.wrapRPCClientError) err = subForwarder.start(ws.rpc.EthSubscribe(ctx, subForwarder.srcCh, args...)) @@ -581,9 +581,9 @@ func (r *rpcClient) blockByNumber(ctx context.Context, number string) (head *evm switch number { case rpc.FinalizedBlockNumber.String(): - r.oneNewFinalizedHead(ctx, chStopInFlight, head) + r.onNewFinalizedHead(ctx, chStopInFlight, head) case rpc.LatestBlockNumber.String(): - r.oneNewHead(ctx, chStopInFlight, head) + r.onNewHead(ctx, chStopInFlight, head) } return @@ -1200,7 +1200,7 @@ func Name(r *rpcClient) string { return r.name } -func (r *rpcClient) oneNewHead(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 } @@ -1220,7 +1220,7 @@ func (r *rpcClient) oneNewHead(ctx context.Context, requestCh <-chan struct{}, h } } -func (r *rpcClient) oneNewFinalizedHead(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 } From 399af4109b13bef778a4271ee485d8ad4e70c990 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Mon, 10 Jun 2024 17:37:54 +0200 Subject: [PATCH 44/53] Move deathDeclarationDelay to config --- common/client/multi_node.go | 3 +- common/client/multi_node_test.go | 23 +++--- common/client/node.go | 1 + common/client/node_test.go | 5 ++ core/chains/evm/client/chain_client.go | 2 + core/chains/evm/client/config_builder.go | 2 + core/chains/evm/client/config_builder_test.go | 4 +- core/chains/evm/client/evm_client.go | 2 +- core/chains/evm/client/evm_client_test.go | 3 +- core/chains/evm/client/helpers_test.go | 11 ++- .../evm/config/chain_scoped_node_pool.go | 4 ++ core/chains/evm/config/config.go | 1 + core/chains/evm/config/config_test.go | 1 + core/chains/evm/config/toml/config.go | 5 ++ .../evm/config/toml/defaults/fallback.toml | 1 + core/config/docs/chains-evm.toml | 8 +++ core/services/chainlink/config_test.go | 2 + .../chainlink/testdata/config-full.toml | 1 + .../config-multi-chain-effective.toml | 3 + core/web/resolver/testdata/config-full.toml | 1 + .../config-multi-chain-effective.toml | 3 + docs/CONFIG.md | 72 +++++++++++++++++++ .../disk-based-logging-disabled.txtar | 1 + .../validate/disk-based-logging-no-dir.txtar | 1 + .../node/validate/disk-based-logging.txtar | 1 + testdata/scripts/node/validate/invalid.txtar | 1 + testdata/scripts/node/validate/valid.txtar | 1 + 27 files changed, 145 insertions(+), 18 deletions(-) diff --git a/common/client/multi_node.go b/common/client/multi_node.go index 4502bb40edd..82837f983bf 100644 --- a/common/client/multi_node.go +++ b/common/client/multi_node.go @@ -138,6 +138,7 @@ func NewMultiNode[ chainFamily string, classifySendTxError func(tx TX, err error) SendTxReturnCode, sendTxSoftTimeout time.Duration, + 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) // Prometheus' default interval is 15s, set this to under 7.5s to avoid @@ -159,7 +160,7 @@ func NewMultiNode[ chainFamily: chainFamily, classifySendTxError: classifySendTxError, reportInterval: reportInterval, - deathDeclarationDelay: reportInterval, + deathDeclarationDelay: deathDeclarationDelay, sendTxSoftTimeout: sendTxSoftTimeout, } diff --git a/common/client/multi_node_test.go b/common/client/multi_node_test.go index 6d41325425a..dc280e0e178 100644 --- a/common/client/multi_node_test.go +++ b/common/client/multi_node_test.go @@ -29,16 +29,17 @@ type testMultiNode struct { } type multiNodeOpts struct { - logger logger.Logger - selectionMode string - leaseDuration time.Duration - noNewHeadsThreshold time.Duration - nodes []Node[types.ID, types.Head[Hashable], multiNodeRPCClient] - sendonlys []SendOnlyNode[types.ID, multiNodeRPCClient] - chainID types.ID - chainFamily string - classifySendTxError func(tx any, err error) SendTxReturnCode - sendTxSoftTimeout time.Duration + logger logger.Logger + selectionMode string + leaseDuration time.Duration + noNewHeadsThreshold time.Duration + nodes []Node[types.ID, types.Head[Hashable], multiNodeRPCClient] + sendonlys []SendOnlyNode[types.ID, multiNodeRPCClient] + chainID types.ID + chainFamily string + classifySendTxError func(tx any, err error) SendTxReturnCode + sendTxSoftTimeout time.Duration + deathDeclarationDelay time.Duration } func newTestMultiNode(t *testing.T, opts multiNodeOpts) testMultiNode { @@ -49,7 +50,7 @@ func newTestMultiNode(t *testing.T, opts multiNodeOpts) testMultiNode { 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.chainID, opts.chainFamily, opts.classifySendTxError, opts.sendTxSoftTimeout, 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]), diff --git a/common/client/node.go b/common/client/node.go index e8eaa94c5cb..cc97d7980f7 100644 --- a/common/client/node.go +++ b/common/client/node.go @@ -44,6 +44,7 @@ type NodeConfig interface { NodeIsSyncingEnabled() bool FinalizedBlockPollInterval() time.Duration EnforceRepeatableRead() bool + DeathDeclarationDelay() time.Duration } type ChainConfig interface { diff --git a/common/client/node_test.go b/common/client/node_test.go index 4a8531197ec..3b971e84902 100644 --- a/common/client/node_test.go +++ b/common/client/node_test.go @@ -19,6 +19,7 @@ type testNodeConfig struct { nodeIsSyncingEnabled bool enforceRepeatableRead bool finalizedBlockPollInterval time.Duration + deathDeclarationDelay time.Duration } func (n testNodeConfig) PollFailureThreshold() uint32 { @@ -49,6 +50,10 @@ func (n testNodeConfig) EnforceRepeatableRead() bool { return n.enforceRepeatableRead } +func (n testNodeConfig) DeathDeclarationDelay() time.Duration { + return n.deathDeclarationDelay +} + type testNode struct { *node[types.ID, Head, NodeClient[types.ID, Head]] } diff --git a/core/chains/evm/client/chain_client.go b/core/chains/evm/client/chain_client.go index b16b7df1002..0fb465f816a 100644 --- a/core/chains/evm/client/chain_client.go +++ b/core/chains/evm/client/chain_client.go @@ -132,6 +132,7 @@ func NewChainClient( chainID *big.Int, chainType chaintype.ChainType, clientErrors evmconfig.ClientErrors, + deathDeclarationDelay time.Duration, ) Client { multiNode := commonclient.NewMultiNode( lggr, @@ -146,6 +147,7 @@ func NewChainClient( return ClassifySendError(err, clientErrors, logger.Sugared(logger.Nop()), tx, common.Address{}, chainType.IsL2()) }, 0, // use the default value provided by the implementation + deathDeclarationDelay, ) return &chainClient{ multiNode: multiNode, diff --git a/core/chains/evm/client/config_builder.go b/core/chains/evm/client/config_builder.go index a88dadde090..115d660a90f 100644 --- a/core/chains/evm/client/config_builder.go +++ b/core/chains/evm/client/config_builder.go @@ -40,6 +40,7 @@ func NewClientConfigs( finalityTagEnabled *bool, finalityBlockOffset *uint32, enforceRepeatableRead *bool, + deathDeclarationDelay time.Duration, ) (commonclient.ChainConfig, evmconfig.NodePool, []*toml.Node, error) { nodes, err := parseNodeConfigs(nodeCfgs) @@ -54,6 +55,7 @@ func NewClientConfigs( SyncThreshold: syncThreshold, NodeIsSyncingEnabled: nodeIsSyncingEnabled, EnforceRepeatableRead: enforceRepeatableRead, + DeathDeclarationDelay: commonconfig.MustNewDuration(deathDeclarationDelay), } nodePoolCfg := &evmconfig.NodePoolConfig{C: nodePool} chainConfig := &evmconfig.EVMConfig{ diff --git a/core/chains/evm/client/config_builder_test.go b/core/chains/evm/client/config_builder_test.go index c488511c839..7c08bf18c1d 100644 --- a/core/chains/evm/client/config_builder_test.go +++ b/core/chains/evm/client/config_builder_test.go @@ -25,6 +25,7 @@ func TestClientConfigBuilder(t *testing.T) { chainTypeStr := "" finalizedBlockOffset := ptr[uint32](16) enforceRepeatableRead := ptr(true) + deathDeclarationDelay := time.Second * 3 nodeConfigs := []client.NodeConfig{ { Name: ptr("foo"), @@ -37,7 +38,7 @@ func TestClientConfigBuilder(t *testing.T) { noNewHeadsThreshold := time.Second chainCfg, nodePool, nodes, err := client.NewClientConfigs(selectionMode, leaseDuration, chainTypeStr, nodeConfigs, pollFailureThreshold, pollInterval, syncThreshold, nodeIsSyncingEnabled, noNewHeadsThreshold, finalityDepth, - finalityTagEnabled, finalizedBlockOffset, enforceRepeatableRead) + finalityTagEnabled, finalizedBlockOffset, enforceRepeatableRead, deathDeclarationDelay) require.NoError(t, err) // Validate node pool configs @@ -48,6 +49,7 @@ func TestClientConfigBuilder(t *testing.T) { require.Equal(t, *syncThreshold, nodePool.SyncThreshold()) require.Equal(t, *nodeIsSyncingEnabled, nodePool.NodeIsSyncingEnabled()) require.Equal(t, *enforceRepeatableRead, nodePool.EnforceRepeatableRead()) + require.Equal(t, deathDeclarationDelay, nodePool.DeathDeclarationDelay()) // Validate node configs require.Equal(t, *nodeConfigs[0].Name, *nodes[0].Name) diff --git a/core/chains/evm/client/evm_client.go b/core/chains/evm/client/evm_client.go index 4d309440590..fd7fa5868a4 100644 --- a/core/chains/evm/client/evm_client.go +++ b/core/chains/evm/client/evm_client.go @@ -35,5 +35,5 @@ func NewEvmClient(cfg evmconfig.NodePool, chainCfg commonclient.ChainConfig, cli } return NewChainClient(lggr, cfg.SelectionMode(), cfg.LeaseDuration(), chainCfg.NodeNoNewHeadsThreshold(), - primaries, sendonlys, chainID, chainType, clientErrors) + primaries, sendonlys, chainID, chainType, clientErrors, cfg.DeathDeclarationDelay()) } diff --git a/core/chains/evm/client/evm_client_test.go b/core/chains/evm/client/evm_client_test.go index 9c8f9d66619..9ad25f96025 100644 --- a/core/chains/evm/client/evm_client_test.go +++ b/core/chains/evm/client/evm_client_test.go @@ -26,6 +26,7 @@ func TestNewEvmClient(t *testing.T) { chainTypeStr := "" finalizedBlockOffset := ptr[uint32](16) enforceRepeatableRead := ptr(true) + deathDeclarationDelay := time.Second * 3 nodeConfigs := []client.NodeConfig{ { Name: ptr("foo"), @@ -37,7 +38,7 @@ func TestNewEvmClient(t *testing.T) { finalityTagEnabled := ptr(true) chainCfg, nodePool, nodes, err := client.NewClientConfigs(selectionMode, leaseDuration, chainTypeStr, nodeConfigs, pollFailureThreshold, pollInterval, syncThreshold, nodeIsSyncingEnabled, noNewHeadsThreshold, finalityDepth, - finalityTagEnabled, finalizedBlockOffset, enforceRepeatableRead) + finalityTagEnabled, finalizedBlockOffset, enforceRepeatableRead, deathDeclarationDelay) require.NoError(t, err) client := client.NewEvmClient(nodePool, chainCfg, nil, logger.Test(t), testutils.FixtureChainID, nodes, chaintype.ChainType(chainTypeStr)) diff --git a/core/chains/evm/client/helpers_test.go b/core/chains/evm/client/helpers_test.go index 39c66271a31..e1017a5564f 100644 --- a/core/chains/evm/client/helpers_test.go +++ b/core/chains/evm/client/helpers_test.go @@ -88,6 +88,7 @@ type TestNodePoolConfig struct { NodeFinalizedBlockPollInterval time.Duration NodeErrors config.ClientErrors EnforceRepeatableReadVal bool + NodeDeathDeclarationDelay time.Duration } func (tc TestNodePoolConfig) PollFailureThreshold() uint32 { return tc.NodePollFailureThreshold } @@ -114,6 +115,10 @@ func (tc TestNodePoolConfig) EnforceRepeatableRead() bool { return tc.EnforceRepeatableReadVal } +func (tc TestNodePoolConfig) DeathDeclarationDelay() time.Duration { + return tc.NodeDeathDeclarationDelay +} + func NewChainClientWithTestNode( t *testing.T, nodeCfg commonclient.NodeConfig, @@ -155,7 +160,7 @@ func NewChainClientWithTestNode( var chainType chaintype.ChainType clientErrors := NewTestClientErrors() - c := NewChainClient(lggr, nodeCfg.SelectionMode(), leaseDuration, noNewHeadsThreshold, primaries, sendonlys, chainID, chainType, &clientErrors) + c := NewChainClient(lggr, nodeCfg.SelectionMode(), leaseDuration, noNewHeadsThreshold, primaries, sendonlys, chainID, chainType, &clientErrors, 0) t.Cleanup(c.Close) return c, nil } @@ -170,7 +175,7 @@ func NewChainClientWithEmptyNode( lggr := logger.Test(t) var chainType chaintype.ChainType - c := NewChainClient(lggr, selectionMode, leaseDuration, noNewHeadsThreshold, nil, nil, chainID, chainType, nil) + c := NewChainClient(lggr, selectionMode, leaseDuration, noNewHeadsThreshold, nil, nil, chainID, chainType, nil, 0) t.Cleanup(c.Close) return c } @@ -196,7 +201,7 @@ func NewChainClientWithMockedRpc( 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} clientErrors := NewTestClientErrors() - c := NewChainClient(lggr, selectionMode, leaseDuration, noNewHeadsThreshold, primaries, nil, chainID, chainType, &clientErrors) + c := NewChainClient(lggr, selectionMode, leaseDuration, noNewHeadsThreshold, primaries, nil, chainID, chainType, &clientErrors, 0) t.Cleanup(c.Close) return c } diff --git a/core/chains/evm/config/chain_scoped_node_pool.go b/core/chains/evm/config/chain_scoped_node_pool.go index 3a6d5602589..a4974366486 100644 --- a/core/chains/evm/config/chain_scoped_node_pool.go +++ b/core/chains/evm/config/chain_scoped_node_pool.go @@ -43,3 +43,7 @@ func (n *NodePoolConfig) Errors() ClientErrors { return &clientErrorsConfig{c: n func (n *NodePoolConfig) EnforceRepeatableRead() bool { return *n.C.EnforceRepeatableRead } + +func (n *NodePoolConfig) DeathDeclarationDelay() time.Duration { + return n.C.DeathDeclarationDelay.Duration() +} diff --git a/core/chains/evm/config/config.go b/core/chains/evm/config/config.go index f0287e65431..ffb2a496baf 100644 --- a/core/chains/evm/config/config.go +++ b/core/chains/evm/config/config.go @@ -172,6 +172,7 @@ type NodePool interface { FinalizedBlockPollInterval() time.Duration Errors() ClientErrors EnforceRepeatableRead() bool + DeathDeclarationDelay() time.Duration } // TODO BCF-2509 does the chainscopedconfig really need the entire app config? diff --git a/core/chains/evm/config/config_test.go b/core/chains/evm/config/config_test.go index 8f1921f551f..ba362bda981 100644 --- a/core/chains/evm/config/config_test.go +++ b/core/chains/evm/config/config_test.go @@ -327,6 +327,7 @@ func TestNodePoolConfig(t *testing.T) { require.Equal(t, uint32(5), cfg.EVM().NodePool().PollFailureThreshold()) require.Equal(t, false, cfg.EVM().NodePool().NodeIsSyncingEnabled()) require.Equal(t, false, cfg.EVM().NodePool().EnforceRepeatableRead()) + require.Equal(t, time.Duration(10000000000), cfg.EVM().NodePool().DeathDeclarationDelay()) } func TestClientErrorsConfig(t *testing.T) { diff --git a/core/chains/evm/config/toml/config.go b/core/chains/evm/config/toml/config.go index b16b3e469ac..394cc403472 100644 --- a/core/chains/evm/config/toml/config.go +++ b/core/chains/evm/config/toml/config.go @@ -848,6 +848,7 @@ type NodePool struct { FinalizedBlockPollInterval *commonconfig.Duration Errors ClientErrors `toml:",omitempty"` EnforceRepeatableRead *bool + DeathDeclarationDelay *commonconfig.Duration } func (p *NodePool) setFrom(f *NodePool) { @@ -876,6 +877,10 @@ func (p *NodePool) setFrom(f *NodePool) { if v := f.EnforceRepeatableRead; v != nil { p.EnforceRepeatableRead = v } + + if v := f.DeathDeclarationDelay; v != nil { + p.DeathDeclarationDelay = v + } p.Errors.setFrom(&f.Errors) } diff --git a/core/chains/evm/config/toml/defaults/fallback.toml b/core/chains/evm/config/toml/defaults/fallback.toml index b207dd6c0dd..a11e646e08b 100644 --- a/core/chains/evm/config/toml/defaults/fallback.toml +++ b/core/chains/evm/config/toml/defaults/fallback.toml @@ -70,6 +70,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 diff --git a/core/config/docs/chains-evm.toml b/core/config/docs/chains-evm.toml index 0170d37f51c..19c74913bf3 100644 --- a/core/config/docs/chains-evm.toml +++ b/core/config/docs/chains-evm.toml @@ -88,6 +88,9 @@ RPCBlockQueryDelay = 1 # Default # FinalizedBlockOffset defines the number of blocks by which the latest finalized block will be shifted. E.g if RPC returns block 100 as latest finalized, node will treat block `100 - FinalizedBlockOffset` as the latest finalized block. # In case of `FinalityTagEnabled = false` Node will treat block `latest - FinalityDepth - FinalizedBlockOffset` as the latest finalized block. # With `EnforceRepeatableRead = true`, RPC is considered healthy only if its most recent finalized block is larger or equal to the highest finalized block observed by the Node minus `FinalizedBlockOffset`. +# CAUTION: Setting this to values higher than 0 increases the number of RPCs considered healthy but can delay transaction production. +# PoS chains with FinalityTags enabled (e.g., Ethereum Mainnet) must be handled with special care, as blocks are finalized in batches (epochs). +# Thus, the `FinalizedBlockOffset=1` configuration will result in larger delays (e.g., 32 blocks for Ethereum Mainnet). FinalizedBlockOffset = 0 # Default [EVM.Transactions] @@ -378,6 +381,11 @@ FinalizedBlockPollInterval = '5s' # Default # # Set false to disable EnforceRepeatableRead = false # Default +# DeathDeclarationDelay defines the minimum duration an RPC must be in unhealthy state before producing an error log message. +# Larger values might be helpful to reduce the noisiness of health checks like `EnforceRepeatableRead = true', which might be falsely +# trigger declaration of `FinalizedBlockOutOfSync` due to insignificant network delays in broadcasting of the finalized state among RPCs. +# RPC will not be picked to handle a request even if this option is set to a nonzero value. +DeathDeclarationDelay = '10s' # Default # **ADVANCED** # Errors enable the node to provide custom regex patterns to match against error messages from RPCs. diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index f413c4959e9..dc9bd9d5cc0 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -591,6 +591,7 @@ func TestConfig_Marshal(t *testing.T) { NodeIsSyncingEnabled: ptr(true), FinalizedBlockPollInterval: &second, EnforceRepeatableRead: ptr(true), + DeathDeclarationDelay: &minute, Errors: evmcfg.ClientErrors{ NonceTooLow: ptr[string]("(: |^)nonce too low"), NonceTooHigh: ptr[string]("(: |^)nonce too high"), @@ -1060,6 +1061,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = true FinalizedBlockPollInterval = '1s' EnforceRepeatableRead = true +DeathDeclarationDelay = '1m0s' [EVM.NodePool.Errors] NonceTooLow = '(: |^)nonce too low' diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index 07f80d94fcd..c76ae58f425 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -354,6 +354,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = true FinalizedBlockPollInterval = '1s' EnforceRepeatableRead = true +DeathDeclarationDelay = '1m0s' [EVM.NodePool.Errors] NonceTooLow = '(: |^)nonce too low' diff --git a/core/services/chainlink/testdata/config-multi-chain-effective.toml b/core/services/chainlink/testdata/config-multi-chain-effective.toml index 5a1657dc1c4..6bffb85de86 100644 --- a/core/services/chainlink/testdata/config-multi-chain-effective.toml +++ b/core/services/chainlink/testdata/config-multi-chain-effective.toml @@ -325,6 +325,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [EVM.OCR] ContractConfirmations = 4 @@ -423,6 +424,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [EVM.OCR] ContractConfirmations = 4 @@ -515,6 +517,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [EVM.OCR] ContractConfirmations = 4 diff --git a/core/web/resolver/testdata/config-full.toml b/core/web/resolver/testdata/config-full.toml index fbecbc2d8a7..a3c3dd4aa1d 100644 --- a/core/web/resolver/testdata/config-full.toml +++ b/core/web/resolver/testdata/config-full.toml @@ -353,6 +353,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [EVM.NodePool.Errors] NonceTooLow = '(: |^)nonce too low' diff --git a/core/web/resolver/testdata/config-multi-chain-effective.toml b/core/web/resolver/testdata/config-multi-chain-effective.toml index 815d78e7682..22469c5a166 100644 --- a/core/web/resolver/testdata/config-multi-chain-effective.toml +++ b/core/web/resolver/testdata/config-multi-chain-effective.toml @@ -325,6 +325,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [EVM.OCR] ContractConfirmations = 4 @@ -423,6 +424,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [EVM.OCR] ContractConfirmations = 4 @@ -515,6 +517,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [EVM.OCR] ContractConfirmations = 4 diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 4a69f114547..ef2c3a5a1b6 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -1805,6 +1805,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -1897,6 +1898,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -1989,6 +1991,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -2081,6 +2084,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -2174,6 +2178,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -2266,6 +2271,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -2358,6 +2364,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -2451,6 +2458,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -2543,6 +2551,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -2634,6 +2643,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -2725,6 +2735,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -2817,6 +2828,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -2910,6 +2922,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -3002,6 +3015,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -3094,6 +3108,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -3186,6 +3201,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -3278,6 +3294,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -3370,6 +3387,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -3462,6 +3480,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -3554,6 +3573,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -3646,6 +3666,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -3738,6 +3759,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -3831,6 +3853,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -3923,6 +3946,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -4014,6 +4038,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -4106,6 +4131,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -4198,6 +4224,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -4290,6 +4317,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -4382,6 +4410,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -4473,6 +4502,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -4565,6 +4595,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -4657,6 +4688,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -4749,6 +4781,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -4841,6 +4874,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -4932,6 +4966,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -5024,6 +5059,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -5116,6 +5152,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -5209,6 +5246,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -5301,6 +5339,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -5393,6 +5432,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -5485,6 +5525,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -5577,6 +5618,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -5668,6 +5710,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -5759,6 +5802,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -5850,6 +5894,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -5942,6 +5987,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -6034,6 +6080,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -6125,6 +6172,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -6217,6 +6265,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -6309,6 +6358,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -6402,6 +6452,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -6495,6 +6546,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -6587,6 +6639,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -6679,6 +6732,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -6771,6 +6825,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -6863,6 +6918,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -6955,6 +7011,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 1 @@ -7047,6 +7104,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -7139,6 +7197,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [OCR] ContractConfirmations = 4 @@ -7334,6 +7393,9 @@ FinalizedBlockOffset = 0 # Default FinalizedBlockOffset defines the number of blocks by which the latest finalized block will be shifted. E.g if RPC returns block 100 as latest finalized, node will treat block `100 - FinalizedBlockOffset` as the latest finalized block. In case of `FinalityTagEnabled = false` Node will treat block `latest - FinalityDepth - FinalizedBlockOffset` as the latest finalized block. With `EnforceRepeatableRead = true`, RPC is considered healthy only if its most recent finalized block is larger or equal to the highest finalized block observed by the Node minus `FinalizedBlockOffset`. +CAUTION: Setting this to values higher than 0 increases the number of RPCs considered healthy but can delay transaction production. +PoS chains with FinalityTags enabled (e.g., Ethereum Mainnet) must be handled with special care, as blocks are finalized in batches (epochs). +Thus, the `FinalizedBlockOffset=1` configuration will result in larger delays (e.g., 32 blocks for Ethereum Mainnet). ## EVM.Transactions ```toml @@ -7843,6 +7905,7 @@ LeaseDuration = '0s' # Default NodeIsSyncingEnabled = false # Default FinalizedBlockPollInterval = '5s' # Default EnforceRepeatableRead = false # Default +DeathDeclarationDelay = '10s' # Default ``` The node pool manages multiple RPC endpoints. @@ -7926,6 +7989,15 @@ block. Set false to disable +### DeathDeclarationDelay +```toml +DeathDeclarationDelay = '10s' # Default +``` +DeathDeclarationDelay defines the minimum duration an RPC must be in unhealthy state before producing an error log message. +Larger values might be helpful to reduce the noisiness of health checks like `EnforceRepeatableRead = true', which might be falsely +trigger declaration of `FinalizedBlockOutOfSync` due to insignificant network delays in broadcasting of the finalized state among RPCs. +RPC will not be picked to handle a request even if this option is set to a nonzero value. + ## EVM.NodePool.Errors :warning: **_ADVANCED_**: _Do not change these settings unless you know what you are doing._ ```toml diff --git a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar index bd6e6b2e0b4..0454b9c4e6e 100644 --- a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar @@ -381,6 +381,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [EVM.OCR] ContractConfirmations = 4 diff --git a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar index 1fa25e25e21..9b9a939b970 100644 --- a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar @@ -381,6 +381,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [EVM.OCR] ContractConfirmations = 4 diff --git a/testdata/scripts/node/validate/disk-based-logging.txtar b/testdata/scripts/node/validate/disk-based-logging.txtar index b07d5092854..69a166f9e40 100644 --- a/testdata/scripts/node/validate/disk-based-logging.txtar +++ b/testdata/scripts/node/validate/disk-based-logging.txtar @@ -381,6 +381,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [EVM.OCR] ContractConfirmations = 4 diff --git a/testdata/scripts/node/validate/invalid.txtar b/testdata/scripts/node/validate/invalid.txtar index 72bfbfa4952..fefe6b3a627 100644 --- a/testdata/scripts/node/validate/invalid.txtar +++ b/testdata/scripts/node/validate/invalid.txtar @@ -371,6 +371,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [EVM.OCR] ContractConfirmations = 4 diff --git a/testdata/scripts/node/validate/valid.txtar b/testdata/scripts/node/validate/valid.txtar index 33775f52e3f..9f59e065f41 100644 --- a/testdata/scripts/node/validate/valid.txtar +++ b/testdata/scripts/node/validate/valid.txtar @@ -378,6 +378,7 @@ LeaseDuration = '0s' NodeIsSyncingEnabled = false FinalizedBlockPollInterval = '5s' EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' [EVM.OCR] ContractConfirmations = 4 From 073707c304e3598d286bde57808cbc34892db54a Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Fri, 14 Jun 2024 16:51:18 +0200 Subject: [PATCH 45/53] Comments adjustments --- common/client/mock_node_test.go | 9 +-- .../mock_pool_chain_info_provider_test.go | 6 +- common/client/multi_node.go | 6 +- common/client/multi_node_test.go | 34 +++++------ common/client/node.go | 4 +- common/client/node_fsm.go | 10 ++-- common/client/node_lifecycle_test.go | 2 +- common/client/types.go | 13 ++-- core/chains/evm/client/chain_client.go | 3 + core/chains/evm/client/rpc_client.go | 14 ++--- core/chains/evm/client/rpc_client_test.go | 60 +++++++++---------- core/config/docs/chains-evm.toml | 17 ++++-- docs/CONFIG.md | 17 ++++-- 13 files changed, 105 insertions(+), 90 deletions(-) diff --git a/common/client/mock_node_test.go b/common/client/mock_node_test.go index 055fcf84807..d8671594dac 100644 --- a/common/client/mock_node_test.go +++ b/common/client/mock_node_test.go @@ -5,8 +5,9 @@ package client import ( context "context" - types "github.com/smartcontractkit/chainlink/v2/common/types" mock "github.com/stretchr/testify/mock" + + types "github.com/smartcontractkit/chainlink/v2/common/types" ) // mockNode is an autogenerated mock type for the Node type @@ -14,12 +15,12 @@ type mockNode[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] stru mock.Mock } -// AppLayerObservations provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) AppLayerObservations() ChainInfo { +// HighestUserObservations provides a mock function with given fields: +func (_m *mockNode[CHAIN_ID, HEAD, RPC]) HighestUserObservations() ChainInfo { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for AppLayerObservations") + panic("no return value specified for HighestUserObservations") } var r0 ChainInfo diff --git a/common/client/mock_pool_chain_info_provider_test.go b/common/client/mock_pool_chain_info_provider_test.go index 1b250eaea75..747a29dc6ee 100644 --- a/common/client/mock_pool_chain_info_provider_test.go +++ b/common/client/mock_pool_chain_info_provider_test.go @@ -9,12 +9,12 @@ type mockPoolChainInfoProvider struct { mock.Mock } -// AppLayerObservations provides a mock function with given fields: -func (_m *mockPoolChainInfoProvider) AppLayerObservations() ChainInfo { +// HighestUserObservations provides a mock function with given fields: +func (_m *mockPoolChainInfoProvider) HighestUserObservations() ChainInfo { ret := _m.Called() if len(ret) == 0 { - panic("no return value specified for AppLayerObservations") + panic("no return value specified for HighestUserObservations") } var r0 ChainInfo diff --git a/common/client/multi_node.go b/common/client/multi_node.go index 82837f983bf..0cb565b969d 100644 --- a/common/client/multi_node.go +++ b/common/client/multi_node.go @@ -283,13 +283,13 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return nLiveNodes, ch } -// AppLayerObservations - 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]) AppLayerObservations() ChainInfo { +// 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 { ch := ChainInfo{ TotalDifficulty: big.NewInt(0), } for _, n := range c.nodes { - nodeChainInfo := n.AppLayerObservations() + nodeChainInfo := n.HighestUserObservations() ch.BlockNumber = max(ch.BlockNumber, nodeChainInfo.BlockNumber) ch.FinalizedBlockNumber = max(ch.FinalizedBlockNumber, nodeChainInfo.FinalizedBlockNumber) ch.SetTotalDifficultyIfGt(nodeChainInfo.TotalDifficulty) diff --git a/common/client/multi_node_test.go b/common/client/multi_node_test.go index dc280e0e178..a887350f868 100644 --- a/common/client/multi_node_test.go +++ b/common/client/multi_node_test.go @@ -433,23 +433,23 @@ func TestMultiNode_selectNode(t *testing.T) { func TestMultiNode_ChainInfo(t *testing.T) { t.Parallel() type nodeParams struct { - LatestChainInfo ChainInfo - AppLayerObservations ChainInfo - State nodeState + LatestChainInfo ChainInfo + HighestUserObservations ChainInfo + State nodeState } testCases := []struct { - Name string - ExpectedNLiveNodes int - ExpectedLatestChainInfo ChainInfo - ExpectedAppLayerObservations ChainInfo - NodeParams []nodeParams + Name string + ExpectedNLiveNodes int + ExpectedLatestChainInfo ChainInfo + ExpectedHighestUserObservations ChainInfo + NodeParams []nodeParams }{ { Name: "no nodes", ExpectedLatestChainInfo: ChainInfo{ TotalDifficulty: big.NewInt(0), }, - ExpectedAppLayerObservations: ChainInfo{ + ExpectedHighestUserObservations: ChainInfo{ TotalDifficulty: big.NewInt(0), }, }, @@ -461,7 +461,7 @@ func TestMultiNode_ChainInfo(t *testing.T) { FinalizedBlockNumber: 10, TotalDifficulty: big.NewInt(10), }, - ExpectedAppLayerObservations: ChainInfo{ + ExpectedHighestUserObservations: ChainInfo{ BlockNumber: 1005, FinalizedBlockNumber: 995, TotalDifficulty: big.NewInt(2005), @@ -474,7 +474,7 @@ func TestMultiNode_ChainInfo(t *testing.T) { FinalizedBlockNumber: 990, TotalDifficulty: big.NewInt(2000), }, - AppLayerObservations: ChainInfo{ + HighestUserObservations: ChainInfo{ BlockNumber: 1005, FinalizedBlockNumber: 995, TotalDifficulty: big.NewInt(2005), @@ -487,7 +487,7 @@ func TestMultiNode_ChainInfo(t *testing.T) { FinalizedBlockNumber: 10, TotalDifficulty: big.NewInt(9), }, - AppLayerObservations: ChainInfo{ + HighestUserObservations: ChainInfo{ BlockNumber: 25, FinalizedBlockNumber: 15, TotalDifficulty: big.NewInt(14), @@ -500,7 +500,7 @@ func TestMultiNode_ChainInfo(t *testing.T) { FinalizedBlockNumber: 9, TotalDifficulty: big.NewInt(10), }, - AppLayerObservations: ChainInfo{ + HighestUserObservations: ChainInfo{ BlockNumber: 24, FinalizedBlockNumber: 14, TotalDifficulty: big.NewInt(15), @@ -513,7 +513,7 @@ func TestMultiNode_ChainInfo(t *testing.T) { FinalizedBlockNumber: 1, TotalDifficulty: nil, }, - AppLayerObservations: ChainInfo{ + HighestUserObservations: ChainInfo{ BlockNumber: 16, FinalizedBlockNumber: 6, TotalDifficulty: nil, @@ -534,7 +534,7 @@ func TestMultiNode_ChainInfo(t *testing.T) { for _, params := range tc.NodeParams { node := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) node.On("StateAndLatest").Return(params.State, params.LatestChainInfo) - node.On("AppLayerObservations").Return(params.AppLayerObservations) + node.On("HighestUserObservations").Return(params.HighestUserObservations) mn.nodes = append(mn.nodes, node) } @@ -542,8 +542,8 @@ func TestMultiNode_ChainInfo(t *testing.T) { assert.Equal(t, tc.ExpectedNLiveNodes, nNodes) assert.Equal(t, tc.ExpectedLatestChainInfo, latestChainInfo) - highestChainInfo := mn.AppLayerObservations() - assert.Equal(t, tc.ExpectedAppLayerObservations, highestChainInfo) + highestChainInfo := mn.HighestUserObservations() + assert.Equal(t, tc.ExpectedHighestUserObservations, highestChainInfo) }) } } diff --git a/common/client/node.go b/common/client/node.go index cc97d7980f7..7871c622eb4 100644 --- a/common/client/node.go +++ b/common/client/node.go @@ -66,8 +66,8 @@ type Node[ State() nodeState // StateAndLatest returns nodeState with the latest ChainInfo observed by Node during current lifecycle. StateAndLatest() (nodeState, ChainInfo) - // AppLayerObservations - returns highest ChainInfo ever observed by underlying RPC excluding results of health check requests - AppLayerObservations() ChainInfo + // HighestUserObservations - returns highest ChainInfo ever observed by underlying RPC excluding results of health check requests + HighestUserObservations() ChainInfo SetPoolChainInfoProvider(PoolChainInfoProvider) // Name is a unique identifier for this node. Name() string diff --git a/common/client/node_fsm.go b/common/client/node_fsm.go index 014bfa94259..5a5e2554431 100644 --- a/common/client/node_fsm.go +++ b/common/client/node_fsm.go @@ -148,7 +148,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) isFinalizedBlockOutOfSync() bool { return false } - highestObservedByCaller := n.poolInfoProvider.AppLayerObservations() + highestObservedByCaller := n.poolInfoProvider.HighestUserObservations() latest, _ := n.rpc.GetInterceptedChainInfo() if n.chainCfg.FinalityTagEnabled() { return latest.FinalizedBlockNumber < highestObservedByCaller.FinalizedBlockNumber-int64(n.chainCfg.FinalizedBlockOffset()) @@ -165,10 +165,10 @@ func (n *node[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, ChainInfo) { return n.recalculateState(), latest } -// AppLayerObservations - returns highest ChainInfo ever observed by external user of the Node -func (n *node[CHAIN_ID, HEAD, RPC]) AppLayerObservations() ChainInfo { - _, appLayerObservations := n.rpc.GetInterceptedChainInfo() - return appLayerObservations +// HighestUserObservations - returns highest ChainInfo ever observed by external user of the Node +func (n *node[CHAIN_ID, HEAD, RPC]) HighestUserObservations() ChainInfo { + _, highestUserObservations := n.rpc.GetInterceptedChainInfo() + return highestUserObservations } func (n *node[CHAIN_ID, HEAD, RPC]) SetPoolChainInfoProvider(poolInfoProvider PoolChainInfoProvider) { n.poolInfoProvider = poolInfoProvider diff --git a/common/client/node_lifecycle_test.go b/common/client/node_lifecycle_test.go index 177f66bc792..ed09aeb2d58 100644 --- a/common/client/node_lifecycle_test.go +++ b/common/client/node_lifecycle_test.go @@ -1669,7 +1669,7 @@ func TestNode_State(t *testing.T) { rpc: rpc, }) poolInfo := newMockPoolChainInfoProvider(t) - poolInfo.On("AppLayerObservations").Return(tc.PoolChainInfo).Once() + poolInfo.On("HighestUserObservations").Return(tc.PoolChainInfo).Once() node.SetPoolChainInfoProvider(poolInfo) node.setState(nodeStateAlive) assert.Equal(t, tc.ExpectedState, node.State()) diff --git a/common/client/types.go b/common/client/types.go index 72670bc328d..0938331f287 100644 --- a/common/client/types.go +++ b/common/client/types.go @@ -77,7 +77,7 @@ type NodeClient[ LatestFinalizedBlock(ctx context.Context) (HEAD, error) // 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. - // appLayerObservations ChainInfo is the highest ChainInfo observed excluding health checks calls. + // highestUserObservations ChainInfo is the highest ChainInfo observed excluding health checks calls. // Its values must not be reset. // The results of corresponding calls, to get the most recent head and the latest finalized head, must be // intercepted and reflected in ChainInfo before being returned to a caller. Otherwise, MultiNode is not able to @@ -85,7 +85,7 @@ type NodeClient[ // DisconnectAll must reset latest ChainInfo to default value. // Ensure implementation does not have a race condition when values are reset before request completion and as // a result latest ChainInfo contains information from the previous cycle. - GetInterceptedChainInfo() (latest, appLayerObservations ChainInfo) + GetInterceptedChainInfo() (latest, highestUserObservations ChainInfo) } // clientAPI includes all the direct RPC methods required by the generalized common client to implement its own. @@ -164,12 +164,13 @@ type connection[ // //go:generate mockery --quiet --name PoolChainInfoProvider --structname mockPoolChainInfoProvider --filename "mock_pool_chain_info_provider_test.go" --inpackage --case=underscore type PoolChainInfoProvider interface { - // LatestChainInfo - returns number of live nodes available in the pool, so we can prevent the last alive node in a pool from being. - // Return highest latest ChainInfo within the alive nodes. E.g. most recent block number and highest block number + // LatestChainInfo - returns number of live nodes available in the pool, so we can prevent the last alive node in a pool from being + // moved to out-of-sync state. It is better to have one out-of-sync node than no nodes at all. + // Returns highest latest ChainInfo within the alive nodes. E.g. most recent block number and highest block number // observed by Node A are 10 and 15; Node B - 12 and 14. This method will return 12. LatestChainInfo() (int, ChainInfo) - // AppLayerObservations - returns highest ChainInfo ever observed by any user of MultiNode. - AppLayerObservations() ChainInfo + // HighestUserObservations - returns highest ChainInfo ever observed by any user of MultiNode. + HighestUserObservations() ChainInfo } // ChainInfo - defines RPC's or MultiNode's view on the chain diff --git a/core/chains/evm/client/chain_client.go b/core/chains/evm/client/chain_client.go index 0fb465f816a..ebe6ac6fd5c 100644 --- a/core/chains/evm/client/chain_client.go +++ b/core/chains/evm/client/chain_client.go @@ -60,6 +60,9 @@ type Client interface { 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) + // LatestFinalizedBlock - returns the latest finalized block as it's returned from an RPC. + // CAUTION: Do not use this or any other ChainClient method to determine the latest finalized block. + // Direct use might cause local finality violations. It's highly recommended to use HeadTracker to get latest finalized block. LatestFinalizedBlock(ctx context.Context) (head *evmtypes.Head, err error) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (commonclient.SendTxReturnCode, error) diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index 39bc41fc84a..7295485eb7c 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -101,7 +101,7 @@ type RPCClient interface { 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, appLayerObservations commonclient.ChainInfo) + GetInterceptedChainInfo() (latest, highestUserObservations commonclient.ChainInfo) } type rawclient struct { @@ -135,7 +135,7 @@ type rpcClient struct { chStopInFlight chan struct{} // intercepted values seen by callers of the rpcClient excluding health check calls. Need to ensure MultiNode provides repeatable read guarantee - appLayerObservations commonclient.ChainInfo + highestUserObservations commonclient.ChainInfo // most recent chain info observed during current lifecycle (reseted on DisconnectAll) latestChainInfo commonclient.ChainInfo } @@ -1208,8 +1208,8 @@ func (r *rpcClient) onNewHead(ctx context.Context, requestCh <-chan struct{}, he r.stateMu.Lock() defer r.stateMu.Unlock() if !commonclient.CtxIsHeathCheckRequest(ctx) { - r.appLayerObservations.BlockNumber = max(r.appLayerObservations.BlockNumber, head.Number) - r.appLayerObservations.SetTotalDifficultyIfGt(head.TotalDifficulty) + r.highestUserObservations.BlockNumber = max(r.highestUserObservations.BlockNumber, head.Number) + r.highestUserObservations.SetTotalDifficultyIfGt(head.TotalDifficulty) } select { case <-requestCh: // no need to update latestChainInfo, as rpcClient already started new life cycle @@ -1227,7 +1227,7 @@ func (r *rpcClient) onNewFinalizedHead(ctx context.Context, requestCh <-chan str r.stateMu.Lock() defer r.stateMu.Unlock() if !commonclient.CtxIsHeathCheckRequest(ctx) { - r.appLayerObservations.FinalizedBlockNumber = max(r.appLayerObservations.FinalizedBlockNumber, head.Number) + r.highestUserObservations.FinalizedBlockNumber = max(r.highestUserObservations.FinalizedBlockNumber, head.Number) } select { case <-requestCh: // no need to update latestChainInfo, as rpcClient already started new life cycle @@ -1237,10 +1237,10 @@ func (r *rpcClient) onNewFinalizedHead(ctx context.Context, requestCh <-chan str } } -func (r *rpcClient) GetInterceptedChainInfo() (latest, appLayerObservations commonclient.ChainInfo) { +func (r *rpcClient) GetInterceptedChainInfo() (latest, highestUserObservations commonclient.ChainInfo) { r.stateMu.RLock() defer r.stateMu.RUnlock() - return r.latestChainInfo, r.appLayerObservations + return r.latestChainInfo, r.highestUserObservations } func ToBlockNumArg(number *big.Int) string { diff --git a/core/chains/evm/client/rpc_client_test.go b/core/chains/evm/client/rpc_client_test.go index fbe2336c7d1..682c4352457 100644 --- a/core/chains/evm/client/rpc_client_test.go +++ b/core/chains/evm/client/rpc_client_test.go @@ -60,13 +60,13 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) // set to default values - latest, appLayerObservations := rpc.GetInterceptedChainInfo() + latest, highestUserObservations := rpc.GetInterceptedChainInfo() assert.Equal(t, int64(0), latest.BlockNumber) assert.Equal(t, int64(0), latest.FinalizedBlockNumber) assert.Nil(t, latest.TotalDifficulty) - assert.Equal(t, int64(0), appLayerObservations.BlockNumber) - assert.Equal(t, int64(0), appLayerObservations.FinalizedBlockNumber) - assert.Nil(t, appLayerObservations.TotalDifficulty) + assert.Equal(t, int64(0), highestUserObservations.BlockNumber) + 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) @@ -79,28 +79,28 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { // received 128 head <-ch - latest, appLayerObservations = rpc.GetInterceptedChainInfo() + latest, highestUserObservations = rpc.GetInterceptedChainInfo() assert.Equal(t, int64(128), latest.BlockNumber) assert.Equal(t, int64(0), latest.FinalizedBlockNumber) assert.Equal(t, big.NewInt(500), latest.TotalDifficulty) - assertAppLayerObservations := func(appLayerObservations commonclient.ChainInfo) { - assert.Equal(t, int64(256), appLayerObservations.BlockNumber) - assert.Equal(t, int64(0), appLayerObservations.FinalizedBlockNumber) - assert.Equal(t, big.NewInt(1000), appLayerObservations.TotalDifficulty) + assertHighestUserObservations := func(highestUserObservations commonclient.ChainInfo) { + assert.Equal(t, int64(256), highestUserObservations.BlockNumber) + assert.Equal(t, int64(0), highestUserObservations.FinalizedBlockNumber) + assert.Equal(t, big.NewInt(1000), highestUserObservations.TotalDifficulty) } - assertAppLayerObservations(appLayerObservations) + assertHighestUserObservations(highestUserObservations) // DisconnectAll resets latest rpc.DisconnectAll() - latest, appLayerObservations = rpc.GetInterceptedChainInfo() + latest, highestUserObservations = rpc.GetInterceptedChainInfo() assert.Equal(t, int64(0), latest.BlockNumber) assert.Equal(t, int64(0), latest.FinalizedBlockNumber) assert.Nil(t, latest.TotalDifficulty) - assertAppLayerObservations(appLayerObservations) + assertHighestUserObservations(highestUserObservations) }) t.Run("App layer observations are not affected by new block if health check flag is present", func(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) @@ -117,14 +117,14 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { // received 256 head <-ch - latest, appLayerObservations := rpc.GetInterceptedChainInfo() + latest, highestUserObservations := rpc.GetInterceptedChainInfo() assert.Equal(t, int64(256), latest.BlockNumber) assert.Equal(t, int64(0), latest.FinalizedBlockNumber) assert.Equal(t, big.NewInt(1000), latest.TotalDifficulty) - assert.Equal(t, int64(0), appLayerObservations.BlockNumber) - assert.Equal(t, int64(0), appLayerObservations.FinalizedBlockNumber) - assert.Equal(t, (*big.Int)(nil), appLayerObservations.TotalDifficulty) + assert.Equal(t, int64(0), highestUserObservations.BlockNumber) + assert.Equal(t, int64(0), highestUserObservations.FinalizedBlockNumber) + assert.Equal(t, (*big.Int)(nil), highestUserObservations.TotalDifficulty) }) t.Run("Block's chain ID matched configured", func(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) @@ -257,43 +257,43 @@ func TestRPCClient_LatestFinalizedBlock(t *testing.T) { // updates chain info _, err := rpc.LatestFinalizedBlock(ctx) require.NoError(t, err) - latest, appLayerObservations := rpc.GetInterceptedChainInfo() + latest, highestUserObservations := rpc.GetInterceptedChainInfo() - assert.Equal(t, int64(0), appLayerObservations.BlockNumber) - assert.Equal(t, int64(128), appLayerObservations.FinalizedBlockNumber) + assert.Equal(t, int64(0), highestUserObservations.BlockNumber) + assert.Equal(t, int64(128), highestUserObservations.FinalizedBlockNumber) assert.Equal(t, int64(0), latest.BlockNumber) assert.Equal(t, int64(128), latest.FinalizedBlockNumber) - // lower block number does not update appLayerObservations + // lower block number does not update highestUserObservations server.Head = &evmtypes.Head{Number: 127} _, err = rpc.LatestFinalizedBlock(ctx) require.NoError(t, err) - latest, appLayerObservations = rpc.GetInterceptedChainInfo() + latest, highestUserObservations = rpc.GetInterceptedChainInfo() - assert.Equal(t, int64(0), appLayerObservations.BlockNumber) - assert.Equal(t, int64(128), appLayerObservations.FinalizedBlockNumber) + assert.Equal(t, int64(0), highestUserObservations.BlockNumber) + assert.Equal(t, int64(128), highestUserObservations.FinalizedBlockNumber) assert.Equal(t, int64(0), latest.BlockNumber) assert.Equal(t, int64(127), latest.FinalizedBlockNumber) - // health check flg prevents change in appLayerObservations + // health check flg prevents change in highestUserObservations server.Head = &evmtypes.Head{Number: 256} _, err = rpc.LatestFinalizedBlock(commonclient.CtxAddHealthCheckFlag(ctx)) require.NoError(t, err) - latest, appLayerObservations = rpc.GetInterceptedChainInfo() + latest, highestUserObservations = rpc.GetInterceptedChainInfo() - assert.Equal(t, int64(0), appLayerObservations.BlockNumber) - assert.Equal(t, int64(128), appLayerObservations.FinalizedBlockNumber) + assert.Equal(t, int64(0), highestUserObservations.BlockNumber) + assert.Equal(t, int64(128), highestUserObservations.FinalizedBlockNumber) assert.Equal(t, int64(0), latest.BlockNumber) assert.Equal(t, int64(256), latest.FinalizedBlockNumber) // DisconnectAll resets latest ChainInfo rpc.DisconnectAll() - latest, appLayerObservations = rpc.GetInterceptedChainInfo() - assert.Equal(t, int64(0), appLayerObservations.BlockNumber) - assert.Equal(t, int64(128), appLayerObservations.FinalizedBlockNumber) + latest, highestUserObservations = rpc.GetInterceptedChainInfo() + assert.Equal(t, int64(0), highestUserObservations.BlockNumber) + assert.Equal(t, int64(128), highestUserObservations.FinalizedBlockNumber) assert.Equal(t, int64(0), latest.BlockNumber) assert.Equal(t, int64(0), latest.FinalizedBlockNumber) diff --git a/core/config/docs/chains-evm.toml b/core/config/docs/chains-evm.toml index 19c74913bf3..66f7fdd2f0a 100644 --- a/core/config/docs/chains-evm.toml +++ b/core/config/docs/chains-evm.toml @@ -85,12 +85,17 @@ RPCDefaultBatchSize = 250 # Default # available from the connected node via RPC, due to race conditions in the code of the remote ETH node. In this case you will get false # "zero" blocks that are missing transactions. RPCBlockQueryDelay = 1 # Default -# FinalizedBlockOffset defines the number of blocks by which the latest finalized block will be shifted. E.g if RPC returns block 100 as latest finalized, node will treat block `100 - FinalizedBlockOffset` as the latest finalized block. -# In case of `FinalityTagEnabled = false` Node will treat block `latest - FinalityDepth - FinalizedBlockOffset` as the latest finalized block. -# With `EnforceRepeatableRead = true`, RPC is considered healthy only if its most recent finalized block is larger or equal to the highest finalized block observed by the Node minus `FinalizedBlockOffset`. -# CAUTION: Setting this to values higher than 0 increases the number of RPCs considered healthy but can delay transaction production. -# PoS chains with FinalityTags enabled (e.g., Ethereum Mainnet) must be handled with special care, as blocks are finalized in batches (epochs). -# Thus, the `FinalizedBlockOffset=1` configuration will result in larger delays (e.g., 32 blocks for Ethereum Mainnet). +# FinalizedBlockOffset defines the number of blocks by which the latest finalized block will be shifted/delayed. +# For example, suppose RPC returns block 100 as the latest finalized. In that case, the CL Node will treat block `100 - FinalizedBlockOffset` as the latest finalized block and `latest - FinalityDepth - FinalizedBlockOffset` in case of `FinalityTagEnabled = false.` +# With `EnforceRepeatableRead = true,` RPC is considered healthy only if its most recent finalized block is larger or equal to the highest finalized block observed by the CL Node minus `FinalizedBlockOffset.` +# Higher values of `FinalizedBlockOffset` with `EnforceRepeatableRead = true` reduce the number of false `FinalizedBlockOutOfSync` declarations on healthy RPCs that are slightly lagging behind due to network delays. +# This may increase the number of healthy RPCs and reduce the probability that the CL Node will not have any healthy alternatives to the active RPC. +# CAUTION: Setting this to values higher than 0 may delay transaction creation in products (e.g., CCIP, Automation) that base their decision on finalized on-chain events. +# PoS chains with `FinalityTagEnabled=true` and batched (epochs) blocks finalization (e.g., Ethereum Mainnet) must be treated with special care as a minor increase in the `FinalizedBlockOffset` may lead to significant delays. +# For example, let's say that `FinalizedBlockOffset = 1` and blocks are finalized in batches of 32. +# The latest finalized block on chain is 64, so block 63 is the latest finalized for CL Node. +# Block 64 will be treated as finalized by CL Node only when chain's latest finalized block is 65. As chain finalizes blocks in batches of 32, +# CL Node has to wait for a whole new batch to be finalized to treat block 64 as finalized. FinalizedBlockOffset = 0 # Default [EVM.Transactions] diff --git a/docs/CONFIG.md b/docs/CONFIG.md index ef2c3a5a1b6..33346472d5c 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -7390,12 +7390,17 @@ available from the connected node via RPC, due to race conditions in the code of ```toml FinalizedBlockOffset = 0 # Default ``` -FinalizedBlockOffset defines the number of blocks by which the latest finalized block will be shifted. E.g if RPC returns block 100 as latest finalized, node will treat block `100 - FinalizedBlockOffset` as the latest finalized block. -In case of `FinalityTagEnabled = false` Node will treat block `latest - FinalityDepth - FinalizedBlockOffset` as the latest finalized block. -With `EnforceRepeatableRead = true`, RPC is considered healthy only if its most recent finalized block is larger or equal to the highest finalized block observed by the Node minus `FinalizedBlockOffset`. -CAUTION: Setting this to values higher than 0 increases the number of RPCs considered healthy but can delay transaction production. -PoS chains with FinalityTags enabled (e.g., Ethereum Mainnet) must be handled with special care, as blocks are finalized in batches (epochs). -Thus, the `FinalizedBlockOffset=1` configuration will result in larger delays (e.g., 32 blocks for Ethereum Mainnet). +FinalizedBlockOffset defines the number of blocks by which the latest finalized block will be shifted/delayed. +For example, suppose RPC returns block 100 as the latest finalized. In that case, the CL Node will treat block `100 - FinalizedBlockOffset` as the latest finalized block and `latest - FinalityDepth - FinalizedBlockOffset` in case of `FinalityTagEnabled = false.` +With `EnforceRepeatableRead = true,` RPC is considered healthy only if its most recent finalized block is larger or equal to the highest finalized block observed by the CL Node minus `FinalizedBlockOffset.` +Higher values of `FinalizedBlockOffset` with `EnforceRepeatableRead = true` reduce the number of false `FinalizedBlockOutOfSync` declarations on healthy RPCs that are slightly lagging behind due to network delays. +This may increase the number of healthy RPCs and reduce the probability that the CL Node will not have any healthy alternatives to the active RPC. +CAUTION: Setting this to values higher than 0 may delay transaction creation in products (e.g., CCIP, Automation) that base their decision on finalized on-chain events. +PoS chains with `FinalityTagEnabled=true` and batched (epochs) blocks finalization (e.g., Ethereum Mainnet) must be treated with special care as a minor increase in the `FinalizedBlockOffset` may lead to significant delays. +For example, let's say that `FinalizedBlockOffset = 1` and blocks are finalized in batches of 32. +The latest finalized block on chain is 64, so block 63 is the latest finalized for CL Node. +Block 64 will be treated as finalized by CL Node only when chain's latest finalized block is 65. As chain finalizes blocks in batches of 32, +CL Node has to wait for a whole new batch to be finalized to treat block 64 as finalized. ## EVM.Transactions ```toml From 7abfbfe7d602be60385532c993c2613fb7d8a707 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Fri, 14 Jun 2024 18:26:53 +0200 Subject: [PATCH 46/53] fix merge issues --- core/capabilities/reader_test.go | 7 ++++++- core/config/docs/chains-evm.toml | 1 - .../relay/evm/evmtesting/chain_reader_interface_tester.go | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/core/capabilities/reader_test.go b/core/capabilities/reader_test.go index 3407ec43a43..d21d1506864 100644 --- a/core/capabilities/reader_test.go +++ b/core/capabilities/reader_test.go @@ -18,7 +18,9 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/types" + evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/keystone_capability_registry" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -82,13 +84,16 @@ func newContractReaderFactory(t *testing.T, simulatedBackend *backends.Simulated testutils.SimulatedChainID, ) db := pgtest.NewSqlxDB(t) + const finalityDepth = 2 + ht := headtracker.NewSimulatedHeadTracker(client, false, finalityDepth) lp := logpoller.NewLogPoller( logpoller.NewORM(testutils.SimulatedChainID, db, lggr), client, lggr, + ht, logpoller.Opts{ PollPeriod: 100 * time.Millisecond, - FinalityDepth: 2, + FinalityDepth: finalityDepth, BackfillBatchSize: 3, RpcBatchSize: 2, KeepFinalizedBlocksDepth: 1000, diff --git a/core/config/docs/chains-evm.toml b/core/config/docs/chains-evm.toml index 66f7fdd2f0a..38c8cb8354f 100644 --- a/core/config/docs/chains-evm.toml +++ b/core/config/docs/chains-evm.toml @@ -391,7 +391,6 @@ EnforceRepeatableRead = false # Default # trigger declaration of `FinalizedBlockOutOfSync` due to insignificant network delays in broadcasting of the finalized state among RPCs. # RPC will not be picked to handle a request even if this option is set to a nonzero value. DeathDeclarationDelay = '10s' # Default - # **ADVANCED** # Errors enable the node to provide custom regex patterns to match against error messages from RPCs. [EVM.NodePool.Errors] diff --git a/core/services/relay/evm/evmtesting/chain_reader_interface_tester.go b/core/services/relay/evm/evmtesting/chain_reader_interface_tester.go index a2c9180d3bf..27a5a2308b2 100644 --- a/core/services/relay/evm/evmtesting/chain_reader_interface_tester.go +++ b/core/services/relay/evm/evmtesting/chain_reader_interface_tester.go @@ -16,6 +16,7 @@ import ( . "github.com/smartcontractkit/chainlink-common/pkg/types/interfacetests" //nolint common practice to import test mods with . "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/chain_reader_tester" _ "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" // force binding for tx type @@ -187,7 +188,8 @@ func (it *EVMChainReaderInterfaceTester[T]) GetChainReader(t T) clcommontypes.Co RpcBatchSize: 1, KeepFinalizedBlocksDepth: 10000, } - lp := logpoller.NewLogPoller(logpoller.NewORM(it.Helper.ChainID(), db, lggr), it.client, lggr, lpOpts) + ht := headtracker.NewSimulatedHeadTracker(it.client, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(logpoller.NewORM(it.Helper.ChainID(), db, lggr), it.client, lggr, ht, lpOpts) require.NoError(t, lp.Start(ctx)) cr, err := evm.NewChainReaderService(ctx, lggr, lp, it.client, it.chainConfig) From b7f02a1295ecfa6f0ab76cd589434b8b8a58e1e9 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Fri, 14 Jun 2024 18:57:41 +0200 Subject: [PATCH 47/53] regen mocks --- common/client/mock_node_test.go | 39 ++++++++++++++++----------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/common/client/mock_node_test.go b/common/client/mock_node_test.go index d8671594dac..f8dfa2b7390 100644 --- a/common/client/mock_node_test.go +++ b/common/client/mock_node_test.go @@ -5,9 +5,8 @@ package client import ( context "context" - mock "github.com/stretchr/testify/mock" - types "github.com/smartcontractkit/chainlink/v2/common/types" + mock "github.com/stretchr/testify/mock" ) // mockNode is an autogenerated mock type for the Node type @@ -15,24 +14,6 @@ type mockNode[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] stru mock.Mock } -// HighestUserObservations provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) HighestUserObservations() ChainInfo { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for HighestUserObservations") - } - - var r0 ChainInfo - if rf, ok := ret.Get(0).(func() ChainInfo); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(ChainInfo) - } - - return r0 -} - // Close provides a mock function with given fields: func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Close() error { ret := _m.Called() @@ -69,6 +50,24 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) ConfiguredChainID() CHAIN_ID { return r0 } +// HighestUserObservations provides a mock function with given fields: +func (_m *mockNode[CHAIN_ID, HEAD, RPC]) HighestUserObservations() ChainInfo { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for HighestUserObservations") + } + + var r0 ChainInfo + if rf, ok := ret.Get(0).(func() ChainInfo); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(ChainInfo) + } + + return r0 +} + // Name provides a mock function with given fields: func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Name() string { ret := _m.Called() From 93030c13933d6c20acf4d7718eaffcb053ee6df0 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Mon, 17 Jun 2024 19:22:36 +0200 Subject: [PATCH 48/53] fix multinode flaky test --- common/client/node_lifecycle_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/client/node_lifecycle_test.go b/common/client/node_lifecycle_test.go index ed09aeb2d58..863a15a1fad 100644 --- a/common/client/node_lifecycle_test.go +++ b/common/client/node_lifecycle_test.go @@ -817,7 +817,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { poolInfo.On("LatestChainInfo").Return(0, ChainInfo{ BlockNumber: 100, TotalDifficulty: big.NewInt(200), - }).Once() + }) node.SetPoolChainInfoProvider(poolInfo) rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: 0}, ChainInfo{BlockNumber: 0}) From 8214f4603d0a3cdb209fedc80674f88f72978993 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Thu, 20 Jun 2024 15:19:08 +0200 Subject: [PATCH 49/53] refactor SetTotalDifficultyIfGt to MaxTotalDifficulty --- common/client/multi_node.go | 4 ++-- common/client/types.go | 17 ++++++++++---- common/client/types_test.go | 34 ++++++++++++++++++++++++++++ core/chains/evm/client/rpc_client.go | 2 +- 4 files changed, 49 insertions(+), 8 deletions(-) create mode 100644 common/client/types_test.go diff --git a/common/client/multi_node.go b/common/client/multi_node.go index 0cb565b969d..c53e5d33b7e 100644 --- a/common/client/multi_node.go +++ b/common/client/multi_node.go @@ -277,7 +277,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP nLiveNodes++ ch.BlockNumber = max(ch.BlockNumber, nodeChainInfo.BlockNumber) ch.FinalizedBlockNumber = max(ch.FinalizedBlockNumber, nodeChainInfo.FinalizedBlockNumber) - ch.SetTotalDifficultyIfGt(nodeChainInfo.TotalDifficulty) + ch.TotalDifficulty = MaxTotalDifficulty(ch.TotalDifficulty, nodeChainInfo.TotalDifficulty) } } return nLiveNodes, ch @@ -292,7 +292,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP nodeChainInfo := n.HighestUserObservations() ch.BlockNumber = max(ch.BlockNumber, nodeChainInfo.BlockNumber) ch.FinalizedBlockNumber = max(ch.FinalizedBlockNumber, nodeChainInfo.FinalizedBlockNumber) - ch.SetTotalDifficultyIfGt(nodeChainInfo.TotalDifficulty) + ch.TotalDifficulty = MaxTotalDifficulty(ch.TotalDifficulty, nodeChainInfo.TotalDifficulty) } return ch } diff --git a/common/client/types.go b/common/client/types.go index 0938331f287..3d548b9deba 100644 --- a/common/client/types.go +++ b/common/client/types.go @@ -180,11 +180,18 @@ type ChainInfo struct { TotalDifficulty *big.Int } -func (c *ChainInfo) SetTotalDifficultyIfGt(td *big.Int) { - if td == nil || (c.TotalDifficulty != nil && c.TotalDifficulty.Cmp(td) >= 0) { - return +func MaxTotalDifficulty(a, b *big.Int) *big.Int { + if a == nil { + if b == nil { + return nil + } + + return big.NewInt(0).Set(b) + } + + if b == nil || a.Cmp(b) >= 0 { + return big.NewInt(0).Set(a) } - // allocate new Int, to prevent read/write race - c.TotalDifficulty = big.NewInt(0).Set(td) + return big.NewInt(0).Set(b) } diff --git a/common/client/types_test.go b/common/client/types_test.go new file mode 100644 index 00000000000..68d7a3fe78e --- /dev/null +++ b/common/client/types_test.go @@ -0,0 +1,34 @@ +package client + +import ( + "math/big" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMaxDifficulty(t *testing.T) { + cases := []struct { + A, B, Result *big.Int + }{ + { + A: nil, B: nil, Result: nil, + }, + { + A: nil, B: big.NewInt(1), Result: big.NewInt(1), + }, + { + A: big.NewInt(1), B: big.NewInt(1), Result: big.NewInt(1), + }, + { + A: big.NewInt(1), B: big.NewInt(2), Result: big.NewInt(2), + }, + } + + for _, test := range cases { + actualResult := MaxTotalDifficulty(test.A, test.B) + assert.Equal(t, test.Result, actualResult, "expected max(%v, %v) to produce %v", test.A, test.B, test.Result) + inverted := MaxTotalDifficulty(test.B, test.A) + assert.Equal(t, actualResult, inverted, "expected max(%v, %v) == max(%v, %v)", test.A, test.B, test.B, test.A) + } +} diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index 7295485eb7c..18744ca4bc1 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -1209,7 +1209,7 @@ func (r *rpcClient) onNewHead(ctx context.Context, requestCh <-chan struct{}, he defer r.stateMu.Unlock() if !commonclient.CtxIsHeathCheckRequest(ctx) { r.highestUserObservations.BlockNumber = max(r.highestUserObservations.BlockNumber, head.Number) - r.highestUserObservations.SetTotalDifficultyIfGt(head.TotalDifficulty) + 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 From ab317a2ada84d6df78c9016cb70cea086ded673d Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko <34754799+dhaidashenko@users.noreply.github.com> Date: Fri, 21 Jun 2024 13:14:34 +0200 Subject: [PATCH 50/53] Use softer language in LatestFinalizedBlock comments Co-authored-by: Dimitris Grigoriou --- core/chains/evm/client/chain_client.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/chains/evm/client/chain_client.go b/core/chains/evm/client/chain_client.go index ebe6ac6fd5c..f18c900b038 100644 --- a/core/chains/evm/client/chain_client.go +++ b/core/chains/evm/client/chain_client.go @@ -61,8 +61,8 @@ type Client interface { HeadByHash(ctx context.Context, n common.Hash) (*evmtypes.Head, error) SubscribeNewHead(ctx context.Context, ch chan<- *evmtypes.Head) (ethereum.Subscription, error) // LatestFinalizedBlock - returns the latest finalized block as it's returned from an RPC. - // CAUTION: Do not use this or any other ChainClient method to determine the latest finalized block. - // Direct use might cause local finality violations. It's highly recommended to use HeadTracker to get latest finalized block. + // CAUTION: Using this method might cause local finality violations. It's highly recommended + // to use HeadTracker to get latest finalized block. LatestFinalizedBlock(ctx context.Context) (head *evmtypes.Head, err error) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (commonclient.SendTxReturnCode, error) From df26dffbe634aad31f40e48a773f2a864627f8e0 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Fri, 21 Jun 2024 14:50:53 +0200 Subject: [PATCH 51/53] nits --- core/chains/evm/client/rpc_client.go | 2 +- core/chains/evm/headtracker/config.go | 21 ---- .../evm/headtracker/head_broadcaster_test.go | 1 - core/chains/evm/headtracker/mocks/config.go | 100 ------------------ 4 files changed, 1 insertion(+), 123 deletions(-) delete mode 100644 core/chains/evm/headtracker/config.go delete mode 100644 core/chains/evm/headtracker/mocks/config.go diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index 18744ca4bc1..6499b18f795 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -303,7 +303,7 @@ func (r *rpcClient) registerSub(sub ethereum.Subscription, stopInFLightCh chan s select { case <-stopInFLightCh: sub.Unsubscribe() - return fmt.Errorf("failed to registred subscription - all in flight requests were canceled") + return fmt.Errorf("failed to register subscription - all in-flight requests were canceled") default: } // TODO: BCI-3358 - delete sub when caller unsubscribes. diff --git a/core/chains/evm/headtracker/config.go b/core/chains/evm/headtracker/config.go deleted file mode 100644 index 8a2807489cf..00000000000 --- a/core/chains/evm/headtracker/config.go +++ /dev/null @@ -1,21 +0,0 @@ -package headtracker - -import ( - "time" - - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" -) - -//go:generate mockery --quiet --name Config --output ./mocks/ --case=underscore - -// Config represents a subset of options needed by head tracker -type Config interface { - BlockEmissionIdleWarningThreshold() time.Duration - FinalityDepth() uint32 - FinalityTagEnabled() bool - FinalizedBlockOffset() uint32 -} - -type HeadTrackerConfig interface { - config.HeadTracker -} diff --git a/core/chains/evm/headtracker/head_broadcaster_test.go b/core/chains/evm/headtracker/head_broadcaster_test.go index 3f60a1a7d0d..7ac61ab34b0 100644 --- a/core/chains/evm/headtracker/head_broadcaster_test.go +++ b/core/chains/evm/headtracker/head_broadcaster_test.go @@ -60,7 +60,6 @@ func TestHeadBroadcaster_Subscribe(t *testing.T) { chchHeaders <- args.Get(1).(chan<- *evmtypes.Head) }). Return(sub, nil) - // 2 for initial and 2 for backfill ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(testutils.Head(1), nil) sub.On("Unsubscribe").Return() diff --git a/core/chains/evm/headtracker/mocks/config.go b/core/chains/evm/headtracker/mocks/config.go deleted file mode 100644 index 05f923d17a7..00000000000 --- a/core/chains/evm/headtracker/mocks/config.go +++ /dev/null @@ -1,100 +0,0 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. - -package mocks - -import ( - time "time" - - mock "github.com/stretchr/testify/mock" -) - -// Config is an autogenerated mock type for the Config type -type Config struct { - mock.Mock -} - -// BlockEmissionIdleWarningThreshold provides a mock function with given fields: -func (_m *Config) BlockEmissionIdleWarningThreshold() time.Duration { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for BlockEmissionIdleWarningThreshold") - } - - var r0 time.Duration - if rf, ok := ret.Get(0).(func() time.Duration); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(time.Duration) - } - - return r0 -} - -// FinalityDepth provides a mock function with given fields: -func (_m *Config) FinalityDepth() uint32 { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for FinalityDepth") - } - - var r0 uint32 - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - return r0 -} - -// FinalityTagEnabled provides a mock function with given fields: -func (_m *Config) FinalityTagEnabled() bool { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for FinalityTagEnabled") - } - - var r0 bool - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - return r0 -} - -// FinalizedBlockOffset provides a mock function with given fields: -func (_m *Config) FinalizedBlockOffset() uint32 { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for FinalizedBlockOffset") - } - - var r0 uint32 - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - return r0 -} - -// NewConfig creates a new instance of Config. 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 NewConfig(t interface { - mock.TestingT - Cleanup(func()) -}) *Config { - mock := &Config{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} From 289fe91e50e200af32868a238583377b99ac9148 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Wed, 26 Jun 2024 15:57:08 +0200 Subject: [PATCH 52/53] Ensure HistoryDepth is >= FinalizedBlockOffset --- core/chains/evm/client/config_builder.go | 4 ++-- core/chains/evm/config/toml/config.go | 5 +++++ core/services/chainlink/config_test.go | 3 ++- core/services/chainlink/testdata/config-invalid.toml | 1 + 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/core/chains/evm/client/config_builder.go b/core/chains/evm/client/config_builder.go index 115d660a90f..19e0f14fd67 100644 --- a/core/chains/evm/client/config_builder.go +++ b/core/chains/evm/client/config_builder.go @@ -38,7 +38,7 @@ func NewClientConfigs( noNewHeadsThreshold time.Duration, finalityDepth *uint32, finalityTagEnabled *bool, - finalityBlockOffset *uint32, + finalizedBlockOffset *uint32, enforceRepeatableRead *bool, deathDeclarationDelay time.Duration, @@ -65,7 +65,7 @@ func NewClientConfigs( FinalityDepth: finalityDepth, FinalityTagEnabled: finalityTagEnabled, NoNewHeadsThreshold: commonconfig.MustNewDuration(noNewHeadsThreshold), - FinalizedBlockOffset: finalityBlockOffset, + FinalizedBlockOffset: finalizedBlockOffset, }, }, } diff --git a/core/chains/evm/config/toml/config.go b/core/chains/evm/config/toml/config.go index 14dc9768d17..3e35bb4b55c 100644 --- a/core/chains/evm/config/toml/config.go +++ b/core/chains/evm/config/toml/config.go @@ -390,6 +390,11 @@ func (c *Chain) ValidateConfig() (err error) { Msg: "must be greater than or equal to 1"}) } + if *c.FinalizedBlockOffset > *c.HeadTracker.HistoryDepth { + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "HeadTracker.HistoryDepth", Value: *c.HeadTracker.HistoryDepth, + Msg: "must be greater than or equal to FinalizedBlockOffset"}) + } + // AutoPurge configs depend on ChainType so handling validation on per chain basis if c.Transactions.AutoPurge.Enabled != nil && *c.Transactions.AutoPurge.Enabled { chainType := c.ChainType.ChainType() diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index b38d3724d9c..4db8fbc9482 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -1294,10 +1294,11 @@ func TestConfig_Validate(t *testing.T) { - WSURL: missing: required for primary nodes - HTTPURL: missing: required for all nodes - 1.HTTPURL: missing: required for all nodes - - 1: 9 errors: + - 1: 10 errors: - ChainType: invalid value (Foo): must not be set with this chain id - Nodes: missing: must have at least one node - ChainType: invalid value (Foo): must be one of arbitrum, celo, gnosis, kroma, metis, optimismBedrock, scroll, wemix, xlayer, zkevm, zksync or omitted + - HeadTracker.HistoryDepth: invalid value (30): must be greater than or equal to FinalizedBlockOffset - GasEstimator.BumpThreshold: invalid value (0): cannot be 0 if auto-purge feature is enabled for Foo - Transactions.AutoPurge.Threshold: missing: needs to be set if auto-purge feature is enabled for Foo - Transactions.AutoPurge.MinAttempts: missing: needs to be set if auto-purge feature is enabled for Foo diff --git a/core/services/chainlink/testdata/config-invalid.toml b/core/services/chainlink/testdata/config-invalid.toml index 68feeeb0451..30fbfff4729 100644 --- a/core/services/chainlink/testdata/config-invalid.toml +++ b/core/services/chainlink/testdata/config-invalid.toml @@ -53,6 +53,7 @@ SendOnly = true ChainID = '1' ChainType = 'Foo' FinalityDepth = 32 +FinalizedBlockOffset = 64 [EVM.Transactions.AutoPurge] Enabled = true From 992aeb45a524cf2970856aa1e06a8edcf43e8c92 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Thu, 27 Jun 2024 17:39:28 +0200 Subject: [PATCH 53/53] regen mocks with newer version --- common/client/mock_pool_chain_info_provider_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/client/mock_pool_chain_info_provider_test.go b/common/client/mock_pool_chain_info_provider_test.go index 747a29dc6ee..4e4955e7381 100644 --- a/common/client/mock_pool_chain_info_provider_test.go +++ b/common/client/mock_pool_chain_info_provider_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package client