rpcclient: drop deprecated Notifications an Subscribe*

Signed-off-by: Roman Khimov <roman@nspcc.ru>
This commit is contained in:
Roman Khimov 2023-10-09 22:43:49 +03:00
parent 413caac941
commit eb995c8600
5 changed files with 9 additions and 311 deletions

View file

@ -26,16 +26,6 @@ APIs/commands/configurations will be removed and here is a list of scheduled
breaking changes. Consider changing your code/scripts/configurations if you're breaking changes. Consider changing your code/scripts/configurations if you're
using anything mentioned here. using anything mentioned here.
## WSClient Notifications channel and SubscribeFor* APIs
Version 0.99.5 of NeoGo introduces a new set of subscription APIs that gives
more control to the WSClient user that can pass specific channels to be used
for specific subscriptions now. Old APIs and generic Notifications channel are
still available, but will be removed, so please convert your code to using new
Receive* APIs.
Removal of these APIs is scheduled for May 2023 (~0.103.0 release).
## SecondsPerBlock protocol configuration ## SecondsPerBlock protocol configuration
With 0.100.0 version SecondsPerBlock protocol configuration setting was With 0.100.0 version SecondsPerBlock protocol configuration setting was

View file

@ -27,8 +27,7 @@ type Internal struct {
func NewInternal(ctx context.Context, register InternalHook) (*Internal, error) { func NewInternal(ctx context.Context, register InternalHook) (*Internal, error) {
c := &Internal{ c := &Internal{
WSClient: WSClient{ WSClient: WSClient{
Client: Client{}, Client: Client{},
Notifications: make(chan Notification),
shutdown: make(chan struct{}), shutdown: make(chan struct{}),
done: make(chan struct{}), done: make(chan struct{}),
@ -67,7 +66,6 @@ eventloop:
} }
} }
close(c.done) close(c.done)
close(c.Notifications)
c.ctxCancel() c.ctxCancel()
// ctx is cancelled, server is notified and will finish soon. // ctx is cancelled, server is notified and will finish soon.
drainloop: drainloop:

View file

@ -16,7 +16,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/neorpc" "github.com/nspcc-dev/neo-go/pkg/neorpc"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/neorpc/rpcevent" "github.com/nspcc-dev/neo-go/pkg/neorpc/rpcevent"
"github.com/nspcc-dev/neo-go/pkg/util"
"go.uber.org/atomic" "go.uber.org/atomic"
) )
@ -56,20 +55,6 @@ import (
// CloseNotificationChannelIfFull option is on. // CloseNotificationChannelIfFull option is on.
type WSClient struct { type WSClient struct {
Client Client
// Notifications is a channel that is used to send events received from
// the server. Client's code is supposed to be reading from this channel if
// it wants to use subscription mechanism. Failing to do so will cause
// WSClient to block even regular requests. This channel is not buffered.
// In case of protocol error or upon connection closure, this channel will
// be closed, so make sure to handle this. Make sure you're not changing the
// received notifications, as it may affect the functionality of other
// notification receivers.
//
// Deprecated: please, use custom channels with ReceiveBlocks, ReceiveTransactions,
// ReceiveExecutionNotifications, ReceiveExecutions, ReceiveNotaryRequests
// methods to subscribe for notifications. This field will be removed in future
// versions.
Notifications chan Notification
ws *websocket.Conn ws *websocket.Conn
wsOpts WSOptions wsOpts WSOptions
@ -360,56 +345,6 @@ func (r *notaryRequestReceiver) Close() {
close(r.ch) close(r.ch)
} }
// naiveReceiver is a structure leaved for deprecated single channel based notifications
// delivering.
//
// Deprecated: this receiver must be removed after outdated subscriptions API removal.
type naiveReceiver struct {
eventID neorpc.EventID
filter any
ch chan<- Notification
}
// EventID implements neorpc.Comparator interface.
func (r *naiveReceiver) EventID() neorpc.EventID {
return r.eventID
}
// Filter implements neorpc.Comparator interface.
func (r *naiveReceiver) Filter() any {
return r.filter
}
// Receiver implements notificationReceiver interface.
func (r *naiveReceiver) Receiver() any {
return r.ch
}
// TrySend implements notificationReceiver interface.
func (r *naiveReceiver) TrySend(ntf Notification, nonBlocking bool) (bool, bool) {
if rpcevent.Matches(r, ntf) {
if nonBlocking {
select {
case r.ch <- ntf:
default:
return true, true
}
} else {
r.ch <- ntf
}
return true, false
}
return false, false
}
// Close implements notificationReceiver interface.
func (r *naiveReceiver) Close() {
r.ch <- Notification{
Type: neorpc.MissedEventID, // backwards-compatible behaviour
}
}
// Notification represents a server-generated notification for client subscriptions. // Notification represents a server-generated notification for client subscriptions.
// Value can be one of *block.Block, *state.AppExecResult, *state.ContainedNotificationEvent // Value can be one of *block.Block, *state.AppExecResult, *state.ContainedNotificationEvent
// *transaction.Transaction or *subscriptions.NotaryRequestEvent based on Type. // *transaction.Transaction or *subscriptions.NotaryRequestEvent based on Type.
@ -485,8 +420,7 @@ func NewWS(ctx context.Context, endpoint string, opts WSOptions) (*WSClient, err
return nil, err return nil, err
} }
wsc := &WSClient{ wsc := &WSClient{
Client: Client{}, Client: Client{},
Notifications: make(chan Notification),
ws: ws, ws: ws,
wsOpts: opts, wsOpts: opts,
@ -634,21 +568,14 @@ readloop:
} }
// dropSubCh closes corresponding subscriber's channel and removes it from the // dropSubCh closes corresponding subscriber's channel and removes it from the
// receivers map. If the channel belongs to a naive subscriber then it will be // receivers map. The channel is still being kept in
// closed manually without call to Close(). The channel is still being kept in
// the subscribers map as technically the server-side subscription still exists // the subscribers map as technically the server-side subscription still exists
// and the user is responsible for unsubscription. This method must be called // and the user is responsible for unsubscription. This method must be called
// under subscriptionsLock taken. It's the caller's duty to ensure dropSubCh // under subscriptionsLock taken. It's the caller's duty to ensure dropSubCh
// will be called once per channel, otherwise panic will occur. // will be called once per channel, otherwise panic will occur.
func (c *WSClient) dropSubCh(rcvrCh any, id string, ignoreCloseNotificationChannelIfFull bool) { func (c *WSClient) dropSubCh(rcvrCh any, id string, ignoreCloseNotificationChannelIfFull bool) {
if ignoreCloseNotificationChannelIfFull || c.wsOpts.CloseNotificationChannelIfFull { if ignoreCloseNotificationChannelIfFull || c.wsOpts.CloseNotificationChannelIfFull {
rcvr := c.subscriptions[id] c.subscriptions[id].Close()
_, ok := rcvr.(*naiveReceiver)
if ok { // naiveReceiver uses c.Notifications that should be handled separately.
close(c.Notifications)
} else {
c.subscriptions[id].Close()
}
delete(c.receivers, rcvrCh) delete(c.receivers, rcvrCh)
} }
} }
@ -778,29 +705,6 @@ func (c *WSClient) performSubscription(params []any, rcvr notificationReceiver)
return resp, nil return resp, nil
} }
// SubscribeForNewBlocks adds subscription for new block events to this instance
// of the client. It can be filtered by primary consensus node index, nil value doesn't
// add any filters.
//
// Deprecated: please, use ReceiveBlocks. This method will be removed in future versions.
func (c *WSClient) SubscribeForNewBlocks(primary *int) (string, error) {
var flt any
if primary != nil {
var f = neorpc.BlockFilter{Primary: primary}
flt = *f.Copy()
}
params := []any{"block_added"}
if flt != nil {
params = append(params, flt)
}
r := &naiveReceiver{
eventID: neorpc.BlockEventID,
filter: flt,
ch: c.Notifications,
}
return c.performSubscription(params, r)
}
// ReceiveBlocks registers provided channel as a receiver for the new block events. // ReceiveBlocks registers provided channel as a receiver for the new block events.
// Events can be filtered by the given BlockFilter, nil value doesn't add any filter. // Events can be filtered by the given BlockFilter, nil value doesn't add any filter.
// See WSClient comments for generic Receive* behaviour details. // See WSClient comments for generic Receive* behaviour details.
@ -820,29 +724,6 @@ func (c *WSClient) ReceiveBlocks(flt *neorpc.BlockFilter, rcvr chan<- *block.Blo
return c.performSubscription(params, r) return c.performSubscription(params, r)
} }
// SubscribeForNewTransactions adds subscription for new transaction events to
// this instance of the client. It can be filtered by the sender and/or the signer, nil
// value is treated as missing filter.
//
// Deprecated: please, use ReceiveTransactions. This method will be removed in future versions.
func (c *WSClient) SubscribeForNewTransactions(sender *util.Uint160, signer *util.Uint160) (string, error) {
var flt any
if sender != nil || signer != nil {
var f = neorpc.TxFilter{Sender: sender, Signer: signer}
flt = *f.Copy()
}
params := []any{"transaction_added"}
if flt != nil {
params = append(params, flt)
}
r := &naiveReceiver{
eventID: neorpc.TransactionEventID,
filter: flt,
ch: c.Notifications,
}
return c.performSubscription(params, r)
}
// ReceiveTransactions registers provided channel as a receiver for new transaction // ReceiveTransactions registers provided channel as a receiver for new transaction
// events. Events can be filtered by the given TxFilter, nil value doesn't add any // events. Events can be filtered by the given TxFilter, nil value doesn't add any
// filter. See WSClient comments for generic Receive* behaviour details. // filter. See WSClient comments for generic Receive* behaviour details.
@ -862,30 +743,6 @@ func (c *WSClient) ReceiveTransactions(flt *neorpc.TxFilter, rcvr chan<- *transa
return c.performSubscription(params, r) return c.performSubscription(params, r)
} }
// SubscribeForExecutionNotifications adds subscription for notifications
// generated during transaction execution to this instance of the client. It can be
// filtered by the contract's hash (that emits notifications), nil value puts no such
// restrictions.
//
// Deprecated: please, use ReceiveExecutionNotifications. This method will be removed in future versions.
func (c *WSClient) SubscribeForExecutionNotifications(contract *util.Uint160, name *string) (string, error) {
var flt any
if contract != nil || name != nil {
var f = neorpc.NotificationFilter{Contract: contract, Name: name}
flt = *f.Copy()
}
params := []any{"notification_from_execution"}
if flt != nil {
params = append(params, flt)
}
r := &naiveReceiver{
eventID: neorpc.NotificationEventID,
filter: flt,
ch: c.Notifications,
}
return c.performSubscription(params, r)
}
// ReceiveExecutionNotifications registers provided channel as a receiver for execution // ReceiveExecutionNotifications registers provided channel as a receiver for execution
// events. Events can be filtered by the given NotificationFilter, nil value doesn't add // events. Events can be filtered by the given NotificationFilter, nil value doesn't add
// any filter. See WSClient comments for generic Receive* behaviour details. // any filter. See WSClient comments for generic Receive* behaviour details.
@ -905,33 +762,6 @@ func (c *WSClient) ReceiveExecutionNotifications(flt *neorpc.NotificationFilter,
return c.performSubscription(params, r) return c.performSubscription(params, r)
} }
// SubscribeForTransactionExecutions adds subscription for application execution
// results generated during transaction execution to this instance of the client. It can
// be filtered by state (HALT/FAULT) to check for successful or failing
// transactions, nil value means no filtering.
//
// Deprecated: please, use ReceiveExecutions. This method will be removed in future versions.
func (c *WSClient) SubscribeForTransactionExecutions(state *string) (string, error) {
var flt any
if state != nil {
if *state != "HALT" && *state != "FAULT" {
return "", errors.New("bad state parameter")
}
var f = neorpc.ExecutionFilter{State: state}
flt = *f.Copy()
}
params := []any{"transaction_executed"}
if flt != nil {
params = append(params, flt)
}
r := &naiveReceiver{
eventID: neorpc.ExecutionEventID,
filter: flt,
ch: c.Notifications,
}
return c.performSubscription(params, r)
}
// ReceiveExecutions registers provided channel as a receiver for // ReceiveExecutions registers provided channel as a receiver for
// application execution result events generated during transaction execution. // application execution result events generated during transaction execution.
// Events can be filtered by the given ExecutionFilter, nil value doesn't add any filter. // Events can be filtered by the given ExecutionFilter, nil value doesn't add any filter.
@ -957,30 +787,6 @@ func (c *WSClient) ReceiveExecutions(flt *neorpc.ExecutionFilter, rcvr chan<- *s
return c.performSubscription(params, r) return c.performSubscription(params, r)
} }
// SubscribeForNotaryRequests adds subscription for notary request payloads
// addition or removal events to this instance of client. It can be filtered by
// request sender's hash, or main tx signer's hash, nil value puts no such
// restrictions.
//
// Deprecated: please, use ReceiveNotaryRequests. This method will be removed in future versions.
func (c *WSClient) SubscribeForNotaryRequests(sender *util.Uint160, mainSigner *util.Uint160) (string, error) {
var flt any
if sender != nil || mainSigner != nil {
var f = neorpc.TxFilter{Sender: sender, Signer: mainSigner}
flt = *f.Copy()
}
params := []any{"notary_request_event"}
if flt != nil {
params = append(params, flt)
}
r := &naiveReceiver{
eventID: neorpc.NotaryRequestEventID,
filter: flt,
ch: c.Notifications,
}
return c.performSubscription(params, r)
}
// ReceiveNotaryRequests registers provided channel as a receiver for notary request // ReceiveNotaryRequests registers provided channel as a receiver for notary request
// payload addition or removal events. Events can be filtered by the given TxFilter // payload addition or removal events. Events can be filtered by the given TxFilter
// where sender corresponds to notary request sender (the second fallback transaction // where sender corresponds to notary request sender (the second fallback transaction

View file

@ -205,31 +205,23 @@ func TestWSClientEvents(t *testing.T) {
wsc.receivers[chan<- *state.AppExecResult(aerCh3)] = []string{"6"} wsc.receivers[chan<- *state.AppExecResult(aerCh3)] = []string{"6"}
// MissedEvent must close the channels above. // MissedEvent must close the channels above.
wsc.subscriptions["7"] = &naiveReceiver{eventID: neorpc.BlockEventID, ch: wsc.Notifications}
wsc.subscriptions["8"] = &naiveReceiver{eventID: neorpc.BlockEventID, ch: wsc.Notifications} // check duplicating subscriptions
wsc.subscriptions["9"] = &naiveReceiver{eventID: neorpc.ExecutionEventID, ch: wsc.Notifications} // check different events
wsc.receivers[wsc.Notifications] = []string{"7", "8", "9"}
wsc.subscriptionsLock.Unlock() wsc.subscriptionsLock.Unlock()
var ( var (
b1Cnt, b2Cnt int b1Cnt, b2Cnt int
aer1Cnt, aer2Cnt, aer3Cnt int aer1Cnt, aer2Cnt, aer3Cnt int
ntfCnt int ntfCnt int
defaultCount int expectedb1Cnt, expectedb2Cnt = 1, 1 // single Block event
expectedb1Cnt, expectedb2Cnt = 1, 1 // single Block event expectedaer1Cnt, expectedaer2Cnt, expectedaer3Cnt = 2, 2, 0 // two HALTED AERs
expectedaer1Cnt, expectedaer2Cnt, expectedaer3Cnt = 2, 2, 0 // two HALTED AERs expectedntfCnt = 1 // single notification event
expectedntfCnt = 1 // single notification event
expectedDefaultCnt = 1 + 2 + 1 // single Block event + two AERs + missed event
aer *state.AppExecResult aer *state.AppExecResult
) )
for b1Cnt+b2Cnt+ for b1Cnt+b2Cnt+
aer1Cnt+aer2Cnt+aer3Cnt+ aer1Cnt+aer2Cnt+aer3Cnt+
ntfCnt+ ntfCnt !=
defaultCount !=
expectedb1Cnt+expectedb2Cnt+ expectedb1Cnt+expectedb2Cnt+
expectedaer1Cnt+expectedaer2Cnt+expectedaer3Cnt+ expectedaer1Cnt+expectedaer2Cnt+expectedaer3Cnt+
expectedntfCnt+ expectedntfCnt {
expectedDefaultCnt {
select { select {
case _, ok = <-bCh1: case _, ok = <-bCh1:
if ok { if ok {
@ -256,10 +248,6 @@ func TestWSClientEvents(t *testing.T) {
if ok { if ok {
ntfCnt++ ntfCnt++
} }
case _, ok = <-wsc.Notifications:
if ok {
defaultCount++
}
case <-time.After(time.Second): case <-time.After(time.Second):
t.Fatal("timeout waiting for event") t.Fatal("timeout waiting for event")
} }
@ -270,7 +258,6 @@ func TestWSClientEvents(t *testing.T) {
assert.Equal(t, expectedaer2Cnt, aer2Cnt) assert.Equal(t, expectedaer2Cnt, aer2Cnt)
assert.Equal(t, expectedaer3Cnt, aer3Cnt) assert.Equal(t, expectedaer3Cnt, aer3Cnt)
assert.Equal(t, expectedntfCnt, ntfCnt) assert.Equal(t, expectedntfCnt, ntfCnt)
assert.Equal(t, expectedDefaultCnt, defaultCount)
// Channels must be closed by server // Channels must be closed by server
_, ok = <-bCh1 _, ok = <-bCh1

View file

@ -2066,89 +2066,6 @@ func TestWSClient_SubscriptionsCompat(t *testing.T) {
bCount++ bCount++
return b1, primary, sender, ntfName, st return b1, primary, sender, ntfName, st
} }
checkDeprecated := func(t *testing.T, filtered bool) {
var (
bID, txID, ntfID, aerID string
err error
)
b, primary, sender, ntfName, st := getData(t)
if filtered {
bID, err = c.SubscribeForNewBlocks(&primary) //nolint:staticcheck // SA1019: c.SubscribeForNewBlocks is deprecated
require.NoError(t, err)
txID, err = c.SubscribeForNewTransactions(&sender, nil) //nolint:staticcheck // SA1019: c.SubscribeForNewTransactions is deprecated
require.NoError(t, err)
ntfID, err = c.SubscribeForExecutionNotifications(nil, &ntfName) //nolint:staticcheck // SA1019: c.SubscribeForExecutionNotifications is deprecated
require.NoError(t, err)
aerID, err = c.SubscribeForTransactionExecutions(&st) //nolint:staticcheck // SA1019: c.SubscribeForTransactionExecutions is deprecated
require.NoError(t, err)
} else {
bID, err = c.SubscribeForNewBlocks(nil) //nolint:staticcheck // SA1019: c.SubscribeForNewBlocks is deprecated
require.NoError(t, err)
txID, err = c.SubscribeForNewTransactions(nil, nil) //nolint:staticcheck // SA1019: c.SubscribeForNewTransactions is deprecated
require.NoError(t, err)
ntfID, err = c.SubscribeForExecutionNotifications(nil, nil) //nolint:staticcheck // SA1019: c.SubscribeForExecutionNotifications is deprecated
require.NoError(t, err)
aerID, err = c.SubscribeForTransactionExecutions(nil) //nolint:staticcheck // SA1019: c.SubscribeForTransactionExecutions is deprecated
require.NoError(t, err)
}
var (
lock sync.RWMutex
received byte
exitCh = make(chan struct{})
)
go func() {
dispatcher:
for {
select {
case ntf := <-c.Notifications: //nolint:staticcheck // SA1019: c.Notifications is deprecated
lock.Lock()
switch ntf.Type {
case neorpc.BlockEventID:
received |= 1
case neorpc.TransactionEventID:
received |= 1 << 1
case neorpc.NotificationEventID:
received |= 1 << 2
case neorpc.ExecutionEventID:
received |= 1 << 3
}
lock.Unlock()
case <-exitCh:
break dispatcher
}
}
drainLoop:
for {
select {
case <-c.Notifications: //nolint:staticcheck // SA1019: c.Notifications is deprecated
default:
break drainLoop
}
}
}()
// Accept the next block and wait for events.
require.NoError(t, chain.AddBlock(b))
assert.Eventually(t, func() bool {
lock.RLock()
defer lock.RUnlock()
return received == 1<<4-1
}, time.Second, 100*time.Millisecond)
require.NoError(t, c.Unsubscribe(bID))
require.NoError(t, c.Unsubscribe(txID))
require.NoError(t, c.Unsubscribe(ntfID))
require.NoError(t, c.Unsubscribe(aerID))
exitCh <- struct{}{}
}
t.Run("deprecated, filtered", func(t *testing.T) {
checkDeprecated(t, true)
})
t.Run("deprecated, non-filtered", func(t *testing.T) {
checkDeprecated(t, false)
})
checkRelevant := func(t *testing.T, filtered bool) { checkRelevant := func(t *testing.T, filtered bool) {
b, primary, sender, ntfName, st := getData(t) b, primary, sender, ntfName, st := getData(t)