Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rpc: add an ability to filter out NotaryRequestEvents #3236

Merged
merged 1 commit into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/notifications.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ Recognized stream names:
* `notary_request_event`
Filter: `sender` field containing a string with hex-encoded Uint160 (LE
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
cancel this subscription and has no meaning other than that.
Expand Down
30 changes: 30 additions & 0 deletions pkg/neorpc/filters.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package neorpc

import (
"github.com/nspcc-dev/neo-go/pkg/core/mempoolevent"
"github.com/nspcc-dev/neo-go/pkg/util"
)

Expand Down Expand Up @@ -38,6 +39,14 @@ type (
State *string `json:"state,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.
Expand Down Expand Up @@ -111,3 +120,24 @@ func (f *ExecutionFilter) Copy() *ExecutionFilter {
}
return res
}

// Copy creates a deep copy of the NotaryRequestFilter. It handles nil NotaryRequestFilter correctly.
func (f *NotaryRequestFilter) Copy() *NotaryRequestFilter {
AnnaShaleva marked this conversation as resolved.
Show resolved Hide resolved
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
}
5 changes: 3 additions & 2 deletions pkg/neorpc/rpcevent/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,9 @@ func Matches(f Comparator, r Container) bool {
containerOK := filt.Container == nil || applog.Container.Equals(*filt.Container)
return stateOK && containerOK
case neorpc.NotaryRequestEventID:
filt := filter.(neorpc.TxFilter)
filt := filter.(neorpc.NotaryRequestFilter)
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
signerOK := true
if filt.Signer != nil {
Expand All @@ -82,7 +83,7 @@ func Matches(f Comparator, r Container) bool {
}
}
}
return senderOk && signerOK
return senderOk && signerOK && typeOk
}
return false
}
19 changes: 16 additions & 3 deletions pkg/neorpc/rpcevent/filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"

"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/transaction"
"github.com/nspcc-dev/neo-go/pkg/neorpc"
Expand Down Expand Up @@ -47,11 +48,13 @@ func TestMatches(t *testing.T) {
sender := util.Uint160{1, 2, 3}
signer := util.Uint160{4, 5, 6}
contract := util.Uint160{7, 8, 9}
notaryType := mempoolevent.TransactionAdded
badUint160 := util.Uint160{9, 9, 9}
cnt := util.Uint256{1, 2, 3}
badUint256 := util.Uint256{9, 9, 9}
name := "ntf name"
badName := "bad name"
badType := mempoolevent.TransactionRemoved
bContainer := testContainer{
id: neorpc.BlockEventID,
pld: &block.Block{
Expand All @@ -76,6 +79,7 @@ func TestMatches(t *testing.T) {
ntrContainer := testContainer{
id: neorpc.NotaryRequestEventID,
pld: &result.NotaryRequestEvent{
Type: notaryType,
NotaryRequest: &payload.P2PNotaryRequest{
MainTransaction: &transaction.Transaction{Signers: []transaction.Signer{{Account: signer}}},
FallbackTransaction: &transaction.Transaction{Signers: []transaction.Signer{{Account: util.Uint160{}}, {Account: sender}}},
Expand Down Expand Up @@ -254,7 +258,7 @@ func TestMatches(t *testing.T) {
name: "notary request, sender mismatch",
comparator: testComparator{
id: neorpc.NotaryRequestEventID,
filter: neorpc.TxFilter{Sender: &badUint160},
filter: neorpc.NotaryRequestFilter{Sender: &badUint160},
},
container: ntrContainer,
expected: false,
Expand All @@ -263,7 +267,16 @@ func TestMatches(t *testing.T) {
name: "notary request, signer mismatch",
comparator: testComparator{
id: neorpc.NotaryRequestEventID,
filter: neorpc.TxFilter{Signer: &badUint160},
filter: neorpc.NotaryRequestFilter{Signer: &badUint160},
AnnaShaleva marked this conversation as resolved.
Show resolved Hide resolved
},
container: ntrContainer,
expected: false,
},
{
name: "notary request, type mismatch",
comparator: testComparator{
id: neorpc.NotaryRequestEventID,
filter: neorpc.NotaryRequestFilter{Type: &badType},
},
container: ntrContainer,
expected: false,
Expand All @@ -272,7 +285,7 @@ func TestMatches(t *testing.T) {
name: "notary request, filter match",
comparator: testComparator{
id: neorpc.NotaryRequestEventID,
filter: neorpc.TxFilter{Sender: &sender, Signer: &signer},
filter: neorpc.NotaryRequestFilter{Sender: &sender, Signer: &signer, Type: &notaryType},
},
container: ntrContainer,
expected: true,
Expand Down
12 changes: 7 additions & 5 deletions pkg/rpcclient/wsclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ func (r *executionReceiver) Close() {

// notaryRequestReceiver stores information about notary requests subscriber.
type notaryRequestReceiver struct {
filter *neorpc.TxFilter
filter *neorpc.NotaryRequestFilter
ch chan<- *result.NotaryRequestEvent
}

Expand Down Expand Up @@ -811,11 +811,13 @@ func (c *WSClient) ReceiveExecutions(flt *neorpc.ExecutionFilter, rcvr chan<- *s
}

// 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
// signer) and signer corresponds to main transaction signers. nil value doesn't add
// any filter. See WSClient comments for generic Receive* behaviour details.
func (c *WSClient) ReceiveNotaryRequests(flt *neorpc.TxFilter, rcvr chan<- *result.NotaryRequestEvent) (string, error) {
// signer), signer corresponds to main transaction signers and type corresponds to the
// [mempoolevent.Type] and denotes whether notary request was added to or removed from
// 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) {
AnnaShaleva marked this conversation as resolved.
Show resolved Hide resolved
AnnaShaleva marked this conversation as resolved.
Show resolved Hide resolved
if rcvr == nil {
return "", ErrNilNotificationReceiver
}
Expand Down
66 changes: 66 additions & 0 deletions pkg/rpcclient/wsclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/gorilla/websocket"
"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/mempoolevent"
"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/neorpc"
Expand Down Expand Up @@ -574,6 +575,71 @@ func TestWSFilteredSubscriptions(t *testing.T) {
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)
AnnaShaleva marked this conversation as resolved.
Show resolved Hide resolved
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)
AnnaShaleva marked this conversation as resolved.
Show resolved Hide resolved
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 {
t.Run(c.name, func(t *testing.T) {
Expand Down
6 changes: 5 additions & 1 deletion pkg/services/rpcsrv/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2734,10 +2734,14 @@ func (s *Server) subscribe(reqParams params.Params, sub *subscriber) (any, *neor
flt := new(neorpc.BlockFilter)
err = jd.Decode(flt)
filter = *flt
case neorpc.TransactionEventID, neorpc.NotaryRequestEventID:
case neorpc.TransactionEventID:
flt := new(neorpc.TxFilter)
err = jd.Decode(flt)
filter = *flt
case neorpc.NotaryRequestEventID:
flt := new(neorpc.NotaryRequestFilter)
err = jd.Decode(flt)
filter = *flt
case neorpc.NotificationEventID:
flt := new(neorpc.NotificationFilter)
err = jd.Decode(flt)
Expand Down
12 changes: 10 additions & 2 deletions pkg/services/rpcsrv/subscription_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,8 +354,16 @@ func TestFilteredNotaryRequestSubscriptions(t *testing.T) {
require.Equal(t, "0x"+goodSender.StringLE(), signer0acc)
},
},
"matching sender and signer": {
params: `["notary_request_event", {"sender":"` + goodSender.StringLE() + `", "signer":"` + goodSender.StringLE() + `"}]`,
"matching type": {
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) {
rmap := resp.Payload[0].(map[string]any)
require.Equal(t, neorpc.NotaryRequestEventID, resp.Event)
Expand Down
Loading