rpc: add an ability to filter out NotaryRequestEvents
Add new filter NotaryRequestFilter, support for filtering NotaryRequestEvents by mempoolevent.Type (added or removed). Closes #2425. Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
This commit is contained in:
parent
afca64f164
commit
3fd48a743e
8 changed files with 139 additions and 14 deletions
|
@ -84,7 +84,8 @@ Recognized stream names:
|
||||||
* `notary_request_event`
|
* `notary_request_event`
|
||||||
Filter: `sender` field containing a string with hex-encoded Uint160 (LE
|
Filter: `sender` field containing a string with hex-encoded Uint160 (LE
|
||||||
representation) for notary request's `Sender` and/or `signer` in the same
|
representation) for notary request's `Sender` and/or `signer` in the same
|
||||||
format for one of main transaction's `Signers`.
|
format for one of main transaction's `Signers`. `type` field containing a
|
||||||
|
string with event type, which could be one of "added" or "removed".
|
||||||
|
|
||||||
Response: returns subscription ID (string) as a result. This ID can be used to
|
Response: returns subscription ID (string) as a result. This ID can be used to
|
||||||
cancel this subscription and has no meaning other than that.
|
cancel this subscription and has no meaning other than that.
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package neorpc
|
package neorpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/mempoolevent"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -38,6 +39,14 @@ type (
|
||||||
State *string `json:"state,omitempty"`
|
State *string `json:"state,omitempty"`
|
||||||
Container *util.Uint256 `json:"container,omitempty"`
|
Container *util.Uint256 `json:"container,omitempty"`
|
||||||
}
|
}
|
||||||
|
// NotaryRequestFilter is a wrapper structure used for notary request events.
|
||||||
|
// It allows to choose notary request events with the specified request sender,
|
||||||
|
// main transaction signer and/or type. nil value treated as missing filter.
|
||||||
|
NotaryRequestFilter struct {
|
||||||
|
Sender *util.Uint160 `json:"sender,omitempty"`
|
||||||
|
Signer *util.Uint160 `json:"signer,omitempty"`
|
||||||
|
Type *mempoolevent.Type `json:"type,omitempty"`
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Copy creates a deep copy of the BlockFilter. It handles nil BlockFilter correctly.
|
// Copy creates a deep copy of the BlockFilter. It handles nil BlockFilter correctly.
|
||||||
|
@ -111,3 +120,24 @@ func (f *ExecutionFilter) Copy() *ExecutionFilter {
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy creates a deep copy of the NotaryRequestFilter. It handles nil NotaryRequestFilter correctly.
|
||||||
|
func (f *NotaryRequestFilter) Copy() *NotaryRequestFilter {
|
||||||
|
if f == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var res = new(NotaryRequestFilter)
|
||||||
|
if f.Sender != nil {
|
||||||
|
res.Sender = new(util.Uint160)
|
||||||
|
*res.Sender = *f.Sender
|
||||||
|
}
|
||||||
|
if f.Signer != nil {
|
||||||
|
res.Signer = new(util.Uint160)
|
||||||
|
*res.Signer = *f.Signer
|
||||||
|
}
|
||||||
|
if f.Type != nil {
|
||||||
|
res.Type = new(mempoolevent.Type)
|
||||||
|
*res.Type = *f.Type
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
|
@ -69,8 +69,9 @@ func Matches(f Comparator, r Container) bool {
|
||||||
containerOK := filt.Container == nil || applog.Container.Equals(*filt.Container)
|
containerOK := filt.Container == nil || applog.Container.Equals(*filt.Container)
|
||||||
return stateOK && containerOK
|
return stateOK && containerOK
|
||||||
case neorpc.NotaryRequestEventID:
|
case neorpc.NotaryRequestEventID:
|
||||||
filt := filter.(neorpc.TxFilter)
|
filt := filter.(neorpc.NotaryRequestFilter)
|
||||||
req := r.EventPayload().(*result.NotaryRequestEvent)
|
req := r.EventPayload().(*result.NotaryRequestEvent)
|
||||||
|
typeOk := filt.Type == nil || req.Type == *filt.Type
|
||||||
senderOk := filt.Sender == nil || req.NotaryRequest.FallbackTransaction.Signers[1].Account == *filt.Sender
|
senderOk := filt.Sender == nil || req.NotaryRequest.FallbackTransaction.Signers[1].Account == *filt.Sender
|
||||||
signerOK := true
|
signerOK := true
|
||||||
if filt.Signer != nil {
|
if filt.Signer != nil {
|
||||||
|
@ -82,7 +83,7 @@ func Matches(f Comparator, r Container) bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return senderOk && signerOK
|
return senderOk && signerOK && typeOk
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/mempoolevent"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/neorpc"
|
"github.com/nspcc-dev/neo-go/pkg/neorpc"
|
||||||
|
@ -47,11 +48,13 @@ func TestMatches(t *testing.T) {
|
||||||
sender := util.Uint160{1, 2, 3}
|
sender := util.Uint160{1, 2, 3}
|
||||||
signer := util.Uint160{4, 5, 6}
|
signer := util.Uint160{4, 5, 6}
|
||||||
contract := util.Uint160{7, 8, 9}
|
contract := util.Uint160{7, 8, 9}
|
||||||
|
notaryType := mempoolevent.TransactionAdded
|
||||||
badUint160 := util.Uint160{9, 9, 9}
|
badUint160 := util.Uint160{9, 9, 9}
|
||||||
cnt := util.Uint256{1, 2, 3}
|
cnt := util.Uint256{1, 2, 3}
|
||||||
badUint256 := util.Uint256{9, 9, 9}
|
badUint256 := util.Uint256{9, 9, 9}
|
||||||
name := "ntf name"
|
name := "ntf name"
|
||||||
badName := "bad name"
|
badName := "bad name"
|
||||||
|
badType := mempoolevent.TransactionRemoved
|
||||||
bContainer := testContainer{
|
bContainer := testContainer{
|
||||||
id: neorpc.BlockEventID,
|
id: neorpc.BlockEventID,
|
||||||
pld: &block.Block{
|
pld: &block.Block{
|
||||||
|
@ -76,6 +79,7 @@ func TestMatches(t *testing.T) {
|
||||||
ntrContainer := testContainer{
|
ntrContainer := testContainer{
|
||||||
id: neorpc.NotaryRequestEventID,
|
id: neorpc.NotaryRequestEventID,
|
||||||
pld: &result.NotaryRequestEvent{
|
pld: &result.NotaryRequestEvent{
|
||||||
|
Type: notaryType,
|
||||||
NotaryRequest: &payload.P2PNotaryRequest{
|
NotaryRequest: &payload.P2PNotaryRequest{
|
||||||
MainTransaction: &transaction.Transaction{Signers: []transaction.Signer{{Account: signer}}},
|
MainTransaction: &transaction.Transaction{Signers: []transaction.Signer{{Account: signer}}},
|
||||||
FallbackTransaction: &transaction.Transaction{Signers: []transaction.Signer{{Account: util.Uint160{}}, {Account: sender}}},
|
FallbackTransaction: &transaction.Transaction{Signers: []transaction.Signer{{Account: util.Uint160{}}, {Account: sender}}},
|
||||||
|
@ -254,7 +258,7 @@ func TestMatches(t *testing.T) {
|
||||||
name: "notary request, sender mismatch",
|
name: "notary request, sender mismatch",
|
||||||
comparator: testComparator{
|
comparator: testComparator{
|
||||||
id: neorpc.NotaryRequestEventID,
|
id: neorpc.NotaryRequestEventID,
|
||||||
filter: neorpc.TxFilter{Sender: &badUint160},
|
filter: neorpc.NotaryRequestFilter{Sender: &badUint160},
|
||||||
},
|
},
|
||||||
container: ntrContainer,
|
container: ntrContainer,
|
||||||
expected: false,
|
expected: false,
|
||||||
|
@ -263,7 +267,16 @@ func TestMatches(t *testing.T) {
|
||||||
name: "notary request, signer mismatch",
|
name: "notary request, signer mismatch",
|
||||||
comparator: testComparator{
|
comparator: testComparator{
|
||||||
id: neorpc.NotaryRequestEventID,
|
id: neorpc.NotaryRequestEventID,
|
||||||
filter: neorpc.TxFilter{Signer: &badUint160},
|
filter: neorpc.NotaryRequestFilter{Signer: &badUint160},
|
||||||
|
},
|
||||||
|
container: ntrContainer,
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "notary request, type mismatch",
|
||||||
|
comparator: testComparator{
|
||||||
|
id: neorpc.NotaryRequestEventID,
|
||||||
|
filter: neorpc.NotaryRequestFilter{Type: &badType},
|
||||||
},
|
},
|
||||||
container: ntrContainer,
|
container: ntrContainer,
|
||||||
expected: false,
|
expected: false,
|
||||||
|
@ -272,7 +285,7 @@ func TestMatches(t *testing.T) {
|
||||||
name: "notary request, filter match",
|
name: "notary request, filter match",
|
||||||
comparator: testComparator{
|
comparator: testComparator{
|
||||||
id: neorpc.NotaryRequestEventID,
|
id: neorpc.NotaryRequestEventID,
|
||||||
filter: neorpc.TxFilter{Sender: &sender, Signer: &signer},
|
filter: neorpc.NotaryRequestFilter{Sender: &sender, Signer: &signer, Type: ¬aryType},
|
||||||
},
|
},
|
||||||
container: ntrContainer,
|
container: ntrContainer,
|
||||||
expected: true,
|
expected: true,
|
||||||
|
|
|
@ -305,7 +305,7 @@ func (r *executionReceiver) Close() {
|
||||||
|
|
||||||
// notaryRequestReceiver stores information about notary requests subscriber.
|
// notaryRequestReceiver stores information about notary requests subscriber.
|
||||||
type notaryRequestReceiver struct {
|
type notaryRequestReceiver struct {
|
||||||
filter *neorpc.TxFilter
|
filter *neorpc.NotaryRequestFilter
|
||||||
ch chan<- *result.NotaryRequestEvent
|
ch chan<- *result.NotaryRequestEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -811,11 +811,13 @@ func (c *WSClient) ReceiveExecutions(flt *neorpc.ExecutionFilter, rcvr chan<- *s
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 NotaryRequestFilter
|
||||||
// where sender corresponds to notary request sender (the second fallback transaction
|
// where sender corresponds to notary request sender (the second fallback transaction
|
||||||
// signer) and signer corresponds to main transaction signers. nil value doesn't add
|
// signer), signer corresponds to main transaction signers and type corresponds to the
|
||||||
// any filter. See WSClient comments for generic Receive* behaviour details.
|
// [mempoolevent.Type] and denotes whether notary request was added to or removed from
|
||||||
func (c *WSClient) ReceiveNotaryRequests(flt *neorpc.TxFilter, rcvr chan<- *result.NotaryRequestEvent) (string, error) {
|
// the notary request pool. nil value doesn't add any filter. See WSClient comments
|
||||||
|
// for generic Receive* behaviour details.
|
||||||
|
func (c *WSClient) ReceiveNotaryRequests(flt *neorpc.NotaryRequestFilter, rcvr chan<- *result.NotaryRequestEvent) (string, error) {
|
||||||
if rcvr == nil {
|
if rcvr == nil {
|
||||||
return "", ErrNilNotificationReceiver
|
return "", ErrNilNotificationReceiver
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/mempoolevent"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/neorpc"
|
"github.com/nspcc-dev/neo-go/pkg/neorpc"
|
||||||
|
@ -574,6 +575,71 @@ func TestWSFilteredSubscriptions(t *testing.T) {
|
||||||
require.Equal(t, util.Uint256{1, 2, 3}, *filt.Container)
|
require.Equal(t, util.Uint256{1, 2, 3}, *filt.Container)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"notary request sender",
|
||||||
|
func(t *testing.T, wsc *WSClient) {
|
||||||
|
sender := util.Uint160{1, 2, 3, 4, 5}
|
||||||
|
_, err := wsc.ReceiveNotaryRequests(&neorpc.NotaryRequestFilter{Sender: &sender}, make(chan *result.NotaryRequestEvent))
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
|
func(t *testing.T, p *params.Params) {
|
||||||
|
param := p.Value(1)
|
||||||
|
filt := new(neorpc.NotaryRequestFilter)
|
||||||
|
require.NoError(t, json.Unmarshal(param.RawMessage, filt))
|
||||||
|
require.Equal(t, util.Uint160{1, 2, 3, 4, 5}, *filt.Sender)
|
||||||
|
require.Nil(t, filt.Signer)
|
||||||
|
require.Nil(t, filt.Type)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notary request signer",
|
||||||
|
func(t *testing.T, wsc *WSClient) {
|
||||||
|
signer := util.Uint160{0, 42}
|
||||||
|
_, err := wsc.ReceiveNotaryRequests(&neorpc.NotaryRequestFilter{Signer: &signer}, make(chan *result.NotaryRequestEvent))
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
|
func(t *testing.T, p *params.Params) {
|
||||||
|
param := p.Value(1)
|
||||||
|
filt := new(neorpc.NotaryRequestFilter)
|
||||||
|
require.NoError(t, json.Unmarshal(param.RawMessage, filt))
|
||||||
|
require.Nil(t, filt.Sender)
|
||||||
|
require.Equal(t, util.Uint160{0, 42}, *filt.Signer)
|
||||||
|
require.Nil(t, filt.Type)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notary request type",
|
||||||
|
func(t *testing.T, wsc *WSClient) {
|
||||||
|
mempoolType := mempoolevent.TransactionAdded
|
||||||
|
_, err := wsc.ReceiveNotaryRequests(&neorpc.NotaryRequestFilter{Type: &mempoolType}, make(chan *result.NotaryRequestEvent))
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
|
func(t *testing.T, p *params.Params) {
|
||||||
|
param := p.Value(1)
|
||||||
|
filt := new(neorpc.NotaryRequestFilter)
|
||||||
|
require.NoError(t, json.Unmarshal(param.RawMessage, filt))
|
||||||
|
require.Equal(t, mempoolevent.TransactionAdded, *filt.Type)
|
||||||
|
require.Nil(t, filt.Sender)
|
||||||
|
require.Nil(t, filt.Signer)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{"notary request sender, signer and type",
|
||||||
|
func(t *testing.T, wsc *WSClient) {
|
||||||
|
sender := util.Uint160{1, 2, 3, 4, 5}
|
||||||
|
signer := util.Uint160{0, 42}
|
||||||
|
mempoolType := mempoolevent.TransactionAdded
|
||||||
|
_, err := wsc.ReceiveNotaryRequests(&neorpc.NotaryRequestFilter{Type: &mempoolType, Signer: &signer, Sender: &sender}, make(chan *result.NotaryRequestEvent))
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
|
func(t *testing.T, p *params.Params) {
|
||||||
|
param := p.Value(1)
|
||||||
|
filt := new(neorpc.NotaryRequestFilter)
|
||||||
|
require.NoError(t, json.Unmarshal(param.RawMessage, filt))
|
||||||
|
require.Equal(t, util.Uint160{1, 2, 3, 4, 5}, *filt.Sender)
|
||||||
|
require.Equal(t, util.Uint160{0, 42}, *filt.Signer)
|
||||||
|
require.Equal(t, mempoolevent.TransactionAdded, *filt.Type)
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
t.Run(c.name, func(t *testing.T) {
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
|
|
@ -2734,10 +2734,14 @@ func (s *Server) subscribe(reqParams params.Params, sub *subscriber) (any, *neor
|
||||||
flt := new(neorpc.BlockFilter)
|
flt := new(neorpc.BlockFilter)
|
||||||
err = jd.Decode(flt)
|
err = jd.Decode(flt)
|
||||||
filter = *flt
|
filter = *flt
|
||||||
case neorpc.TransactionEventID, neorpc.NotaryRequestEventID:
|
case neorpc.TransactionEventID:
|
||||||
flt := new(neorpc.TxFilter)
|
flt := new(neorpc.TxFilter)
|
||||||
err = jd.Decode(flt)
|
err = jd.Decode(flt)
|
||||||
filter = *flt
|
filter = *flt
|
||||||
|
case neorpc.NotaryRequestEventID:
|
||||||
|
flt := new(neorpc.NotaryRequestFilter)
|
||||||
|
err = jd.Decode(flt)
|
||||||
|
filter = *flt
|
||||||
case neorpc.NotificationEventID:
|
case neorpc.NotificationEventID:
|
||||||
flt := new(neorpc.NotificationFilter)
|
flt := new(neorpc.NotificationFilter)
|
||||||
err = jd.Decode(flt)
|
err = jd.Decode(flt)
|
||||||
|
|
|
@ -354,8 +354,16 @@ func TestFilteredNotaryRequestSubscriptions(t *testing.T) {
|
||||||
require.Equal(t, "0x"+goodSender.StringLE(), signer0acc)
|
require.Equal(t, "0x"+goodSender.StringLE(), signer0acc)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"matching sender and signer": {
|
"matching type": {
|
||||||
params: `["notary_request_event", {"sender":"` + goodSender.StringLE() + `", "signer":"` + goodSender.StringLE() + `"}]`,
|
params: `["notary_request_event", {"type":"added"}]`,
|
||||||
|
check: func(t *testing.T, resp *neorpc.Notification) {
|
||||||
|
require.Equal(t, neorpc.NotaryRequestEventID, resp.Event)
|
||||||
|
rmap := resp.Payload[0].(map[string]any)
|
||||||
|
require.Equal(t, "added", rmap["type"].(string))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"matching sender, signer and type": {
|
||||||
|
params: `["notary_request_event", {"sender":"` + goodSender.StringLE() + `", "signer":"` + goodSender.StringLE() + `","type":"added"}]`,
|
||||||
check: func(t *testing.T, resp *neorpc.Notification) {
|
check: func(t *testing.T, resp *neorpc.Notification) {
|
||||||
rmap := resp.Payload[0].(map[string]any)
|
rmap := resp.Payload[0].(map[string]any)
|
||||||
require.Equal(t, neorpc.NotaryRequestEventID, resp.Event)
|
require.Equal(t, neorpc.NotaryRequestEventID, resp.Event)
|
||||||
|
|
Loading…
Reference in a new issue