From 0ba16fe5802e5a079a9c3dc66e485d08598227c3 Mon Sep 17 00:00:00 2001 From: Pavel Karpy Date: Wed, 20 Nov 2024 20:31:15 +0300 Subject: [PATCH 1/2] ws: allow filtering notification by parameters `Any` type with nil/null value is treated as a parameter filter that allows any notification value. Not more than 16 filter parameters are allowed. Closes #3624. Signed-off-by: Pavel Karpy --- docs/notifications.md | 13 +++- pkg/neorpc/filters.go | 92 ++++++++++++++++++++++-- pkg/neorpc/filters_test.go | 10 +++ pkg/neorpc/rpcevent/filter.go | 23 +++++- pkg/neorpc/rpcevent/filter_test.go | 44 ++++++++++++ pkg/rpcclient/wsclient_test.go | 24 +++++++ pkg/services/rpcsrv/subscription_test.go | 37 ++++++++++ 7 files changed, 234 insertions(+), 9 deletions(-) diff --git a/docs/notifications.md b/docs/notifications.md index 68889780bc..4f3187965d 100644 --- a/docs/notifications.md +++ b/docs/notifications.md @@ -16,7 +16,8 @@ Currently supported events: Contents: transaction. Filters: sender and signer. * notification generated during execution - Contents: container hash, contract hash, notification name, stack item. Filters: contract hash, notification name. + Contents: container hash, contract hash, notification name, stack item. + Filters: contract hash, notification name, notification parameters. * transaction/persisting script executed Contents: application execution result. Filters: VM state, script container hash. @@ -84,9 +85,15 @@ Recognized stream names: format for one of transaction's `Signers`. * `notification_from_execution` Filter: `contract` field containing a string with hex-encoded Uint160 (LE - representation) and/or `name` field containing a string with execution + representation), `name` field containing a string with execution notification name which should be a valid UTF-8 string not longer than - 32 bytes. + 32 bytes and/or `parameters` field containing an ordered array of structs + with `type` and `value` fields. Not more than 16 parameters are accepted. + Parameter's `type` must be not-a-complex type from the list: `Any`, + `Boolean`, `Integer`, `ByteArray`, `String`, `Hash160`, `Hash256`, `PublicKey` + or `Signature`. Filter that allows any parameter must be omitted or must + be `Any` typed with zero value. It is prohibited to have `parameters` be + filled with `Any` types only. * `transaction_executed` Filter: `state` field containing `HALT` or `FAULT` string for successful and failed executions respectively and/or `container` field containing diff --git a/pkg/neorpc/filters.go b/pkg/neorpc/filters.go index f05cf93431..88bebdc34b 100644 --- a/pkg/neorpc/filters.go +++ b/pkg/neorpc/filters.go @@ -3,13 +3,21 @@ package neorpc import ( "errors" "fmt" + "slices" "github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/core/mempoolevent" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/vmstate" ) +// MaxNotificationFilterParametersCount is a reasonable filter's parameter limit +// that does not allow attackers to increase node resources usage but that +// also should be enough for real applications. +const MaxNotificationFilterParametersCount = 16 + type ( // BlockFilter is a wrapper structure for the block event filter. It allows // to filter blocks by primary index and/or by block index (allowing blocks @@ -29,11 +37,27 @@ type ( } // NotificationFilter is a wrapper structure representing a filter used for // notifications generated during transaction execution. Notifications can - // be filtered by contract hash and/or by name. nil value treated as missing - // filter. + // be filtered by contract hash, by event name and/or by notification + // parameters. Notification parameter filters will be applied in the order + // corresponding to a produced notification's parameters. Not more than + // [MaxNotificationFilterParametersCount] parameters are accepted (see also + // [NotificationFilter.IsValid]). `Any`-typed parameter with zero value + // allows any notification parameter. Supported parameter types: + // - [smartcontract.AnyType] + // - [smartcontract.BoolType] + // - [smartcontract.IntegerType] + // - [smartcontract.ByteArrayType] + // - [smartcontract.StringType] + // - [smartcontract.Hash160Type] + // - [smartcontract.Hash256Type] + // - [smartcontract.PublicKeyType] + // - [smartcontract.SignatureType] + // nil value treated as missing filter. NotificationFilter struct { - Contract *util.Uint160 `json:"contract,omitempty"` - Name *string `json:"name,omitempty"` + Contract *util.Uint160 `json:"contract,omitempty"` + Name *string `json:"name,omitempty"` + Parameters []smartcontract.Parameter `json:"parameters,omitempty"` + parametersCache []stackitem.Item } // ExecutionFilter is a wrapper structure used for transaction and persisting // scripts execution events. It allows to choose failing or successful @@ -112,7 +136,8 @@ func (f TxFilter) IsValid() error { return nil } -// Copy creates a deep copy of the NotificationFilter. It handles nil NotificationFilter correctly. +// Copy creates a deep copy of the NotificationFilter. It handles nil +// NotificationFilter correctly. func (f *NotificationFilter) Copy() *NotificationFilter { if f == nil { return nil @@ -126,14 +151,71 @@ func (f *NotificationFilter) Copy() *NotificationFilter { res.Name = new(string) *res.Name = *f.Name } + if len(f.Parameters) != 0 { + res.Parameters = slices.Clone(f.Parameters) + } return res } +// ParametersAsStackItems returns [stackitem.Item] version of [NotificationFilter.Parameters] +// according to [smartcontract.Parameter.ToStackItem]; Notice that the result is cached +// internally in [NotificationFilter] for efficiency, so once you call this method it will +// not change even if you change any structure fields. If you need to update parameters, use +// [NotificationFilter.Copy]. It mainly should be used by server code. Must not be used +// concurrently. +func (f *NotificationFilter) ParametersAsStackItems() ([]stackitem.Item, error) { + if len(f.Parameters) == 0 { + return nil, nil + } + if f.parametersCache == nil { + f.parametersCache = make([]stackitem.Item, 0, len(f.Parameters)) + for i, p := range f.Parameters { + si, err := p.ToStackItem() + if err != nil { + f.parametersCache = nil + return nil, fmt.Errorf("converting %d parameter to stack item: %w", i, err) + } + f.parametersCache = append(f.parametersCache, si) + } + } + + return f.parametersCache, nil +} + // IsValid implements SubscriptionFilter interface. func (f NotificationFilter) IsValid() error { if f.Name != nil && len(*f.Name) > runtime.MaxEventNameLen { return fmt.Errorf("%w: NotificationFilter name parameter must be less than %d", ErrInvalidSubscriptionFilter, runtime.MaxEventNameLen) } + l := len(f.Parameters) + noopFilter := l > 0 + if l > 0 { + if l > MaxNotificationFilterParametersCount { + return fmt.Errorf("%w: NotificationFilter's parameters number exceeded: %d > %d", ErrInvalidSubscriptionFilter, l, MaxNotificationFilterParametersCount) + } + for i, parameter := range f.Parameters { + switch parameter.Type { + case smartcontract.BoolType, + smartcontract.IntegerType, + smartcontract.ByteArrayType, + smartcontract.StringType, + smartcontract.Hash160Type, + smartcontract.Hash256Type, + smartcontract.PublicKeyType, + smartcontract.SignatureType: + noopFilter = false + case smartcontract.AnyType: + default: + return fmt.Errorf("%w: NotificationFilter type parameter %d is unsupported: %s", ErrInvalidSubscriptionFilter, i, parameter.Type) + } + if _, err := parameter.ToStackItem(); err != nil { + return fmt.Errorf("%w: NotificationFilter %d filter parameter does not correspond to any stack item: %w", ErrInvalidSubscriptionFilter, i, err) + } + } + } + if noopFilter { + return fmt.Errorf("%w: NotificationFilter cannot have all parameters of type %s", ErrInvalidSubscriptionFilter, smartcontract.AnyType) + } return nil } diff --git a/pkg/neorpc/filters_test.go b/pkg/neorpc/filters_test.go index e38591c5d9..07d3368cdb 100644 --- a/pkg/neorpc/filters_test.go +++ b/pkg/neorpc/filters_test.go @@ -3,6 +3,7 @@ package neorpc import ( "testing" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/stretchr/testify/require" ) @@ -88,6 +89,15 @@ func TestNotificationFilterCopy(t *testing.T) { require.Equal(t, bf, tf) *bf.Name = "azaza" require.NotEqual(t, bf, tf) + + var err error + bf.Parameters, err = smartcontract.NewParametersFromValues(1, "2", []byte{3}) + require.NoError(t, err) + + tf = bf.Copy() + require.Equal(t, bf, tf) + bf.Parameters[0], bf.Parameters[1] = bf.Parameters[1], bf.Parameters[0] + require.NotEqual(t, bf, tf) } func TestExecutionFilterCopy(t *testing.T) { diff --git a/pkg/neorpc/rpcevent/filter.go b/pkg/neorpc/rpcevent/filter.go index 744b59f392..26203bcbe4 100644 --- a/pkg/neorpc/rpcevent/filter.go +++ b/pkg/neorpc/rpcevent/filter.go @@ -6,6 +6,7 @@ import ( "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/result" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) type ( @@ -66,7 +67,27 @@ func Matches(f Comparator, r Container) bool { notification := r.EventPayload().(*state.ContainedNotificationEvent) hashOk := filt.Contract == nil || notification.ScriptHash.Equals(*filt.Contract) nameOk := filt.Name == nil || notification.Name == *filt.Name - return hashOk && nameOk + parametersOk := true + if len(filt.Parameters) > 0 { + stackItems := notification.Item.Value().([]stackitem.Item) + parameters, err := filt.ParametersAsStackItems() + if err != nil { + return false + } + if len(parameters) > len(stackItems) { + return false + } + for i, p := range parameters { + if p.Type() == stackitem.AnyT && p.Value() == nil { + continue + } + if !p.Equals(stackItems[i]) { + parametersOk = false + break + } + } + } + return hashOk && nameOk && parametersOk case neorpc.ExecutionEventID: filt := filter.(neorpc.ExecutionFilter) applog := r.EventPayload().(*state.AppExecResult) diff --git a/pkg/neorpc/rpcevent/filter_test.go b/pkg/neorpc/rpcevent/filter_test.go index 70cfa92255..b9eac22b67 100644 --- a/pkg/neorpc/rpcevent/filter_test.go +++ b/pkg/neorpc/rpcevent/filter_test.go @@ -10,7 +10,9 @@ import ( "github.com/nspcc-dev/neo-go/pkg/neorpc" "github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/network/payload" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/vmstate" "github.com/stretchr/testify/require" ) @@ -55,6 +57,10 @@ func TestMatches(t *testing.T) { name := "ntf name" badName := "bad name" badType := mempoolevent.TransactionRemoved + parameters, err := smartcontract.NewParametersFromValues(1, "2", []byte{3}) + require.NoError(t, err) + badParameters, err := smartcontract.NewParametersFromValues([]byte{3}, "2", []byte{1}) + require.NoError(t, err) bContainer := testContainer{ id: neorpc.BlockEventID, pld: &block.Block{ @@ -76,6 +82,16 @@ func TestMatches(t *testing.T) { id: neorpc.NotificationEventID, pld: &state.ContainedNotificationEvent{NotificationEvent: state.NotificationEvent{ScriptHash: contract, Name: name}}, } + ntfContainerParameters := testContainer{ + id: neorpc.NotificationEventID, + pld: &state.ContainedNotificationEvent{ + NotificationEvent: state.NotificationEvent{ + ScriptHash: contract, + Name: name, + Item: stackitem.NewArray(prmsToStack(t, parameters)), + }, + }, + } exContainer := testContainer{ id: neorpc.ExecutionEventID, pld: &state.AppExecResult{Container: cnt, Execution: state.Execution{VMState: st}}, @@ -261,6 +277,24 @@ func TestMatches(t *testing.T) { container: ntfContainer, expected: true, }, + { + name: "notification, parameters match", + comparator: testComparator{ + id: neorpc.NotificationEventID, + filter: neorpc.NotificationFilter{Name: &name, Contract: &contract, Parameters: parameters}, + }, + container: ntfContainerParameters, + expected: true, + }, + { + name: "notification, parameters mismatch", + comparator: testComparator{ + id: neorpc.NotificationEventID, + filter: neorpc.NotificationFilter{Name: &name, Contract: &contract, Parameters: badParameters}, + }, + container: ntfContainerParameters, + expected: false, + }, { name: "execution, no filter", comparator: testComparator{id: neorpc.ExecutionEventID}, @@ -343,3 +377,13 @@ func TestMatches(t *testing.T) { }) } } + +func prmsToStack(t *testing.T, pp []smartcontract.Parameter) []stackitem.Item { + res := make([]stackitem.Item, 0, len(pp)) + for _, p := range pp { + s, err := p.ToStackItem() + require.NoError(t, err) + res = append(res, s) + } + return res +} diff --git a/pkg/rpcclient/wsclient_test.go b/pkg/rpcclient/wsclient_test.go index c8e5b8e6a6..17b7fdb684 100644 --- a/pkg/rpcclient/wsclient_test.go +++ b/pkg/rpcclient/wsclient_test.go @@ -24,6 +24,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/network/payload" "github.com/nspcc-dev/neo-go/pkg/services/rpcsrv/params" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/vmstate" "github.com/stretchr/testify/assert" @@ -591,6 +592,7 @@ func TestWSFilteredSubscriptions(t *testing.T) { require.NoError(t, json.Unmarshal(param.RawMessage, filt)) require.Equal(t, util.Uint160{1, 2, 3, 4, 5}, *filt.Contract) require.Nil(t, filt.Name) + require.Empty(t, filt.Parameters) }, }, {"notifications name", @@ -605,6 +607,7 @@ func TestWSFilteredSubscriptions(t *testing.T) { require.NoError(t, json.Unmarshal(param.RawMessage, filt)) require.Equal(t, "my_pretty_notification", *filt.Name) require.Nil(t, filt.Contract) + require.Empty(t, filt.Parameters) }, }, {"notifications contract hash and name", @@ -620,6 +623,27 @@ func TestWSFilteredSubscriptions(t *testing.T) { require.NoError(t, json.Unmarshal(param.RawMessage, filt)) require.Equal(t, util.Uint160{1, 2, 3, 4, 5}, *filt.Contract) require.Equal(t, "my_pretty_notification", *filt.Name) + require.Empty(t, filt.Parameters) + }, + }, + {"notifications parameters", + func(t *testing.T, wsc *WSClient) { + contract := util.Uint160{1, 2, 3, 4, 5} + name := "my_pretty_notification" + prms, err := smartcontract.NewParametersFromValues(1, "2", []byte{3}) + require.NoError(t, err) + _, err = wsc.ReceiveExecutionNotifications(&neorpc.NotificationFilter{Contract: &contract, Name: &name, Parameters: prms}, make(chan *state.ContainedNotificationEvent)) + require.NoError(t, err) + }, + func(t *testing.T, p *params.Params) { + param := p.Value(1) + filt := new(neorpc.NotificationFilter) + prms, err := smartcontract.NewParametersFromValues(1, "2", []byte{3}) + require.NoError(t, err) + require.NoError(t, json.Unmarshal(param.RawMessage, filt)) + require.Equal(t, util.Uint160{1, 2, 3, 4, 5}, *filt.Contract) + require.Equal(t, "my_pretty_notification", *filt.Name) + require.Equal(t, prms, filt.Parameters) }, }, {"executions state", diff --git a/pkg/services/rpcsrv/subscription_test.go b/pkg/services/rpcsrv/subscription_test.go index 7466f18e73..07c5ab08af 100644 --- a/pkg/services/rpcsrv/subscription_test.go +++ b/pkg/services/rpcsrv/subscription_test.go @@ -1,8 +1,11 @@ package rpcsrv import ( + "encoding/base64" + "encoding/hex" "encoding/json" "fmt" + "slices" "strings" "sync" "testing" @@ -14,6 +17,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/neorpc" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/stretchr/testify/require" ) @@ -261,6 +265,39 @@ func TestFilteredSubscriptions(t *testing.T) { require.Equal(t, "my_pretty_notification", n) }, }, + "notification matching contract hash and parameter": { + params: `["notification_from_execution", {"contract":"` + testContractHash + `", "parameters":[{"type":"Any","value":null},{"type":"Hash160","value":"` + testContractHash + `"}]}]`, + check: func(t *testing.T, resp *neorpc.Notification) { + rmap := resp.Payload[0].(map[string]any) + require.Equal(t, neorpc.NotificationEventID, resp.Event) + c := rmap["contract"].(string) + require.Equal(t, "0x"+testContractHash, c) + // It should be exact unique "Init" call sending all the tokens to the contract itself. + parameters := rmap["state"].(map[string]any)["value"].([]any) + require.Len(t, parameters, 3) + // Sender. + toType := parameters[1].(map[string]any)["type"].(string) + require.Equal(t, smartcontract.Hash160Type.ConvertToStackitemType().String(), toType) + to := parameters[1].(map[string]any)["value"].(string) + hashExp, err := hex.DecodeString(testContractHash) + require.NoError(t, err) + slices.Reverse(hashExp) + hashGot, err := base64.StdEncoding.DecodeString(to) + require.NoError(t, err) + require.Equal(t, hashExp, hashGot) + // This amount happens only for initial token distribution. + amountType := parameters[2].(map[string]any)["type"].(string) + require.Equal(t, smartcontract.IntegerType.ConvertToStackitemType().String(), amountType) + amount := parameters[2].(map[string]any)["value"].(string) + require.Equal(t, "1000000", amount) + }, + }, + "notification matching contract hash but unknown parameter": { + params: `["notification_from_execution", {"contract":"` + testContractHash + `", "parameters":[{"type":"Any","value":null},{"type":"Hash160","value":"ffffffffffffffffffffffffffffffffffffffff"}]}]`, + check: func(t *testing.T, resp *neorpc.Notification) { + t.Fatal("this filter should not return any notification from test contract") + }, + }, "execution matching state": { params: `["transaction_executed", {"state":"HALT"}]`, check: func(t *testing.T, resp *neorpc.Notification) { From 97bd73b38c6d7a8309174abf7b6a7c139a890017 Mon Sep 17 00:00:00 2001 From: Pavel Karpy Date: Fri, 22 Nov 2024 18:39:28 +0300 Subject: [PATCH 2/2] rpcsrv/tests: simplify tests with parsed test contract hash Signed-off-by: Pavel Karpy --- pkg/services/rpcsrv/client_test.go | 10 +-- pkg/services/rpcsrv/server_test.go | 92 ++++++++++++------------ pkg/services/rpcsrv/subscription_test.go | 23 +++--- 3 files changed, 61 insertions(+), 64 deletions(-) diff --git a/pkg/services/rpcsrv/client_test.go b/pkg/services/rpcsrv/client_test.go index 9f24c7b53f..b29cec914b 100644 --- a/pkg/services/rpcsrv/client_test.go +++ b/pkg/services/rpcsrv/client_test.go @@ -75,7 +75,7 @@ func TestClient_NEP17(t *testing.T) { t.Cleanup(c.Close) require.NoError(t, c.Init()) - h, err := util.Uint160DecodeStringLE(testContractHash) + h, err := util.Uint160DecodeStringLE(testContractHashLE) require.NoError(t, err) rub := nep17.NewReader(invoker.New(c, nil), h) @@ -295,7 +295,7 @@ func TestClientManagementContract(t *testing.T) { ids, err := manReader.GetContractHashesExpanded(10) require.NoError(t, err) ctrs := make([]management.IDHash, 0) - for i, s := range []string{testContractHash, verifyContractHash, verifyWithArgsContractHash, nnsContractHash, nfsoContractHash, storageContractHash} { + for i, s := range []string{testContractHashLE, verifyContractHash, verifyWithArgsContractHash, nnsContractHash, nfsoContractHash, storageContractHash} { h, err := util.Uint160DecodeStringLE(s) require.NoError(t, err) ctrs = append(ctrs, management.IDHash{ID: int32(i) + 1, Hash: h}) @@ -2288,7 +2288,7 @@ func TestClient_FindStorage(t *testing.T) { t.Cleanup(c.Close) require.NoError(t, c.Init()) - h, err := util.Uint160DecodeStringLE(testContractHash) + h, err := util.Uint160DecodeStringLE(testContractHashLE) require.NoError(t, err) prefix := []byte("aa") expected := result.FindStorage{ @@ -2355,7 +2355,7 @@ func TestClient_FindStorageHistoric(t *testing.T) { root, err := util.Uint256DecodeStringLE(block20StateRootLE) require.NoError(t, err) - h, err := util.Uint160DecodeStringLE(testContractHash) + h, err := util.Uint160DecodeStringLE(testContractHashLE) require.NoError(t, err) prefix := []byte("aa") expected := result.FindStorage{ @@ -2424,7 +2424,7 @@ func TestClient_GetStorageHistoric(t *testing.T) { root, err := util.Uint256DecodeStringLE(block20StateRootLE) require.NoError(t, err) - h, err := util.Uint160DecodeStringLE(testContractHash) + h, err := util.Uint160DecodeStringLE(testContractHashLE) require.NoError(t, err) key := []byte("aa10") expected := []byte("v2") diff --git a/pkg/services/rpcsrv/server_test.go b/pkg/services/rpcsrv/server_test.go index 5041b44bca..e12cdddc45 100644 --- a/pkg/services/rpcsrv/server_test.go +++ b/pkg/services/rpcsrv/server_test.go @@ -80,9 +80,9 @@ type rpcTestCase struct { const ( // genesisBlockHash is an LE hash of genesis block in basic testing chain. genesisBlockHash = "0f8fb4e17d2ab9f3097af75ca7fd16064160fb8043db94909e00dd4e257b9dc4" - // testContractHash is an LE hash of NEP-17 "Rubl" contract deployed at block #2 + // testContractHashLE is an LE hash of NEP-17 "Rubl" contract deployed at block #2 // of basic testing chain. - testContractHash = "449fe8fbd4523072f5e3a4dfa17a494c119d4c08" + testContractHashLE = "449fe8fbd4523072f5e3a4dfa17a494c119d4c08" // deploymentTxHash is an LE hash of transaction that deploys NEP-17 "Rubl" // contract at block #2 of basic testing chain. deploymentTxHash = "bbb8ec059dd320dc9de5a8fb8c75351d8e369fca0256f7a6bdb623dcf71861ed" @@ -125,6 +125,10 @@ const ( ) var ( + // testContractHashLE is a hash of NEP-11 divisible "Rubl" contract + // placed in "internal/basicchain/testdata/test_contract.go" and + // deployed at block #2 of basic testing chain. + testContractHash, _ = util.Uint160DecodeStringLE(testContractHashLE) // nnsHash is a hash of NEP-11 non-divisible "examples/nft-nd-nns" contract // deployed at block #11 of basic testing chain. nnsHash, _ = util.Uint160DecodeStringLE(nnsContractHash) @@ -159,7 +163,7 @@ var rpcFunctionsWithUnsupportedStatesTestCases = map[string][]rpcTestCase{ "getstate": { { name: "unsupported state", - params: `["0000000000000000000000000000000000000000000000000000000000000000", "` + testContractHash + `", "QQ=="]`, + params: `["0000000000000000000000000000000000000000000000000000000000000000", "` + testContractHashLE + `", "QQ=="]`, fail: true, errCode: neorpc.ErrUnsupportedStateCode, }, @@ -275,12 +279,12 @@ var rpcTestCases = map[string][]rpcTestCase{ "getcontractstate": { { name: "positive, by hash", - params: fmt.Sprintf(`["%s"]`, testContractHash), + params: fmt.Sprintf(`["%s"]`, testContractHashLE), result: func(e *executor) any { return &state.Contract{} }, check: func(t *testing.T, e *executor, cs any) { res, ok := cs.(*state.Contract) require.True(t, ok) - assert.Equal(t, testContractHash, res.Hash.StringLE()) + assert.Equal(t, testContractHashLE, res.Hash.StringLE()) }, }, { @@ -554,7 +558,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "invalid key", - params: `["0000000000000000000000000000000000000000000000000000000000000000", "` + testContractHash + `", "notahex"]`, + params: `["0000000000000000000000000000000000000000000000000000000000000000", "` + testContractHashLE + `", "notahex"]`, fail: true, errCode: neorpc.InvalidParamsCode, }, @@ -580,7 +584,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "invalid key", - params: `["0000000000000000000000000000000000000000000000000000000000000000", "` + testContractHash + `", "notabase64%"]`, + params: `["0000000000000000000000000000000000000000000000000000000000000000", "` + testContractHashLE + `", "notabase64%"]`, fail: true, errCode: neorpc.InvalidParamsCode, }, @@ -592,7 +596,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "unknown root/item", - params: `["0000000000000000000000000000000000000000000000000000000000000000", "` + testContractHash + `", "QQ=="]`, + params: `["0000000000000000000000000000000000000000000000000000000000000000", "` + testContractHashLE + `", "QQ=="]`, fail: true, errCode: neorpc.ErrUnknownContractCode, }, @@ -618,13 +622,13 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "invalid prefix", - params: `["` + block20StateRootLE + `", "` + testContractHash + `", "notabase64%"]`, + params: `["` + block20StateRootLE + `", "` + testContractHashLE + `", "notabase64%"]`, fail: true, errCode: neorpc.InvalidParamsCode, }, { name: "invalid key", - params: `["` + block20StateRootLE + `", "` + testContractHash + `", "QQ==", "notabase64%"]`, + params: `["` + block20StateRootLE + `", "` + testContractHashLE + `", "QQ==", "notabase64%"]`, fail: true, errCode: neorpc.InvalidParamsCode, }, @@ -666,7 +670,7 @@ var rpcTestCases = map[string][]rpcTestCase{ "getstorage": { { name: "positive", - params: fmt.Sprintf(`["%s", "dGVzdGtleQ=="]`, testContractHash), + params: fmt.Sprintf(`["%s", "dGVzdGtleQ=="]`, testContractHashLE), result: func(e *executor) any { v := base64.StdEncoding.EncodeToString([]byte("newtestvalue")) return &v @@ -674,7 +678,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "missing key", - params: fmt.Sprintf(`["%s", "dGU="]`, testContractHash), + params: fmt.Sprintf(`["%s", "dGU="]`, testContractHashLE), fail: true, errCode: neorpc.ErrUnknownStorageItemCode, }, @@ -686,7 +690,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "no second parameter", - params: fmt.Sprintf(`["%s"]`, testContractHash), + params: fmt.Sprintf(`["%s"]`, testContractHashLE), fail: true, errCode: neorpc.InvalidParamsCode, }, @@ -698,7 +702,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "invalid key", - params: fmt.Sprintf(`["%s", "notabase64$"]`, testContractHash), + params: fmt.Sprintf(`["%s", "notabase64$"]`, testContractHashLE), fail: true, errCode: neorpc.InvalidParamsCode, }, @@ -706,7 +710,7 @@ var rpcTestCases = map[string][]rpcTestCase{ "getstoragehistoric": { { name: "positive", - params: fmt.Sprintf(`["%s", "%s", "%s"]`, block20StateRootLE, testContractHash, base64.StdEncoding.EncodeToString([]byte("aa10"))), + params: fmt.Sprintf(`["%s", "%s", "%s"]`, block20StateRootLE, testContractHashLE, base64.StdEncoding.EncodeToString([]byte("aa10"))), result: func(e *executor) any { v := base64.StdEncoding.EncodeToString([]byte("v2")) return &v @@ -714,7 +718,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "missing key", - params: fmt.Sprintf(`["%s", "%s", "dGU="]`, block20StateRootLE, testContractHash), + params: fmt.Sprintf(`["%s", "%s", "dGU="]`, block20StateRootLE, testContractHashLE), fail: true, errCode: neorpc.ErrUnknownStorageItemCode, }, @@ -732,7 +736,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "no third parameter", - params: fmt.Sprintf(`["%s", "%s"]`, block20StateRootLE, testContractHash), + params: fmt.Sprintf(`["%s", "%s"]`, block20StateRootLE, testContractHashLE), fail: true, errCode: neorpc.InvalidParamsCode, }, @@ -750,7 +754,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "invalid key", - params: fmt.Sprintf(`["%s", "%s", "notabase64$"]`, block20StateRootLE, testContractHash), + params: fmt.Sprintf(`["%s", "%s", "notabase64$"]`, block20StateRootLE, testContractHashLE), fail: true, errCode: neorpc.InvalidParamsCode, }, @@ -758,7 +762,7 @@ var rpcTestCases = map[string][]rpcTestCase{ "findstorage": { { name: "not truncated", - params: fmt.Sprintf(`["%s", "%s"]`, testContractHash, base64.StdEncoding.EncodeToString([]byte("aa1"))), + params: fmt.Sprintf(`["%s", "%s"]`, testContractHashLE, base64.StdEncoding.EncodeToString([]byte("aa1"))), result: func(_ *executor) any { return new(result.FindStorage) }, check: func(t *testing.T, e *executor, res any) { actual, ok := res.(*result.FindStorage) @@ -779,7 +783,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "truncated first page", - params: fmt.Sprintf(`["%s", "%s"]`, testContractHash, base64.StdEncoding.EncodeToString([]byte("aa"))), + params: fmt.Sprintf(`["%s", "%s"]`, testContractHashLE, base64.StdEncoding.EncodeToString([]byte("aa"))), result: func(_ *executor) any { return new(result.FindStorage) }, check: func(t *testing.T, e *executor, res any) { actual, ok := res.(*result.FindStorage) @@ -804,7 +808,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "truncated second page", - params: fmt.Sprintf(`["%s", "%s", 2]`, testContractHash, base64.StdEncoding.EncodeToString([]byte("aa"))), + params: fmt.Sprintf(`["%s", "%s", 2]`, testContractHashLE, base64.StdEncoding.EncodeToString([]byte("aa"))), result: func(_ *executor) any { return new(result.FindStorage) }, check: func(t *testing.T, e *executor, res any) { actual, ok := res.(*result.FindStorage) @@ -850,7 +854,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "unknown key", - params: fmt.Sprintf(`["%s", "%s"]`, testContractHash, base64.StdEncoding.EncodeToString([]byte("unknown-key"))), + params: fmt.Sprintf(`["%s", "%s"]`, testContractHashLE, base64.StdEncoding.EncodeToString([]byte("unknown-key"))), result: func(_ *executor) any { return new(result.FindStorage) }, check: func(t *testing.T, e *executor, res any) { actual, ok := res.(*result.FindStorage) @@ -872,7 +876,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "no second parameter", - params: fmt.Sprintf(`["%s"]`, testContractHash), + params: fmt.Sprintf(`["%s"]`, testContractHashLE), fail: true, errCode: neorpc.InvalidParamsCode, }, @@ -884,13 +888,13 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "invalid key", - params: fmt.Sprintf(`["%s", "notabase64$"]`, testContractHash), + params: fmt.Sprintf(`["%s", "notabase64$"]`, testContractHashLE), fail: true, errCode: neorpc.InvalidParamsCode, }, { name: "invalid page", - params: fmt.Sprintf(`["%s", "", "not-an-int"]`, testContractHash), + params: fmt.Sprintf(`["%s", "", "not-an-int"]`, testContractHashLE), fail: true, errCode: neorpc.InvalidParamsCode, }, @@ -898,7 +902,7 @@ var rpcTestCases = map[string][]rpcTestCase{ "findstoragehistoric": { { name: "not truncated", - params: fmt.Sprintf(`["%s", "%s", "%s"]`, block20StateRootLE, testContractHash, base64.StdEncoding.EncodeToString([]byte("aa1"))), + params: fmt.Sprintf(`["%s", "%s", "%s"]`, block20StateRootLE, testContractHashLE, base64.StdEncoding.EncodeToString([]byte("aa1"))), result: func(_ *executor) any { return new(result.FindStorage) }, check: func(t *testing.T, e *executor, res any) { actual, ok := res.(*result.FindStorage) @@ -919,7 +923,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "truncated first page", - params: fmt.Sprintf(`["%s", "%s", "%s"]`, block20StateRootLE, testContractHash, base64.StdEncoding.EncodeToString([]byte("aa"))), + params: fmt.Sprintf(`["%s", "%s", "%s"]`, block20StateRootLE, testContractHashLE, base64.StdEncoding.EncodeToString([]byte("aa"))), result: func(_ *executor) any { return new(result.FindStorage) }, check: func(t *testing.T, e *executor, res any) { actual, ok := res.(*result.FindStorage) @@ -944,7 +948,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "truncated second page", - params: fmt.Sprintf(`["%s","%s", "%s", 2]`, block20StateRootLE, testContractHash, base64.StdEncoding.EncodeToString([]byte("aa"))), + params: fmt.Sprintf(`["%s","%s", "%s", 2]`, block20StateRootLE, testContractHashLE, base64.StdEncoding.EncodeToString([]byte("aa"))), result: func(_ *executor) any { return new(result.FindStorage) }, check: func(t *testing.T, e *executor, res any) { actual, ok := res.(*result.FindStorage) @@ -990,7 +994,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "unknown key", - params: fmt.Sprintf(`["%s", "%s", "%s"]`, block20StateRootLE, testContractHash, base64.StdEncoding.EncodeToString([]byte("unknown-key"))), + params: fmt.Sprintf(`["%s", "%s", "%s"]`, block20StateRootLE, testContractHashLE, base64.StdEncoding.EncodeToString([]byte("unknown-key"))), result: func(_ *executor) any { return new(result.FindStorage) }, check: func(t *testing.T, e *executor, res any) { actual, ok := res.(*result.FindStorage) @@ -1024,7 +1028,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "no third parameter", - params: fmt.Sprintf(`["%s", "%s"]`, block20StateRootLE, testContractHash), + params: fmt.Sprintf(`["%s", "%s"]`, block20StateRootLE, testContractHashLE), fail: true, errCode: neorpc.InvalidParamsCode, }, @@ -1036,13 +1040,13 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "invalid key", - params: fmt.Sprintf(`["%s", "%s", "notabase64$"]`, block20StateRootLE, testContractHash), + params: fmt.Sprintf(`["%s", "%s", "notabase64$"]`, block20StateRootLE, testContractHashLE), fail: true, errCode: neorpc.InvalidParamsCode, }, { name: "invalid page", - params: fmt.Sprintf(`["%s", "%s", "", "not-an-int"]`, block20StateRootLE, testContractHash), + params: fmt.Sprintf(`["%s", "%s", "", "not-an-int"]`, block20StateRootLE, testContractHashLE), fail: true, errCode: neorpc.InvalidParamsCode, }, @@ -2726,12 +2730,12 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) [] require.NoError(t, err) rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getproof", "params": ["%s", "%s", "%s"]}`, - r.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("testkey"))) + r.Root.StringLE(), testContractHashLE, base64.StdEncoding.EncodeToString([]byte("testkey"))) body := doRPCCall(rpc, httpSrv.URL, t) rawRes := checkErrGetResult(t, body, false, 0) res := new(result.ProofWithKey) require.NoError(t, json.Unmarshal(rawRes, res)) - h, _ := util.Uint160DecodeStringLE(testContractHash) + h, _ := util.Uint160DecodeStringLE(testContractHashLE) skey := makeStorageKey(chain.GetContractState(h).ID, []byte("testkey")) require.Equal(t, skey, res.Key) require.True(t, len(res.Proof) > 0) @@ -2784,14 +2788,14 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) [] root, err := e.chain.GetStateModule().GetStateRoot(4) require.NoError(t, err) // `testkey`-`testvalue` pair was put to the contract storage at block #3 - params := fmt.Sprintf(`"%s", "%s", "%s"`, root.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("testkey"))) + params := fmt.Sprintf(`"%s", "%s", "%s"`, root.Root.StringLE(), testContractHashLE, base64.StdEncoding.EncodeToString([]byte("testkey"))) testGetState(t, params, base64.StdEncoding.EncodeToString([]byte("testvalue"))) }) t.Run("negative: invalid key", func(t *testing.T) { root, err := e.chain.GetStateModule().GetStateRoot(4) require.NoError(t, err) // `testkey`-`testvalue` pair was put to the contract storage at block #3 - params := fmt.Sprintf(`"%s", "%s", "%s"`, root.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("invalidkey"))) + params := fmt.Sprintf(`"%s", "%s", "%s"`, root.Root.StringLE(), testContractHashLE, base64.StdEncoding.EncodeToString([]byte("invalidkey"))) body := doRPCCall(fmt.Sprintf(rpc, params), httpSrv.URL, t) checkErrGetResult(t, body, true, neorpc.InvalidParamsCode) }) @@ -2799,7 +2803,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) [] root, err := e.chain.GetStateModule().GetStateRoot(16) require.NoError(t, err) // `testkey`-`newtestvalue` pair was put to the contract storage at block #16 - params := fmt.Sprintf(`"%s", "%s", "%s"`, root.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("testkey"))) + params := fmt.Sprintf(`"%s", "%s", "%s"`, root.Root.StringLE(), testContractHashLE, base64.StdEncoding.EncodeToString([]byte("testkey"))) testGetState(t, params, base64.StdEncoding.EncodeToString([]byte("newtestvalue"))) }) }) @@ -2834,7 +2838,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) [] // pairs for this test where put to the contract storage at block #16 root, err := e.chain.GetStateModule().GetStateRoot(16) require.NoError(t, err) - params := fmt.Sprintf(`"%s", "%s", "%s"`, root.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("aa"))) + params := fmt.Sprintf(`"%s", "%s", "%s"`, root.Root.StringLE(), testContractHashLE, base64.StdEncoding.EncodeToString([]byte("aa"))) testFindStates(t, params, root.Root, result.FindStates{ Results: []result.KeyValue{ {Key: []byte("aa10"), Value: []byte("v2")}, @@ -2848,7 +2852,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) [] // empty prefix should be considered as no prefix specified. root, err := e.chain.GetStateModule().GetStateRoot(16) require.NoError(t, err) - params := fmt.Sprintf(`"%s", "%s", "%s", ""`, root.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("aa"))) + params := fmt.Sprintf(`"%s", "%s", "%s", ""`, root.Root.StringLE(), testContractHashLE, base64.StdEncoding.EncodeToString([]byte("aa"))) testFindStates(t, params, root.Root, result.FindStates{ Results: []result.KeyValue{ {Key: []byte("aa10"), Value: []byte("v2")}, @@ -2873,7 +2877,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) [] // pairs for this test where put to the contract storage at block #16 root, err := e.chain.GetStateModule().GetStateRoot(16) require.NoError(t, err) - params := fmt.Sprintf(`"%s", "%s", "%s", "%s"`, root.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("aa")), base64.StdEncoding.EncodeToString([]byte("aa10"))) + params := fmt.Sprintf(`"%s", "%s", "%s", "%s"`, root.Root.StringLE(), testContractHashLE, base64.StdEncoding.EncodeToString([]byte("aa")), base64.StdEncoding.EncodeToString([]byte("aa10"))) testFindStates(t, params, root.Root, result.FindStates{ Results: []result.KeyValue{ {Key: []byte("aa50"), Value: []byte("v3")}, @@ -2886,7 +2890,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) [] // pairs for this test where put to the contract storage at block #16 root, err := e.chain.GetStateModule().GetStateRoot(16) require.NoError(t, err) - params := fmt.Sprintf(`"%s", "%s", "%s", "", %d`, root.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("aa")), limit) + params := fmt.Sprintf(`"%s", "%s", "%s", "", %d`, root.Root.StringLE(), testContractHashLE, base64.StdEncoding.EncodeToString([]byte("aa")), limit) expected := result.FindStates{ Results: []result.KeyValue{ {Key: []byte("aa10"), Value: []byte("v2")}, @@ -2904,7 +2908,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) [] // pairs for this test where put to the contract storage at block #16 root, err := e.chain.GetStateModule().GetStateRoot(16) require.NoError(t, err) - params := fmt.Sprintf(`"%s", "%s", "%s", "%s", %d`, root.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("aa")), base64.StdEncoding.EncodeToString([]byte("aa00")), 1) + params := fmt.Sprintf(`"%s", "%s", "%s", "%s", %d`, root.Root.StringLE(), testContractHashLE, base64.StdEncoding.EncodeToString([]byte("aa")), base64.StdEncoding.EncodeToString([]byte("aa00")), 1) testFindStates(t, params, root.Root, result.FindStates{ Results: []result.KeyValue{ {Key: []byte("aa10"), Value: []byte("v2")}, @@ -3599,7 +3603,7 @@ func checkNep11Balances(t *testing.T, e *executor, acc any) { func checkNep17Balances(t *testing.T, e *executor, acc any) { res, ok := acc.(*result.NEP17Balances) require.True(t, ok) - rubles, err := util.Uint160DecodeStringLE(testContractHash) + rubles, err := util.Uint160DecodeStringLE(testContractHashLE) require.NoError(t, err) expected := result.NEP17Balances{ Balances: []result.NEP17Balance{ @@ -3741,7 +3745,7 @@ func checkNep17Transfers(t *testing.T, e *executor, acc any) { func checkNep17TransfersAux(t *testing.T, e *executor, acc any, sent, rcvd []int) { res, ok := acc.(*result.NEP17Transfers) require.True(t, ok) - rublesHash, err := util.Uint160DecodeStringLE(testContractHash) + rublesHash, err := util.Uint160DecodeStringLE(testContractHashLE) require.NoError(t, err) blockWithFAULTedTx, err := e.chain.GetBlock(e.chain.GetHeaderHash(faultedTxBlock)) // Transaction with ABORT inside. diff --git a/pkg/services/rpcsrv/subscription_test.go b/pkg/services/rpcsrv/subscription_test.go index 07c5ab08af..e0fa37950e 100644 --- a/pkg/services/rpcsrv/subscription_test.go +++ b/pkg/services/rpcsrv/subscription_test.go @@ -2,10 +2,8 @@ package rpcsrv import ( "encoding/base64" - "encoding/hex" "encoding/json" "fmt" - "slices" "strings" "sync" "testing" @@ -237,12 +235,12 @@ func TestFilteredSubscriptions(t *testing.T) { }, }, "notification matching contract hash": { - params: `["notification_from_execution", {"contract":"` + testContractHash + `"}]`, + params: `["notification_from_execution", {"contract":"` + testContractHashLE + `"}]`, check: func(t *testing.T, resp *neorpc.Notification) { rmap := resp.Payload[0].(map[string]any) require.Equal(t, neorpc.NotificationEventID, resp.Event) c := rmap["contract"].(string) - require.Equal(t, "0x"+testContractHash, c) + require.Equal(t, "0x"+testContractHashLE, c) }, }, "notification matching name": { @@ -255,23 +253,23 @@ func TestFilteredSubscriptions(t *testing.T) { }, }, "notification matching contract hash and name": { - params: `["notification_from_execution", {"contract":"` + testContractHash + `", "name":"my_pretty_notification"}]`, + params: `["notification_from_execution", {"contract":"` + testContractHashLE + `", "name":"my_pretty_notification"}]`, check: func(t *testing.T, resp *neorpc.Notification) { rmap := resp.Payload[0].(map[string]any) require.Equal(t, neorpc.NotificationEventID, resp.Event) c := rmap["contract"].(string) - require.Equal(t, "0x"+testContractHash, c) + require.Equal(t, "0x"+testContractHashLE, c) n := rmap["name"].(string) require.Equal(t, "my_pretty_notification", n) }, }, "notification matching contract hash and parameter": { - params: `["notification_from_execution", {"contract":"` + testContractHash + `", "parameters":[{"type":"Any","value":null},{"type":"Hash160","value":"` + testContractHash + `"}]}]`, + params: `["notification_from_execution", {"contract":"` + testContractHashLE + `", "parameters":[{"type":"Any","value":null},{"type":"Hash160","value":"` + testContractHashLE + `"}]}]`, check: func(t *testing.T, resp *neorpc.Notification) { rmap := resp.Payload[0].(map[string]any) require.Equal(t, neorpc.NotificationEventID, resp.Event) c := rmap["contract"].(string) - require.Equal(t, "0x"+testContractHash, c) + require.Equal(t, "0x"+testContractHashLE, c) // It should be exact unique "Init" call sending all the tokens to the contract itself. parameters := rmap["state"].(map[string]any)["value"].([]any) require.Len(t, parameters, 3) @@ -279,12 +277,7 @@ func TestFilteredSubscriptions(t *testing.T) { toType := parameters[1].(map[string]any)["type"].(string) require.Equal(t, smartcontract.Hash160Type.ConvertToStackitemType().String(), toType) to := parameters[1].(map[string]any)["value"].(string) - hashExp, err := hex.DecodeString(testContractHash) - require.NoError(t, err) - slices.Reverse(hashExp) - hashGot, err := base64.StdEncoding.DecodeString(to) - require.NoError(t, err) - require.Equal(t, hashExp, hashGot) + require.Equal(t, base64.StdEncoding.EncodeToString(testContractHash.BytesBE()), to) // This amount happens only for initial token distribution. amountType := parameters[2].(map[string]any)["type"].(string) require.Equal(t, smartcontract.IntegerType.ConvertToStackitemType().String(), amountType) @@ -293,7 +286,7 @@ func TestFilteredSubscriptions(t *testing.T) { }, }, "notification matching contract hash but unknown parameter": { - params: `["notification_from_execution", {"contract":"` + testContractHash + `", "parameters":[{"type":"Any","value":null},{"type":"Hash160","value":"ffffffffffffffffffffffffffffffffffffffff"}]}]`, + params: `["notification_from_execution", {"contract":"` + testContractHashLE + `", "parameters":[{"type":"Any","value":null},{"type":"Hash160","value":"ffffffffffffffffffffffffffffffffffffffff"}]}]`, check: func(t *testing.T, resp *neorpc.Notification) { t.Fatal("this filter should not return any notification from test contract") },