github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/services/rpcsrv/server_test.go (about)

     1  package rpcsrv
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/base64"
     7  	"encoding/binary"
     8  	"encoding/json"
     9  	"fmt"
    10  	gio "io"
    11  	"math"
    12  	"math/big"
    13  	"net"
    14  	"net/http"
    15  	"net/http/httptest"
    16  	"reflect"
    17  	"sort"
    18  	"strconv"
    19  	"strings"
    20  	"sync/atomic"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/google/uuid"
    25  	"github.com/gorilla/websocket"
    26  	"github.com/nspcc-dev/neo-go/internal/random"
    27  	"github.com/nspcc-dev/neo-go/internal/testchain"
    28  	"github.com/nspcc-dev/neo-go/internal/testserdes"
    29  	"github.com/nspcc-dev/neo-go/pkg/config"
    30  	"github.com/nspcc-dev/neo-go/pkg/core"
    31  	"github.com/nspcc-dev/neo-go/pkg/core/block"
    32  	"github.com/nspcc-dev/neo-go/pkg/core/fee"
    33  	"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
    34  	"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
    35  	"github.com/nspcc-dev/neo-go/pkg/core/state"
    36  	"github.com/nspcc-dev/neo-go/pkg/core/storage/dboper"
    37  	"github.com/nspcc-dev/neo-go/pkg/core/transaction"
    38  	"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
    39  	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
    40  	"github.com/nspcc-dev/neo-go/pkg/encoding/address"
    41  	"github.com/nspcc-dev/neo-go/pkg/io"
    42  	"github.com/nspcc-dev/neo-go/pkg/neorpc"
    43  	"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
    44  	"github.com/nspcc-dev/neo-go/pkg/network"
    45  	"github.com/nspcc-dev/neo-go/pkg/network/payload"
    46  	rpc2 "github.com/nspcc-dev/neo-go/pkg/services/oracle/broadcaster"
    47  	"github.com/nspcc-dev/neo-go/pkg/services/rpcsrv/params"
    48  	"github.com/nspcc-dev/neo-go/pkg/smartcontract"
    49  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
    50  	"github.com/nspcc-dev/neo-go/pkg/util"
    51  	"github.com/nspcc-dev/neo-go/pkg/vm/emit"
    52  	"github.com/nspcc-dev/neo-go/pkg/vm/invocations"
    53  	"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
    54  	"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
    55  	"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
    56  	"github.com/nspcc-dev/neo-go/pkg/wallet"
    57  	"github.com/stretchr/testify/assert"
    58  	"github.com/stretchr/testify/require"
    59  	"go.uber.org/zap/zapcore"
    60  )
    61  
    62  type executor struct {
    63  	chain   *core.Blockchain
    64  	httpSrv *httptest.Server
    65  }
    66  
    67  type rpcTestCase struct {
    68  	name    string
    69  	params  string
    70  	fail    bool
    71  	errCode int64
    72  	result  func(e *executor) any
    73  	check   func(t *testing.T, e *executor, result any)
    74  }
    75  
    76  const genesisBlockHash = "0f8fb4e17d2ab9f3097af75ca7fd16064160fb8043db94909e00dd4e257b9dc4"
    77  const testContractHash = "565cff9508ebc75aadd7fe59f38dac610ab6093c"
    78  const deploymentTxHash = "a14390941cc3a1d87393eff720a722e9cd350bd6ed233c5fe2001326c80eb68e"
    79  
    80  const (
    81  	verifyContractHash                = "06ed5314c2e4cb103029a60b86d46afa2fb8f67c"
    82  	verifyContractAVM                 = "VwIAQS1RCDBwDBTunqIsJ+NL0BSPxBCOCPdOj1BIskrZMCQE2zBxaBPOStkoJATbKGlK2SgkBNsol0A="
    83  	verifyWithArgsContractHash        = "4dc916254efd2947c93b11207e8ffc0bb56161c5"
    84  	nnsContractHash                   = "892429fcd47c30f8451781acc627e8b20e0d64f3"
    85  	nnsToken1ID                       = "6e656f2e636f6d"
    86  	nfsoContractHash                  = "730ebe719ab8e3b69d11dafc95cdb9bf409db179"
    87  	nfsoToken1ID                      = "7e244ffd6aa85fb1579d2ed22e9b761ab62e3486"
    88  	storageContractHash               = "ebc0c16a76c808cd4dde6bcc063f09e45e331ec7"
    89  	faultedTxHashLE                   = "82279bfe9bada282ca0f8cb8e0bb124b921af36f00c69a518320322c6f4fef60"
    90  	faultedTxBlock             uint32 = 23
    91  	invokescriptContractAVM           = "VwIADBQBDAMOBQYMDQIODw0DDgcJAAAAAErZMCQE2zBwaEH4J+yMqiYEEUAMFA0PAwIJAAIBAwcDBAUCAQAOBgwJStkwJATbMHFpQfgn7IyqJgQSQBNA"
    92  	block20StateRootLE                = "2d95b1149230d40c3043a84d42249b7b344f8755ea9fd0b2d95c5d011af85fc7"
    93  )
    94  
    95  var (
    96  	nnsHash, _            = util.Uint160DecodeStringLE(nnsContractHash)
    97  	nfsoHash, _           = util.Uint160DecodeStringLE(nfsoContractHash)
    98  	nfsoToken1ContainerID = util.Uint256{1, 2, 3}
    99  	nfsoToken1ObjectID    = util.Uint256{4, 5, 6}
   100  )
   101  
   102  var rpcFunctionsWithUnsupportedStatesTestCases = map[string][]rpcTestCase{
   103  	"getproof": {
   104  		{
   105  			name:    "unsupported state",
   106  			params:  `[]`,
   107  			fail:    true,
   108  			errCode: neorpc.ErrUnsupportedStateCode,
   109  		},
   110  	},
   111  	"verifyproof": {
   112  		{
   113  			name:    "unsupported state",
   114  			params:  `[]`,
   115  			fail:    true,
   116  			errCode: neorpc.ErrUnsupportedStateCode,
   117  		},
   118  	},
   119  	"getstate": {
   120  		{
   121  			name:    "unsupported state",
   122  			params:  `["0000000000000000000000000000000000000000000000000000000000000000", "` + testContractHash + `", "QQ=="]`,
   123  			fail:    true,
   124  			errCode: neorpc.ErrUnsupportedStateCode,
   125  		},
   126  	},
   127  	"findstates": {
   128  		{
   129  			name:    "unsupported state",
   130  			params:  `["` + block20StateRootLE + `", "0xabcdef"]`,
   131  			fail:    true,
   132  			errCode: neorpc.ErrUnsupportedStateCode,
   133  		},
   134  	},
   135  	"findstoragehistoric": {
   136  		{
   137  			name:    "unsupported state",
   138  			params:  `["` + block20StateRootLE + `", "0xabcdef"]`,
   139  			fail:    true,
   140  			errCode: neorpc.ErrUnsupportedStateCode,
   141  		},
   142  	},
   143  	"invokefunctionhistoric": {
   144  		{
   145  			name:    "unsupported state",
   146  			params:  `[]`,
   147  			fail:    true,
   148  			errCode: neorpc.ErrUnsupportedStateCode,
   149  		},
   150  	},
   151  }
   152  
   153  var rpcTestCases = map[string][]rpcTestCase{
   154  	"getapplicationlog": {
   155  		{
   156  			name:   "positive",
   157  			params: `["` + deploymentTxHash + `"]`,
   158  			result: func(e *executor) any { return &result.ApplicationLog{} },
   159  			check: func(t *testing.T, e *executor, acc any) {
   160  				res, ok := acc.(*result.ApplicationLog)
   161  				require.True(t, ok)
   162  				expectedTxHash, err := util.Uint256DecodeStringLE(deploymentTxHash)
   163  				require.NoError(t, err)
   164  				assert.Equal(t, 1, len(res.Executions))
   165  				assert.Equal(t, expectedTxHash, res.Container)
   166  				assert.Equal(t, trigger.Application, res.Executions[0].Trigger)
   167  				assert.Equal(t, vmstate.Halt, res.Executions[0].VMState)
   168  			},
   169  		},
   170  		{
   171  			name:   "positive, genesis block",
   172  			params: `["` + genesisBlockHash + `"]`,
   173  			result: func(e *executor) any { return &result.ApplicationLog{} },
   174  			check: func(t *testing.T, e *executor, acc any) {
   175  				res, ok := acc.(*result.ApplicationLog)
   176  				require.True(t, ok)
   177  				assert.Equal(t, genesisBlockHash, res.Container.StringLE())
   178  				assert.Equal(t, 2, len(res.Executions))
   179  				assert.Equal(t, trigger.OnPersist, res.Executions[0].Trigger)
   180  				assert.Equal(t, trigger.PostPersist, res.Executions[1].Trigger)
   181  				assert.Equal(t, vmstate.Halt, res.Executions[0].VMState)
   182  			},
   183  		},
   184  		{
   185  			name:   "positive, genesis block, postPersist",
   186  			params: `["` + genesisBlockHash + `", "PostPersist"]`,
   187  			result: func(e *executor) any { return &result.ApplicationLog{} },
   188  			check: func(t *testing.T, e *executor, acc any) {
   189  				res, ok := acc.(*result.ApplicationLog)
   190  				require.True(t, ok)
   191  				assert.Equal(t, genesisBlockHash, res.Container.StringLE())
   192  				assert.Equal(t, 1, len(res.Executions))
   193  				assert.Equal(t, trigger.PostPersist, res.Executions[0].Trigger)
   194  				assert.Equal(t, vmstate.Halt, res.Executions[0].VMState)
   195  			},
   196  		},
   197  		{
   198  			name:   "positive, genesis block, onPersist",
   199  			params: `["` + genesisBlockHash + `", "OnPersist"]`,
   200  			result: func(e *executor) any { return &result.ApplicationLog{} },
   201  			check: func(t *testing.T, e *executor, acc any) {
   202  				res, ok := acc.(*result.ApplicationLog)
   203  				require.True(t, ok)
   204  				assert.Equal(t, genesisBlockHash, res.Container.StringLE())
   205  				assert.Equal(t, 1, len(res.Executions))
   206  				assert.Equal(t, trigger.OnPersist, res.Executions[0].Trigger)
   207  				assert.Equal(t, vmstate.Halt, res.Executions[0].VMState)
   208  			},
   209  		},
   210  		{
   211  			name:    "invalid trigger (not a string)",
   212  			params:  `["` + genesisBlockHash + `", 1]`,
   213  			fail:    true,
   214  			errCode: neorpc.InvalidParamsCode,
   215  		},
   216  		{
   217  			name:    "no params",
   218  			params:  `[]`,
   219  			fail:    true,
   220  			errCode: neorpc.InvalidParamsCode,
   221  		},
   222  		{
   223  			name:    "invalid address",
   224  			params:  `["notahash"]`,
   225  			fail:    true,
   226  			errCode: neorpc.InvalidParamsCode,
   227  		},
   228  		{
   229  			name:    "invalid tx hash",
   230  			params:  `["d24cc1d52b5c0216cbf3835bb5bac8ccf32639fa1ab6627ec4e2b9f33f7ec02f"]`,
   231  			fail:    true,
   232  			errCode: neorpc.ErrUnknownScriptContainerCode,
   233  		},
   234  	},
   235  	"getcontractstate": {
   236  		{
   237  			name:   "positive, by hash",
   238  			params: fmt.Sprintf(`["%s"]`, testContractHash),
   239  			result: func(e *executor) any { return &state.Contract{} },
   240  			check: func(t *testing.T, e *executor, cs any) {
   241  				res, ok := cs.(*state.Contract)
   242  				require.True(t, ok)
   243  				assert.Equal(t, testContractHash, res.Hash.StringLE())
   244  			},
   245  		},
   246  		{
   247  			name:   "positive, by id",
   248  			params: `[1]`,
   249  			result: func(e *executor) any { return &state.Contract{} },
   250  			check: func(t *testing.T, e *executor, cs any) {
   251  				res, ok := cs.(*state.Contract)
   252  				require.True(t, ok)
   253  				assert.Equal(t, int32(1), res.ID)
   254  			},
   255  		},
   256  		{
   257  			name:   "positive, native by id",
   258  			params: `[-3]`,
   259  			result: func(e *executor) any { return &state.Contract{} },
   260  			check: func(t *testing.T, e *executor, cs any) {
   261  				res, ok := cs.(*state.Contract)
   262  				require.True(t, ok)
   263  				assert.Equal(t, int32(-3), res.ID)
   264  			},
   265  		},
   266  		{
   267  			name:   "positive, native by name",
   268  			params: `["PolicyContract"]`,
   269  			result: func(e *executor) any { return &state.Contract{} },
   270  			check: func(t *testing.T, e *executor, cs any) {
   271  				res, ok := cs.(*state.Contract)
   272  				require.True(t, ok)
   273  				assert.Equal(t, int32(-7), res.ID)
   274  			},
   275  		},
   276  		{
   277  			name:    "negative, bad hash",
   278  			params:  `["6d1eeca891ee93de2b7a77eb91c26f3b3c04d6c3"]`,
   279  			fail:    true,
   280  			errCode: neorpc.ErrUnknownContractCode,
   281  		},
   282  		{
   283  			name:    "negative, bad ID",
   284  			params:  `[-100]`,
   285  			fail:    true,
   286  			errCode: neorpc.ErrUnknownContractCode,
   287  		},
   288  		{
   289  			name:    "negative, bad native name",
   290  			params:  `["unknown_native"]`,
   291  			fail:    true,
   292  			errCode: neorpc.InvalidParamsCode,
   293  		},
   294  		{
   295  			name:    "no params",
   296  			params:  `[]`,
   297  			fail:    true,
   298  			errCode: neorpc.InvalidParamsCode,
   299  		},
   300  		{
   301  			name:    "invalid hash",
   302  			params:  `["notahex"]`,
   303  			fail:    true,
   304  			errCode: neorpc.InvalidParamsCode,
   305  		},
   306  	},
   307  	"getnep11balances": {
   308  		{
   309  			name:    "no params",
   310  			params:  `[]`,
   311  			fail:    true,
   312  			errCode: neorpc.InvalidParamsCode,
   313  		},
   314  		{
   315  			name:    "invalid address",
   316  			params:  `["notahex"]`,
   317  			fail:    true,
   318  			errCode: neorpc.InvalidParamsCode,
   319  		},
   320  		{
   321  			name:   "positive",
   322  			params: `["` + testchain.PrivateKeyByID(0).GetScriptHash().StringLE() + `"]`,
   323  			result: func(e *executor) any { return &result.NEP11Balances{} },
   324  			check:  checkNep11Balances,
   325  		},
   326  		{
   327  			name:   "positive_address",
   328  			params: `["` + address.Uint160ToString(testchain.PrivateKeyByID(0).GetScriptHash()) + `"]`,
   329  			result: func(e *executor) any { return &result.NEP11Balances{} },
   330  			check:  checkNep11Balances,
   331  		},
   332  	},
   333  	"getnep11properties": {
   334  		{
   335  			name:    "no params",
   336  			params:  `[]`,
   337  			fail:    true,
   338  			errCode: neorpc.InvalidParamsCode,
   339  		},
   340  		{
   341  			name:    "invalid address",
   342  			params:  `["notahex"]`,
   343  			fail:    true,
   344  			errCode: neorpc.InvalidParamsCode,
   345  		},
   346  		{
   347  			name:    "no token",
   348  			params:  `["` + nnsContractHash + `"]`,
   349  			fail:    true,
   350  			errCode: neorpc.InvalidParamsCode,
   351  		},
   352  		{
   353  			name:    "bad token",
   354  			params:  `["` + nnsContractHash + `", "abcdef"]`,
   355  			fail:    true,
   356  			errCode: neorpc.ErrExecutionFailedCode,
   357  		},
   358  		{
   359  			name:   "positive",
   360  			params: `["` + nnsContractHash + `", "6e656f2e636f6d"]`,
   361  			result: func(e *executor) any {
   362  				return &map[string]any{
   363  					"name":       "neo.com",
   364  					"expiration": "lhbLRl0B",
   365  					"admin":      nil,
   366  				}
   367  			},
   368  		},
   369  	},
   370  	"getnep11transfers": {
   371  		{
   372  			name:    "no params",
   373  			params:  `[]`,
   374  			fail:    true,
   375  			errCode: neorpc.InvalidParamsCode,
   376  		},
   377  		{
   378  			name:    "invalid address",
   379  			params:  `["notahex"]`,
   380  			fail:    true,
   381  			errCode: neorpc.InvalidParamsCode,
   382  		},
   383  		{
   384  			name:    "invalid timestamp",
   385  			params:  `["` + testchain.PrivateKeyByID(0).Address() + `", "notanumber"]`,
   386  			fail:    true,
   387  			errCode: neorpc.InvalidParamsCode,
   388  		},
   389  		{
   390  			name:    "invalid stop timestamp",
   391  			params:  `["` + testchain.PrivateKeyByID(0).Address() + `", "1", "blah"]`,
   392  			fail:    true,
   393  			errCode: neorpc.InvalidParamsCode,
   394  		},
   395  		{
   396  			name:   "positive",
   397  			params: `["` + testchain.PrivateKeyByID(0).Address() + `", 0]`,
   398  			result: func(e *executor) any { return &result.NEP11Transfers{} },
   399  			check:  checkNep11Transfers,
   400  		},
   401  	},
   402  	"getnep17balances": {
   403  		{
   404  			name:    "no params",
   405  			params:  `[]`,
   406  			fail:    true,
   407  			errCode: neorpc.InvalidParamsCode,
   408  		},
   409  		{
   410  			name:    "invalid address",
   411  			params:  `["notahex"]`,
   412  			fail:    true,
   413  			errCode: neorpc.InvalidParamsCode,
   414  		},
   415  		{
   416  			name:   "positive",
   417  			params: `["` + testchain.PrivateKeyByID(0).GetScriptHash().StringLE() + `"]`,
   418  			result: func(e *executor) any { return &result.NEP17Balances{} },
   419  			check:  checkNep17Balances,
   420  		},
   421  		{
   422  			name:   "positive_address",
   423  			params: `["` + address.Uint160ToString(testchain.PrivateKeyByID(0).GetScriptHash()) + `"]`,
   424  			result: func(e *executor) any { return &result.NEP17Balances{} },
   425  			check:  checkNep17Balances,
   426  		},
   427  	},
   428  	"getnep17transfers": {
   429  		{
   430  			name:    "no params",
   431  			params:  `[]`,
   432  			fail:    true,
   433  			errCode: neorpc.InvalidParamsCode,
   434  		},
   435  		{
   436  			name:    "invalid address",
   437  			params:  `["notahex"]`,
   438  			fail:    true,
   439  			errCode: neorpc.InvalidParamsCode,
   440  		},
   441  		{
   442  			name:    "invalid timestamp",
   443  			params:  `["` + testchain.PrivateKeyByID(0).Address() + `", "notanumber"]`,
   444  			fail:    true,
   445  			errCode: neorpc.InvalidParamsCode,
   446  		},
   447  		{
   448  			name:    "invalid stop timestamp",
   449  			params:  `["` + testchain.PrivateKeyByID(0).Address() + `", "1", "blah"]`,
   450  			fail:    true,
   451  			errCode: neorpc.InvalidParamsCode,
   452  		},
   453  		{
   454  			name:    "invalid limit",
   455  			params:  `["` + testchain.PrivateKeyByID(0).Address() + `", "1", "2", "0"]`,
   456  			fail:    true,
   457  			errCode: neorpc.InvalidParamsCode,
   458  		},
   459  		{
   460  			name:    "invalid limit 2",
   461  			params:  `["` + testchain.PrivateKeyByID(0).Address() + `", "1", "2", "bleh"]`,
   462  			fail:    true,
   463  			errCode: neorpc.InvalidParamsCode,
   464  		},
   465  		{
   466  			name:    "invalid limit 3",
   467  			params:  `["` + testchain.PrivateKeyByID(0).Address() + `", "1", "2", "100500"]`,
   468  			fail:    true,
   469  			errCode: neorpc.InvalidParamsCode,
   470  		},
   471  		{
   472  			name:    "invalid page",
   473  			params:  `["` + testchain.PrivateKeyByID(0).Address() + `", "1", "2", "3", "-1"]`,
   474  			fail:    true,
   475  			errCode: neorpc.InvalidParamsCode,
   476  		},
   477  		{
   478  			name:    "invalid page 2",
   479  			params:  `["` + testchain.PrivateKeyByID(0).Address() + `", "1", "2", "3", "jajaja"]`,
   480  			fail:    true,
   481  			errCode: neorpc.InvalidParamsCode,
   482  		},
   483  		{
   484  			name:   "positive",
   485  			params: `["` + testchain.PrivateKeyByID(0).Address() + `", 0]`,
   486  			result: func(e *executor) any { return &result.NEP17Transfers{} },
   487  			check:  checkNep17Transfers,
   488  		},
   489  		{
   490  			name:   "positive_hash",
   491  			params: `["` + testchain.PrivateKeyByID(0).GetScriptHash().StringLE() + `", 0]`,
   492  			result: func(e *executor) any { return &result.NEP17Transfers{} },
   493  			check:  checkNep17Transfers,
   494  		},
   495  	},
   496  	"getproof": {
   497  		{
   498  			name:    "no params",
   499  			params:  `[]`,
   500  			fail:    true,
   501  			errCode: neorpc.InvalidParamsCode,
   502  		},
   503  		{
   504  			name:    "invalid root",
   505  			params:  `["0xabcdef"]`,
   506  			fail:    true,
   507  			errCode: neorpc.InvalidParamsCode,
   508  		},
   509  		{
   510  			name:    "invalid contract",
   511  			params:  `["0000000000000000000000000000000000000000000000000000000000000000", "0xabcdef"]`,
   512  			fail:    true,
   513  			errCode: neorpc.InvalidParamsCode,
   514  		},
   515  		{
   516  			name:    "invalid key",
   517  			params:  `["0000000000000000000000000000000000000000000000000000000000000000", "` + testContractHash + `", "notahex"]`,
   518  			fail:    true,
   519  			errCode: neorpc.InvalidParamsCode,
   520  		},
   521  	},
   522  	"getstate": {
   523  		{
   524  			name:    "no params",
   525  			params:  `[]`,
   526  			fail:    true,
   527  			errCode: neorpc.InvalidParamsCode,
   528  		},
   529  		{
   530  			name:    "invalid root",
   531  			params:  `["0xabcdef"]`,
   532  			fail:    true,
   533  			errCode: neorpc.InvalidParamsCode,
   534  		},
   535  		{
   536  			name:    "invalid contract",
   537  			params:  `["0000000000000000000000000000000000000000000000000000000000000000", "0xabcdef"]`,
   538  			fail:    true,
   539  			errCode: neorpc.InvalidParamsCode,
   540  		},
   541  		{
   542  			name:    "invalid key",
   543  			params:  `["0000000000000000000000000000000000000000000000000000000000000000", "` + testContractHash + `", "notabase64%"]`,
   544  			fail:    true,
   545  			errCode: neorpc.InvalidParamsCode,
   546  		},
   547  		{
   548  			name:    "unknown contract",
   549  			params:  `["0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000", "QQ=="]`,
   550  			fail:    true,
   551  			errCode: neorpc.ErrUnknownContractCode,
   552  		},
   553  		{
   554  			name:    "unknown root/item",
   555  			params:  `["0000000000000000000000000000000000000000000000000000000000000000", "` + testContractHash + `", "QQ=="]`,
   556  			fail:    true,
   557  			errCode: neorpc.ErrUnknownContractCode,
   558  		},
   559  	},
   560  	"findstates": {
   561  		{
   562  			name:    "no params",
   563  			params:  `[]`,
   564  			fail:    true,
   565  			errCode: neorpc.InvalidParamsCode,
   566  		},
   567  		{
   568  			name:    "invalid root",
   569  			params:  `["0xabcdef"]`,
   570  			fail:    true,
   571  			errCode: neorpc.InvalidParamsCode,
   572  		},
   573  		{
   574  			name:    "invalid contract",
   575  			params:  `["` + block20StateRootLE + `", "0xabcdef"]`,
   576  			fail:    true,
   577  			errCode: neorpc.InvalidParamsCode,
   578  		},
   579  		{
   580  			name:    "invalid prefix",
   581  			params:  `["` + block20StateRootLE + `", "` + testContractHash + `", "notabase64%"]`,
   582  			fail:    true,
   583  			errCode: neorpc.InvalidParamsCode,
   584  		},
   585  		{
   586  			name:    "invalid key",
   587  			params:  `["` + block20StateRootLE + `", "` + testContractHash + `", "QQ==", "notabase64%"]`,
   588  			fail:    true,
   589  			errCode: neorpc.InvalidParamsCode,
   590  		},
   591  		{
   592  			name:    "unknown contract/large count",
   593  			params:  `["` + block20StateRootLE + `", "0000000000000000000000000000000000000000", "QQ==", "QQ==", 101]`,
   594  			fail:    true,
   595  			errCode: neorpc.ErrUnknownContractCode,
   596  		},
   597  	},
   598  	"getstateheight": {
   599  		{
   600  			name:   "positive",
   601  			params: `[]`,
   602  			result: func(_ *executor) any { return new(result.StateHeight) },
   603  			check: func(t *testing.T, e *executor, res any) {
   604  				sh, ok := res.(*result.StateHeight)
   605  				require.True(t, ok)
   606  
   607  				require.Equal(t, e.chain.BlockHeight(), sh.Local)
   608  				require.Equal(t, uint32(0), sh.Validated)
   609  			},
   610  		},
   611  	},
   612  	"getstateroot": {
   613  		{
   614  			name:    "no params",
   615  			params:  `[]`,
   616  			fail:    true,
   617  			errCode: neorpc.InvalidParamsCode,
   618  		},
   619  		{
   620  			name:    "invalid hash",
   621  			params:  `["0x1234567890"]`,
   622  			fail:    true,
   623  			errCode: neorpc.ErrUnknownStateRootCode,
   624  		},
   625  	},
   626  	"getstorage": {
   627  		{
   628  			name:   "positive",
   629  			params: fmt.Sprintf(`["%s", "dGVzdGtleQ=="]`, testContractHash),
   630  			result: func(e *executor) any {
   631  				v := base64.StdEncoding.EncodeToString([]byte("newtestvalue"))
   632  				return &v
   633  			},
   634  		},
   635  		{
   636  			name:    "missing key",
   637  			params:  fmt.Sprintf(`["%s", "dGU="]`, testContractHash),
   638  			fail:    true,
   639  			errCode: neorpc.ErrUnknownStorageItemCode,
   640  		},
   641  		{
   642  			name:    "no params",
   643  			params:  `[]`,
   644  			fail:    true,
   645  			errCode: neorpc.InvalidParamsCode,
   646  		},
   647  		{
   648  			name:    "no second parameter",
   649  			params:  fmt.Sprintf(`["%s"]`, testContractHash),
   650  			fail:    true,
   651  			errCode: neorpc.InvalidParamsCode,
   652  		},
   653  		{
   654  			name:    "invalid hash",
   655  			params:  `["notahex"]`,
   656  			fail:    true,
   657  			errCode: neorpc.InvalidParamsCode,
   658  		},
   659  		{
   660  			name:    "invalid key",
   661  			params:  fmt.Sprintf(`["%s", "notabase64$"]`, testContractHash),
   662  			fail:    true,
   663  			errCode: neorpc.InvalidParamsCode,
   664  		},
   665  	},
   666  	"getstoragehistoric": {
   667  		{
   668  			name:   "positive",
   669  			params: fmt.Sprintf(`["%s", "%s", "%s"]`, block20StateRootLE, testContractHash, base64.StdEncoding.EncodeToString([]byte("aa10"))),
   670  			result: func(e *executor) any {
   671  				v := base64.StdEncoding.EncodeToString([]byte("v2"))
   672  				return &v
   673  			},
   674  		},
   675  		{
   676  			name:    "missing key",
   677  			params:  fmt.Sprintf(`["%s", "%s", "dGU="]`, block20StateRootLE, testContractHash),
   678  			fail:    true,
   679  			errCode: neorpc.ErrUnknownStorageItemCode,
   680  		},
   681  		{
   682  			name:    "no params",
   683  			params:  `[]`,
   684  			fail:    true,
   685  			errCode: neorpc.InvalidParamsCode,
   686  		},
   687  		{
   688  			name:    "no second parameter",
   689  			params:  fmt.Sprintf(`["%s"]`, block20StateRootLE),
   690  			fail:    true,
   691  			errCode: neorpc.InvalidParamsCode,
   692  		},
   693  		{
   694  			name:    "no third parameter",
   695  			params:  fmt.Sprintf(`["%s", "%s"]`, block20StateRootLE, testContractHash),
   696  			fail:    true,
   697  			errCode: neorpc.InvalidParamsCode,
   698  		},
   699  		{
   700  			name:    "invalid stateroot",
   701  			params:  `["notahex"]`,
   702  			fail:    true,
   703  			errCode: neorpc.InvalidParamsCode,
   704  		},
   705  		{
   706  			name:    "invalid hash",
   707  			params:  fmt.Sprintf(`["%s", "notahex"]`, block20StateRootLE),
   708  			fail:    true,
   709  			errCode: neorpc.InvalidParamsCode,
   710  		},
   711  		{
   712  			name:    "invalid key",
   713  			params:  fmt.Sprintf(`["%s", "%s", "notabase64$"]`, block20StateRootLE, testContractHash),
   714  			fail:    true,
   715  			errCode: neorpc.InvalidParamsCode,
   716  		},
   717  	},
   718  	"findstorage": {
   719  		{
   720  			name:   "not truncated",
   721  			params: fmt.Sprintf(`["%s", "%s"]`, testContractHash, base64.StdEncoding.EncodeToString([]byte("aa1"))),
   722  			result: func(_ *executor) any { return new(result.FindStorage) },
   723  			check: func(t *testing.T, e *executor, res any) {
   724  				actual, ok := res.(*result.FindStorage)
   725  				require.True(t, ok)
   726  
   727  				expected := &result.FindStorage{
   728  					Results: []result.KeyValue{
   729  						{
   730  							Key:   []byte("aa10"),
   731  							Value: []byte("v2"),
   732  						},
   733  					},
   734  					Next:      1,
   735  					Truncated: false,
   736  				}
   737  				require.Equal(t, expected, actual)
   738  			},
   739  		},
   740  		{
   741  			name:   "truncated first page",
   742  			params: fmt.Sprintf(`["%s", "%s"]`, testContractHash, base64.StdEncoding.EncodeToString([]byte("aa"))),
   743  			result: func(_ *executor) any { return new(result.FindStorage) },
   744  			check: func(t *testing.T, e *executor, res any) {
   745  				actual, ok := res.(*result.FindStorage)
   746  				require.True(t, ok)
   747  
   748  				expected := &result.FindStorage{
   749  					Results: []result.KeyValue{
   750  						{
   751  							Key:   []byte("aa"),
   752  							Value: []byte("v1"),
   753  						},
   754  						{
   755  							Key:   []byte("aa10"),
   756  							Value: []byte("v2"),
   757  						},
   758  					},
   759  					Next:      2,
   760  					Truncated: true,
   761  				}
   762  				require.Equal(t, expected, actual)
   763  			},
   764  		},
   765  		{
   766  			name:   "truncated second page",
   767  			params: fmt.Sprintf(`["%s", "%s", 2]`, testContractHash, base64.StdEncoding.EncodeToString([]byte("aa"))),
   768  			result: func(_ *executor) any { return new(result.FindStorage) },
   769  			check: func(t *testing.T, e *executor, res any) {
   770  				actual, ok := res.(*result.FindStorage)
   771  				require.True(t, ok)
   772  
   773  				expected := &result.FindStorage{
   774  					Results: []result.KeyValue{
   775  						{
   776  							Key:   []byte("aa50"),
   777  							Value: []byte("v3"),
   778  						},
   779  					},
   780  					Next:      3,
   781  					Truncated: false,
   782  				}
   783  				require.Equal(t, expected, actual)
   784  			},
   785  		},
   786  		{
   787  			name:   "empty prefix",
   788  			params: fmt.Sprintf(`["%s", ""]`, storageContractHash),
   789  			result: func(_ *executor) any { return new(result.FindStorage) },
   790  			check: func(t *testing.T, e *executor, res any) {
   791  				actual, ok := res.(*result.FindStorage)
   792  				require.True(t, ok)
   793  
   794  				expected := &result.FindStorage{
   795  					Results: []result.KeyValue{
   796  						{
   797  							Key:   []byte{0x01, 0x00},
   798  							Value: []byte{},
   799  						},
   800  						{
   801  							Key:   []byte{0x01, 0x01},
   802  							Value: []byte{0x01},
   803  						},
   804  					},
   805  					Next:      2,
   806  					Truncated: true,
   807  				}
   808  				require.Equal(t, expected, actual)
   809  			},
   810  		},
   811  		{
   812  			name:   "unknown key",
   813  			params: fmt.Sprintf(`["%s", "%s"]`, testContractHash, base64.StdEncoding.EncodeToString([]byte("unknown-key"))),
   814  			result: func(_ *executor) any { return new(result.FindStorage) },
   815  			check: func(t *testing.T, e *executor, res any) {
   816  				actual, ok := res.(*result.FindStorage)
   817  				require.True(t, ok)
   818  
   819  				expected := &result.FindStorage{
   820  					Results:   []result.KeyValue{},
   821  					Next:      0,
   822  					Truncated: false,
   823  				}
   824  				require.Equal(t, expected, actual)
   825  			},
   826  		},
   827  		{
   828  			name:    "no params",
   829  			params:  `[]`,
   830  			fail:    true,
   831  			errCode: neorpc.InvalidParamsCode,
   832  		},
   833  		{
   834  			name:    "no second parameter",
   835  			params:  fmt.Sprintf(`["%s"]`, testContractHash),
   836  			fail:    true,
   837  			errCode: neorpc.InvalidParamsCode,
   838  		},
   839  		{
   840  			name:    "invalid hash",
   841  			params:  `["notahex"]`,
   842  			fail:    true,
   843  			errCode: neorpc.InvalidParamsCode,
   844  		},
   845  		{
   846  			name:    "invalid key",
   847  			params:  fmt.Sprintf(`["%s", "notabase64$"]`, testContractHash),
   848  			fail:    true,
   849  			errCode: neorpc.InvalidParamsCode,
   850  		},
   851  		{
   852  			name:    "invalid page",
   853  			params:  fmt.Sprintf(`["%s", "", "not-an-int"]`, testContractHash),
   854  			fail:    true,
   855  			errCode: neorpc.InvalidParamsCode,
   856  		},
   857  	},
   858  	"findstoragehistoric": {
   859  		{
   860  			name:   "not truncated",
   861  			params: fmt.Sprintf(`["%s", "%s", "%s"]`, block20StateRootLE, testContractHash, base64.StdEncoding.EncodeToString([]byte("aa1"))),
   862  			result: func(_ *executor) any { return new(result.FindStorage) },
   863  			check: func(t *testing.T, e *executor, res any) {
   864  				actual, ok := res.(*result.FindStorage)
   865  				require.True(t, ok)
   866  
   867  				expected := &result.FindStorage{
   868  					Results: []result.KeyValue{
   869  						{
   870  							Key:   []byte("aa10"),
   871  							Value: []byte("v2"),
   872  						},
   873  					},
   874  					Next:      1,
   875  					Truncated: false,
   876  				}
   877  				require.Equal(t, expected, actual)
   878  			},
   879  		},
   880  		{
   881  			name:   "truncated first page",
   882  			params: fmt.Sprintf(`["%s", "%s", "%s"]`, block20StateRootLE, testContractHash, base64.StdEncoding.EncodeToString([]byte("aa"))),
   883  			result: func(_ *executor) any { return new(result.FindStorage) },
   884  			check: func(t *testing.T, e *executor, res any) {
   885  				actual, ok := res.(*result.FindStorage)
   886  				require.True(t, ok)
   887  
   888  				expected := &result.FindStorage{
   889  					Results: []result.KeyValue{
   890  						{
   891  							Key:   []byte("aa10"), // items traversal order may differ from the one provided by `findstorage` due to MPT traversal strategy.
   892  							Value: []byte("v2"),
   893  						},
   894  						{
   895  							Key:   []byte("aa50"),
   896  							Value: []byte("v3"),
   897  						},
   898  					},
   899  					Next:      2,
   900  					Truncated: true,
   901  				}
   902  				require.Equal(t, expected, actual)
   903  			},
   904  		},
   905  		{
   906  			name:   "truncated second page",
   907  			params: fmt.Sprintf(`["%s","%s", "%s", 2]`, block20StateRootLE, testContractHash, base64.StdEncoding.EncodeToString([]byte("aa"))),
   908  			result: func(_ *executor) any { return new(result.FindStorage) },
   909  			check: func(t *testing.T, e *executor, res any) {
   910  				actual, ok := res.(*result.FindStorage)
   911  				require.True(t, ok)
   912  
   913  				expected := &result.FindStorage{
   914  					Results: []result.KeyValue{
   915  						{
   916  							Key:   []byte("aa"),
   917  							Value: []byte("v1"),
   918  						},
   919  					},
   920  					Next:      3,
   921  					Truncated: false,
   922  				}
   923  				require.Equal(t, expected, actual)
   924  			},
   925  		},
   926  		{
   927  			name:   "empty prefix",
   928  			params: fmt.Sprintf(`["%s", "%s", ""]`, block20StateRootLE, nnsContractHash),
   929  			result: func(_ *executor) any { return new(result.FindStorage) },
   930  			check: func(t *testing.T, e *executor, res any) {
   931  				actual, ok := res.(*result.FindStorage)
   932  				require.True(t, ok)
   933  
   934  				expected := &result.FindStorage{
   935  					Results: []result.KeyValue{
   936  						{
   937  							Key:   []byte{0x00}, // total supply
   938  							Value: []byte{0x01},
   939  						},
   940  						{
   941  							Key:   append([]byte{0x01}, testchain.PrivateKeyByID(0).GetScriptHash().BytesBE()...), // balance of priv0
   942  							Value: []byte{0x01},
   943  						},
   944  					},
   945  					Next:      2,
   946  					Truncated: true,
   947  				}
   948  				require.Equal(t, expected, actual)
   949  			},
   950  		},
   951  		{
   952  			name:   "unknown key",
   953  			params: fmt.Sprintf(`["%s", "%s", "%s"]`, block20StateRootLE, testContractHash, base64.StdEncoding.EncodeToString([]byte("unknown-key"))),
   954  			result: func(_ *executor) any { return new(result.FindStorage) },
   955  			check: func(t *testing.T, e *executor, res any) {
   956  				actual, ok := res.(*result.FindStorage)
   957  				require.True(t, ok)
   958  
   959  				expected := &result.FindStorage{
   960  					Results:   []result.KeyValue{},
   961  					Next:      0,
   962  					Truncated: false,
   963  				}
   964  				require.Equal(t, expected, actual)
   965  			},
   966  		},
   967  		{
   968  			name:    "no params",
   969  			params:  `[]`,
   970  			fail:    true,
   971  			errCode: neorpc.InvalidParamsCode,
   972  		},
   973  		{
   974  			name:    "invalid stateroot",
   975  			params:  `[12345]`,
   976  			fail:    true,
   977  			errCode: neorpc.InvalidParamsCode,
   978  		},
   979  		{
   980  			name:    "no second parameter",
   981  			params:  fmt.Sprintf(`["%s"]`, block20StateRootLE),
   982  			fail:    true,
   983  			errCode: neorpc.InvalidParamsCode,
   984  		},
   985  		{
   986  			name:    "no third parameter",
   987  			params:  fmt.Sprintf(`["%s", "%s"]`, block20StateRootLE, testContractHash),
   988  			fail:    true,
   989  			errCode: neorpc.InvalidParamsCode,
   990  		},
   991  		{
   992  			name:    "invalid hash",
   993  			params:  fmt.Sprintf(`["%s", "notahex"]`, block20StateRootLE),
   994  			fail:    true,
   995  			errCode: neorpc.InvalidParamsCode,
   996  		},
   997  		{
   998  			name:    "invalid key",
   999  			params:  fmt.Sprintf(`["%s", "%s", "notabase64$"]`, block20StateRootLE, testContractHash),
  1000  			fail:    true,
  1001  			errCode: neorpc.InvalidParamsCode,
  1002  		},
  1003  		{
  1004  			name:    "invalid page",
  1005  			params:  fmt.Sprintf(`["%s", "%s", "", "not-an-int"]`, block20StateRootLE, testContractHash),
  1006  			fail:    true,
  1007  			errCode: neorpc.InvalidParamsCode,
  1008  		},
  1009  	},
  1010  	"getbestblockhash": {
  1011  		{
  1012  			params: "[]",
  1013  			result: func(e *executor) any {
  1014  				v := "0x" + e.chain.CurrentBlockHash().StringLE()
  1015  				return &v
  1016  			},
  1017  		},
  1018  	},
  1019  	"getblock": {
  1020  		{
  1021  			name:   "positive",
  1022  			params: "[3, 1]",
  1023  			result: func(_ *executor) any { return &result.Block{} },
  1024  			check: func(t *testing.T, e *executor, blockRes any) {
  1025  				res, ok := blockRes.(*result.Block)
  1026  				require.True(t, ok)
  1027  
  1028  				block, err := e.chain.GetBlock(e.chain.GetHeaderHash(3))
  1029  				require.NoErrorf(t, err, "could not get block")
  1030  
  1031  				assert.Equal(t, block.Hash(), res.Hash())
  1032  				for i, tx := range res.Transactions {
  1033  					actualTx := block.Transactions[i]
  1034  					require.True(t, ok)
  1035  					require.Equal(t, actualTx.Nonce, tx.Nonce)
  1036  					require.Equal(t, block.Transactions[i].Hash(), tx.Hash())
  1037  				}
  1038  			},
  1039  		},
  1040  		{
  1041  			name:    "no params",
  1042  			params:  `[]`,
  1043  			fail:    true,
  1044  			errCode: neorpc.InvalidParamsCode,
  1045  		},
  1046  		{
  1047  			name:    "bad params",
  1048  			params:  `[[]]`,
  1049  			fail:    true,
  1050  			errCode: neorpc.InvalidParamsCode,
  1051  		},
  1052  		{
  1053  			name:    "invalid height",
  1054  			params:  `[-1]`,
  1055  			fail:    true,
  1056  			errCode: neorpc.ErrUnknownHeightCode,
  1057  		},
  1058  		{
  1059  			name:    "invalid hash",
  1060  			params:  `["notahex"]`,
  1061  			fail:    true,
  1062  			errCode: neorpc.InvalidParamsCode,
  1063  		},
  1064  		{
  1065  			name:    "missing hash",
  1066  			params:  `["` + util.Uint256{}.String() + `"]`,
  1067  			fail:    true,
  1068  			errCode: neorpc.ErrUnknownBlockCode,
  1069  		},
  1070  	},
  1071  	"getblockcount": {
  1072  		{
  1073  			params: "[]",
  1074  			result: func(e *executor) any {
  1075  				v := int(e.chain.BlockHeight() + 1)
  1076  				return &v
  1077  			},
  1078  		},
  1079  	},
  1080  	"getblockhash": {
  1081  		{
  1082  			params: "[1]",
  1083  			result: func(e *executor) any {
  1084  				// We don't have `t` here for proper handling, but
  1085  				// error here would lead to panic down below.
  1086  				block, _ := e.chain.GetBlock(e.chain.GetHeaderHash(1))
  1087  				expectedHash := "0x" + block.Hash().StringLE()
  1088  				return &expectedHash
  1089  			},
  1090  		},
  1091  		{
  1092  			name:    "string height",
  1093  			params:  `["first"]`,
  1094  			fail:    true,
  1095  			errCode: neorpc.InvalidParamsCode,
  1096  		},
  1097  		{
  1098  			name:    "invalid number height",
  1099  			params:  `[-2]`,
  1100  			fail:    true,
  1101  			errCode: neorpc.ErrUnknownHeightCode,
  1102  		},
  1103  	},
  1104  	"getblockheader": {
  1105  		{
  1106  			name:    "invalid verbose type",
  1107  			params:  `["9673799c5b5a294427401cb07d6cc615ada3a0d5c5bf7ed6f0f54f24abb2e2ac", true]`,
  1108  			fail:    true,
  1109  			errCode: neorpc.ErrUnknownBlockCode,
  1110  		},
  1111  		{
  1112  			name:    "invalid block hash",
  1113  			params:  `["notahash"]`,
  1114  			fail:    true,
  1115  			errCode: neorpc.InvalidParamsCode,
  1116  		},
  1117  		{
  1118  			name:    "unknown block",
  1119  			params:  `["a6e526375a780335112299f2262501e5e9574c3ba61b16bbc1e282b344f6c141"]`,
  1120  			fail:    true,
  1121  			errCode: neorpc.ErrUnknownBlockCode,
  1122  		},
  1123  		{
  1124  			name:    "no params",
  1125  			params:  `[]`,
  1126  			fail:    true,
  1127  			errCode: neorpc.InvalidParamsCode,
  1128  		},
  1129  	},
  1130  	"getblockheadercount": {
  1131  		{
  1132  			params: "[]",
  1133  			result: func(e *executor) any {
  1134  				v := int(e.chain.HeaderHeight() + 1)
  1135  				return &v
  1136  			},
  1137  		},
  1138  	},
  1139  	"getblocksysfee": {
  1140  		{
  1141  			name:   "positive",
  1142  			params: "[1]",
  1143  			result: func(e *executor) any {
  1144  				block, _ := e.chain.GetBlock(e.chain.GetHeaderHash(1))
  1145  
  1146  				var expectedBlockSysFee int64
  1147  				for _, tx := range block.Transactions {
  1148  					expectedBlockSysFee += tx.SystemFee
  1149  				}
  1150  				return &expectedBlockSysFee
  1151  			},
  1152  		},
  1153  		{
  1154  			name:    "no params",
  1155  			params:  `[]`,
  1156  			fail:    true,
  1157  			errCode: neorpc.InvalidParamsCode,
  1158  		},
  1159  		{
  1160  			name:    "string height",
  1161  			params:  `["first"]`,
  1162  			fail:    true,
  1163  			errCode: neorpc.InvalidParamsCode,
  1164  		},
  1165  		{
  1166  			name:    "invalid number height",
  1167  			params:  `[-2]`,
  1168  			fail:    true,
  1169  			errCode: neorpc.ErrUnknownHeightCode,
  1170  		},
  1171  	},
  1172  	"getcommittee": {
  1173  		{
  1174  			params: "[]",
  1175  			result: func(e *executor) any {
  1176  				expected, _ := e.chain.GetCommittee()
  1177  				sort.Sort(expected)
  1178  				return &expected
  1179  			},
  1180  		},
  1181  	},
  1182  	"getconnectioncount": {
  1183  		{
  1184  			params: "[]",
  1185  			result: func(*executor) any {
  1186  				v := 0
  1187  				return &v
  1188  			},
  1189  		},
  1190  	},
  1191  	"getnativecontracts": {
  1192  		{
  1193  			params: "[]",
  1194  			result: func(e *executor) any {
  1195  				return new([]state.Contract)
  1196  			},
  1197  			check: func(t *testing.T, e *executor, res any) {
  1198  				lst := res.(*[]state.Contract)
  1199  				for i := range *lst {
  1200  					cs := e.chain.GetContractState((*lst)[i].Hash)
  1201  					require.Equal(t, *cs, (*lst)[i])
  1202  					require.True(t, cs.ID <= 0)
  1203  				}
  1204  			},
  1205  		},
  1206  	},
  1207  	"getpeers": {
  1208  		{
  1209  			params: "[]",
  1210  			result: func(*executor) any {
  1211  				return &result.GetPeers{
  1212  					Unconnected: []result.Peer{},
  1213  					Connected:   []result.Peer{},
  1214  					Bad:         []result.Peer{},
  1215  				}
  1216  			},
  1217  		},
  1218  	},
  1219  	"getrawtransaction": {
  1220  		{
  1221  			name:    "no params",
  1222  			params:  `[]`,
  1223  			fail:    true,
  1224  			errCode: neorpc.InvalidParamsCode,
  1225  		},
  1226  		{
  1227  			name:    "invalid hash",
  1228  			params:  `["notahex"]`,
  1229  			fail:    true,
  1230  			errCode: neorpc.InvalidParamsCode,
  1231  		},
  1232  		{
  1233  			name:    "missing hash",
  1234  			params:  `["` + util.Uint256{}.String() + `"]`,
  1235  			fail:    true,
  1236  			errCode: neorpc.ErrUnknownTransactionCode,
  1237  		},
  1238  	},
  1239  	"gettransactionheight": {
  1240  		{
  1241  			name:   "positive",
  1242  			params: `["` + deploymentTxHash + `"]`,
  1243  			result: func(e *executor) any {
  1244  				h := 0
  1245  				return &h
  1246  			},
  1247  			check: func(t *testing.T, e *executor, resp any) {
  1248  				h, ok := resp.(*int)
  1249  				require.True(t, ok)
  1250  				assert.Equal(t, 2, *h)
  1251  			},
  1252  		},
  1253  		{
  1254  			name:    "no params",
  1255  			params:  `[]`,
  1256  			fail:    true,
  1257  			errCode: neorpc.InvalidParamsCode,
  1258  		},
  1259  		{
  1260  			name:    "invalid hash",
  1261  			params:  `["notahex"]`,
  1262  			fail:    true,
  1263  			errCode: neorpc.InvalidParamsCode,
  1264  		},
  1265  		{
  1266  			name:    "missing hash",
  1267  			params:  `["` + util.Uint256{}.String() + `"]`,
  1268  			fail:    true,
  1269  			errCode: neorpc.ErrUnknownTransactionCode,
  1270  		},
  1271  	},
  1272  	"getunclaimedgas": {
  1273  		{
  1274  			name:    "no params",
  1275  			params:  "[]",
  1276  			fail:    true,
  1277  			errCode: neorpc.InvalidParamsCode,
  1278  		},
  1279  		{
  1280  			name:    "invalid address",
  1281  			params:  `["invalid"]`,
  1282  			fail:    true,
  1283  			errCode: neorpc.InvalidParamsCode,
  1284  		},
  1285  		{
  1286  			name:   "positive",
  1287  			params: `["` + testchain.MultisigAddress() + `"]`,
  1288  			result: func(*executor) any {
  1289  				return &result.UnclaimedGas{}
  1290  			},
  1291  			check: func(t *testing.T, e *executor, resp any) {
  1292  				actual, ok := resp.(*result.UnclaimedGas)
  1293  				require.True(t, ok)
  1294  				expected := result.UnclaimedGas{
  1295  					Address:   testchain.MultisigScriptHash(),
  1296  					Unclaimed: *big.NewInt(11500),
  1297  				}
  1298  				assert.Equal(t, expected, *actual)
  1299  			},
  1300  		},
  1301  	},
  1302  	"getcandidates": {
  1303  		{
  1304  			params: "[]",
  1305  			result: func(*executor) any {
  1306  				return &[]result.Candidate{}
  1307  			},
  1308  		},
  1309  	},
  1310  	"getnextblockvalidators": {
  1311  		{
  1312  			params: "[]",
  1313  			result: func(*executor) any {
  1314  				return &[]result.Validator{}
  1315  			},
  1316  			/* preview3 doesn't return any validators until there is a vote
  1317  			check: func(t *testing.T, e *executor, validators any) {
  1318  				var expected []result.Validator
  1319  				sBValidators := e.chain.GetStandByValidators()
  1320  				for _, sbValidator := range sBValidators {
  1321  					expected = append(expected, result.Validator{
  1322  						PublicKey: *sbValidator,
  1323  						Votes:     0,
  1324  						Active:    true,
  1325  					})
  1326  				}
  1327  
  1328  				actual, ok := validators.(*[]result.Validator)
  1329  				require.True(t, ok)
  1330  
  1331  				assert.ElementsMatch(t, expected, *actual)
  1332  			},
  1333  			*/
  1334  		},
  1335  	},
  1336  	"getversion": {
  1337  		{
  1338  			params: "[]",
  1339  			result: func(*executor) any { return &result.Version{} },
  1340  			check: func(t *testing.T, e *executor, ver any) {
  1341  				resp, ok := ver.(*result.Version)
  1342  				require.True(t, ok)
  1343  				require.Equal(t, "/NEO-GO:0.98.6-test/", resp.UserAgent)
  1344  
  1345  				cfg := e.chain.GetConfig()
  1346  				require.EqualValues(t, address.NEO3Prefix, resp.Protocol.AddressVersion)
  1347  				require.EqualValues(t, cfg.Magic, resp.Protocol.Network)
  1348  				require.EqualValues(t, cfg.TimePerBlock/time.Millisecond, resp.Protocol.MillisecondsPerBlock)
  1349  				require.EqualValues(t, cfg.MaxTraceableBlocks, resp.Protocol.MaxTraceableBlocks)
  1350  				require.EqualValues(t, cfg.MaxValidUntilBlockIncrement, resp.Protocol.MaxValidUntilBlockIncrement)
  1351  				require.EqualValues(t, cfg.MaxTransactionsPerBlock, resp.Protocol.MaxTransactionsPerBlock)
  1352  				require.EqualValues(t, cfg.MemPoolSize, resp.Protocol.MemoryPoolMaxTransactions)
  1353  				require.EqualValues(t, cfg.ValidatorsCount, resp.Protocol.ValidatorsCount)
  1354  				require.EqualValues(t, cfg.InitialGASSupply, resp.Protocol.InitialGasDistribution)
  1355  
  1356  				require.Equal(t, 0, len(resp.Protocol.CommitteeHistory))
  1357  				require.True(t, resp.Protocol.P2PSigExtensions) // Yeah, notary is enabled.
  1358  				require.False(t, resp.Protocol.StateRootInHeader)
  1359  				require.Equal(t, 0, len(resp.Protocol.ValidatorsHistory))
  1360  			},
  1361  		},
  1362  	},
  1363  	"invokefunction": {
  1364  		{
  1365  			name:   "positive",
  1366  			params: `["50befd26fdf6e4d957c11e078b24ebce6291456f", "test", []]`,
  1367  			result: func(e *executor) any { return &result.Invoke{} },
  1368  			check: func(t *testing.T, e *executor, inv any) {
  1369  				res, ok := inv.(*result.Invoke)
  1370  				require.True(t, ok)
  1371  				assert.NotNil(t, res.Script)
  1372  				assert.NotEqual(t, "", res.State)
  1373  				assert.NotEqual(t, 0, res.GasConsumed)
  1374  			},
  1375  		},
  1376  		{
  1377  			name:   "positive, with notifications",
  1378  			params: `["` + nnsContractHash + `", "transfer", [{"type":"Hash160", "value":"0x0bcd2978634d961c24f5aea0802297ff128724d6"},{"type":"String", "value":"neo.com"},{"type":"Any", "value":null}],["0xb248508f4ef7088e10c48f14d04be3272ca29eee"]]`,
  1379  			result: func(e *executor) any {
  1380  				script := append([]byte{0x0b, 0x0c, 0x07, 0x6e, 0x65, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x0c, 0x14, 0xd6, 0x24, 0x87, 0x12, 0xff, 0x97, 0x22, 0x80, 0xa0, 0xae, 0xf5, 0x24, 0x1c, 0x96, 0x4d, 0x63, 0x78, 0x29, 0xcd, 0xb, 0x13, 0xc0, 0x1f, 0xc, 0x8, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0xc, 0x14}, nnsHash.BytesBE()...)
  1381  				script = append(script, 0x41, 0x62, 0x7d, 0x5b, 0x52)
  1382  				return &result.Invoke{
  1383  					State:       "HALT",
  1384  					GasConsumed: 31922970,
  1385  					Script:      script,
  1386  					Stack:       []stackitem.Item{stackitem.Make(true)},
  1387  					Notifications: []state.NotificationEvent{{
  1388  						ScriptHash: nnsHash,
  1389  						Name:       "Transfer",
  1390  						Item: stackitem.NewArray([]stackitem.Item{
  1391  							stackitem.Make([]byte{0xee, 0x9e, 0xa2, 0x2c, 0x27, 0xe3, 0x4b, 0xd0, 0x14, 0x8f, 0xc4, 0x10, 0x8e, 0x08, 0xf7, 0x4e, 0x8f, 0x50, 0x48, 0xb2}),
  1392  							stackitem.Make([]byte{0xd6, 0x24, 0x87, 0x12, 0xff, 0x97, 0x22, 0x80, 0xa0, 0xae, 0xf5, 0x24, 0x1c, 0x96, 0x4d, 0x63, 0x78, 0x29, 0xcd, 0x0b}),
  1393  							stackitem.Make(1),
  1394  							stackitem.Make("neo.com"),
  1395  						}),
  1396  					}},
  1397  				}
  1398  			},
  1399  		},
  1400  		{
  1401  			name:   "positive, with storage changes",
  1402  			params: `["0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5", "transfer", [{"type":"Hash160", "value":"0xb248508f4ef7088e10c48f14d04be3272ca29eee"},{"type":"Hash160", "value":"0x0bcd2978634d961c24f5aea0802297ff128724d6"},{"type":"Integer", "value":1},{"type":"Any", "value":null}],["0xb248508f4ef7088e10c48f14d04be3272ca29eee"],true]`,
  1403  			result: func(e *executor) any { return &result.Invoke{} },
  1404  			check: func(t *testing.T, e *executor, inv any) {
  1405  				res, ok := inv.(*result.Invoke)
  1406  				require.True(t, ok)
  1407  				assert.NotNil(t, res.Script)
  1408  				assert.Equal(t, "HALT", res.State)
  1409  				assert.Equal(t, []stackitem.Item{stackitem.Make(true)}, res.Stack)
  1410  				assert.NotEqual(t, 0, res.GasConsumed)
  1411  				chg := []dboper.Operation{{
  1412  					State: "Changed",
  1413  					Key:   []byte{0xfa, 0xff, 0xff, 0xff, 0xb},
  1414  					Value: []byte{0x54, 0xb2, 0xd2, 0xa3, 0x51, 0x79, 0x12},
  1415  				}, {
  1416  					State: "Added",
  1417  					Key:   []byte{0xfb, 0xff, 0xff, 0xff, 0x14, 0xd6, 0x24, 0x87, 0x12, 0xff, 0x97, 0x22, 0x80, 0xa0, 0xae, 0xf5, 0x24, 0x1c, 0x96, 0x4d, 0x63, 0x78, 0x29, 0xcd, 0xb},
  1418  					Value: []byte{0x41, 0x04, 0x21, 0x01, 0x01, 0x21, 0x01, 0x18, 0x00, 0x21, 0x00},
  1419  				}, {
  1420  					State: "Changed",
  1421  					Key:   []byte{0xfb, 0xff, 0xff, 0xff, 0x14, 0xee, 0x9e, 0xa2, 0x2c, 0x27, 0xe3, 0x4b, 0xd0, 0x14, 0x8f, 0xc4, 0x10, 0x8e, 0x8, 0xf7, 0x4e, 0x8f, 0x50, 0x48, 0xb2},
  1422  					Value: []byte{0x41, 0x04, 0x21, 0x04, 0x2f, 0xd9, 0xf5, 0x05, 0x21, 0x01, 0x18, 0x00, 0x21, 0x00},
  1423  				}, {
  1424  					State: "Changed",
  1425  					Key:   []byte{0xfa, 0xff, 0xff, 0xff, 0x14, 0xee, 0x9e, 0xa2, 0x2c, 0x27, 0xe3, 0x4b, 0xd0, 0x14, 0x8f, 0xc4, 0x10, 0x8e, 0x8, 0xf7, 0x4e, 0x8f, 0x50, 0x48, 0xb2},
  1426  					Value: []byte{0x41, 0x01, 0x21, 0x05, 0x0c, 0x76, 0x4f, 0xdf, 0x08},
  1427  				}}
  1428  				// Can be returned in any order.
  1429  				assert.ElementsMatch(t, chg, res.Diagnostics.Changes)
  1430  			},
  1431  		},
  1432  		{
  1433  			name:   "positive, verbose",
  1434  			params: `["` + nnsContractHash + `", "resolve", [{"type":"String", "value":"neo.com"},{"type":"Integer","value":1}], [], true]`,
  1435  			result: func(e *executor) any {
  1436  				script := append([]byte{0x11, 0xc, 0x7, 0x6e, 0x65, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x12, 0xc0, 0x1f, 0xc, 0x7, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0xc, 0x14}, nnsHash.BytesBE()...)
  1437  				script = append(script, 0x41, 0x62, 0x7d, 0x5b, 0x52)
  1438  				stdHash, _ := e.chain.GetNativeContractScriptHash(nativenames.StdLib)
  1439  				cryptoHash, _ := e.chain.GetNativeContractScriptHash(nativenames.CryptoLib)
  1440  				return &result.Invoke{
  1441  					State:         "HALT",
  1442  					GasConsumed:   13970250,
  1443  					Script:        script,
  1444  					Stack:         []stackitem.Item{stackitem.Make("1.2.3.4")},
  1445  					Notifications: []state.NotificationEvent{},
  1446  					Diagnostics: &result.InvokeDiag{
  1447  						Changes: []dboper.Operation{},
  1448  						Invocations: []*invocations.Tree{{
  1449  							Current: hash.Hash160(script),
  1450  							Calls: []*invocations.Tree{
  1451  								{
  1452  									Current: nnsHash,
  1453  									Calls: []*invocations.Tree{
  1454  										{
  1455  											Current: stdHash,
  1456  										},
  1457  										{
  1458  											Current: cryptoHash,
  1459  										},
  1460  										{
  1461  											Current: stdHash,
  1462  										},
  1463  										{
  1464  											Current: cryptoHash,
  1465  										},
  1466  										{
  1467  											Current: cryptoHash,
  1468  										},
  1469  									},
  1470  								},
  1471  							},
  1472  						}},
  1473  					},
  1474  				}
  1475  			},
  1476  		},
  1477  		{
  1478  			name:    "no params",
  1479  			params:  `[]`,
  1480  			fail:    true,
  1481  			errCode: neorpc.InvalidParamsCode,
  1482  		},
  1483  		{
  1484  			name:    "not a string",
  1485  			params:  `[42, "test", []]`,
  1486  			fail:    true,
  1487  			errCode: neorpc.ErrUnknownContractCode,
  1488  		},
  1489  		{
  1490  			name:    "not a scripthash",
  1491  			params:  `["qwerty", "test", []]`,
  1492  			fail:    true,
  1493  			errCode: neorpc.InvalidParamsCode,
  1494  		},
  1495  		{
  1496  			name:    "bad params",
  1497  			params:  `["50befd26fdf6e4d957c11e078b24ebce6291456f", "test", [{"type": "Integer", "value": "qwerty"}]]`,
  1498  			fail:    true,
  1499  			errCode: neorpc.InvalidParamsCode,
  1500  		},
  1501  	},
  1502  	"invokefunctionhistoric": {
  1503  		{
  1504  			name:   "positive, by index",
  1505  			params: `[20, "50befd26fdf6e4d957c11e078b24ebce6291456f", "test", []]`,
  1506  			result: func(e *executor) any { return &result.Invoke{} },
  1507  			check: func(t *testing.T, e *executor, inv any) {
  1508  				res, ok := inv.(*result.Invoke)
  1509  				require.True(t, ok)
  1510  				assert.NotNil(t, res.Script)
  1511  				assert.NotEqual(t, "", res.State)
  1512  				assert.NotEqual(t, 0, res.GasConsumed)
  1513  			},
  1514  		},
  1515  		{
  1516  			name:   "positive, by stateroot",
  1517  			params: `["` + block20StateRootLE + `", "50befd26fdf6e4d957c11e078b24ebce6291456f", "test", []]`,
  1518  			result: func(e *executor) any { return &result.Invoke{} },
  1519  			check: func(t *testing.T, e *executor, inv any) {
  1520  				res, ok := inv.(*result.Invoke)
  1521  				require.True(t, ok)
  1522  				assert.NotNil(t, res.Script)
  1523  				assert.NotEqual(t, "", res.State)
  1524  				assert.NotEqual(t, 0, res.GasConsumed)
  1525  			},
  1526  		},
  1527  		{
  1528  			name:   "positive, with notifications",
  1529  			params: `[20, "` + nnsContractHash + `", "transfer", [{"type":"Hash160", "value":"0x0bcd2978634d961c24f5aea0802297ff128724d6"},{"type":"String", "value":"neo.com"},{"type":"Any", "value":null}],["0xb248508f4ef7088e10c48f14d04be3272ca29eee"]]`,
  1530  			result: func(e *executor) any {
  1531  				script := append([]byte{0x0b, 0x0c, 0x07, 0x6e, 0x65, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x0c, 0x14, 0xd6, 0x24, 0x87, 0x12, 0xff, 0x97, 0x22, 0x80, 0xa0, 0xae, 0xf5, 0x24, 0x1c, 0x96, 0x4d, 0x63, 0x78, 0x29, 0xcd, 0xb, 0x13, 0xc0, 0x1f, 0xc, 0x8, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0xc, 0x14}, nnsHash.BytesBE()...)
  1532  				script = append(script, 0x41, 0x62, 0x7d, 0x5b, 0x52)
  1533  				return &result.Invoke{
  1534  					State:       "HALT",
  1535  					GasConsumed: 31922970,
  1536  					Script:      script,
  1537  					Stack:       []stackitem.Item{stackitem.Make(true)},
  1538  					Notifications: []state.NotificationEvent{{
  1539  						ScriptHash: nnsHash,
  1540  						Name:       "Transfer",
  1541  						Item: stackitem.NewArray([]stackitem.Item{
  1542  							stackitem.Make([]byte{0xee, 0x9e, 0xa2, 0x2c, 0x27, 0xe3, 0x4b, 0xd0, 0x14, 0x8f, 0xc4, 0x10, 0x8e, 0x08, 0xf7, 0x4e, 0x8f, 0x50, 0x48, 0xb2}),
  1543  							stackitem.Make([]byte{0xd6, 0x24, 0x87, 0x12, 0xff, 0x97, 0x22, 0x80, 0xa0, 0xae, 0xf5, 0x24, 0x1c, 0x96, 0x4d, 0x63, 0x78, 0x29, 0xcd, 0x0b}),
  1544  							stackitem.Make(1),
  1545  							stackitem.Make("neo.com"),
  1546  						}),
  1547  					}},
  1548  				}
  1549  			},
  1550  		},
  1551  		{
  1552  			name:   "positive, verbose",
  1553  			params: `[20, "` + nnsContractHash + `", "resolve", [{"type":"String", "value":"neo.com"},{"type":"Integer","value":1}], [], true]`,
  1554  			result: func(e *executor) any {
  1555  				script := append([]byte{0x11, 0xc, 0x7, 0x6e, 0x65, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x12, 0xc0, 0x1f, 0xc, 0x7, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0xc, 0x14}, nnsHash.BytesBE()...)
  1556  				script = append(script, 0x41, 0x62, 0x7d, 0x5b, 0x52)
  1557  				stdHash, _ := e.chain.GetNativeContractScriptHash(nativenames.StdLib)
  1558  				cryptoHash, _ := e.chain.GetNativeContractScriptHash(nativenames.CryptoLib)
  1559  				return &result.Invoke{
  1560  					State:         "HALT",
  1561  					GasConsumed:   13970250,
  1562  					Script:        script,
  1563  					Stack:         []stackitem.Item{stackitem.Make("1.2.3.4")},
  1564  					Notifications: []state.NotificationEvent{},
  1565  					Diagnostics: &result.InvokeDiag{
  1566  						Changes: []dboper.Operation{},
  1567  						Invocations: []*invocations.Tree{{
  1568  							Current: hash.Hash160(script),
  1569  							Calls: []*invocations.Tree{
  1570  								{
  1571  									Current: nnsHash,
  1572  									Calls: []*invocations.Tree{
  1573  										{
  1574  											Current: stdHash,
  1575  										},
  1576  										{
  1577  											Current: cryptoHash,
  1578  										},
  1579  										{
  1580  											Current: stdHash,
  1581  										},
  1582  										{
  1583  											Current: cryptoHash,
  1584  										},
  1585  										{
  1586  											Current: cryptoHash,
  1587  										},
  1588  									},
  1589  								},
  1590  							},
  1591  						}},
  1592  					},
  1593  				}
  1594  			},
  1595  		},
  1596  		{
  1597  			name:    "no params",
  1598  			params:  `[]`,
  1599  			fail:    true,
  1600  			errCode: neorpc.InvalidParamsCode,
  1601  		},
  1602  		{
  1603  			name:    "no args",
  1604  			params:  `[20]`,
  1605  			fail:    true,
  1606  			errCode: neorpc.InvalidParamsCode,
  1607  		},
  1608  		{
  1609  			name:    "not a string",
  1610  			params:  `[20, 42, "test", []]`,
  1611  			fail:    true,
  1612  			errCode: neorpc.ErrUnknownContractCode,
  1613  		},
  1614  		{
  1615  			name:    "not a scripthash",
  1616  			params:  `[20,"qwerty", "test", []]`,
  1617  			fail:    true,
  1618  			errCode: neorpc.InvalidParamsCode,
  1619  		},
  1620  		{
  1621  			name:    "bad params",
  1622  			params:  `[20,"50befd26fdf6e4d957c11e078b24ebce6291456f", "test", [{"type": "Integer", "value": "qwerty"}]]`,
  1623  			fail:    true,
  1624  			errCode: neorpc.InvalidParamsCode,
  1625  		},
  1626  		{
  1627  			name:    "bad height",
  1628  			params:  `[100500,"50befd26fdf6e4d957c11e078b24ebce6291456f", "test", [{"type": "Integer", "value": 1}]]`,
  1629  			fail:    true,
  1630  			errCode: neorpc.InvalidParamsCode,
  1631  		},
  1632  		{
  1633  			name:    "bad stateroot",
  1634  			params:  `["` + util.Uint256{1, 2, 3}.StringLE() + `","50befd26fdf6e4d957c11e078b24ebce6291456f", "test", [{"type": "Integer", "value": 1}]]`,
  1635  			fail:    true,
  1636  			errCode: neorpc.InvalidParamsCode,
  1637  		},
  1638  	},
  1639  	"invokescript": {
  1640  		{
  1641  			name:   "positive",
  1642  			params: `["UcVrDUhlbGxvLCB3b3JsZCFoD05lby5SdW50aW1lLkxvZ2FsdWY="]`,
  1643  			result: func(e *executor) any { return &result.Invoke{} },
  1644  			check: func(t *testing.T, e *executor, inv any) {
  1645  				res, ok := inv.(*result.Invoke)
  1646  				require.True(t, ok)
  1647  				assert.NotEqual(t, "", res.Script)
  1648  				assert.NotEqual(t, "", res.State)
  1649  				assert.NotEqual(t, 0, res.GasConsumed)
  1650  			},
  1651  		},
  1652  		{
  1653  			name:   "positive,verbose",
  1654  			params: `["UcVrDUhlbGxvLCB3b3JsZCFoD05lby5SdW50aW1lLkxvZ2FsdWY=",[],true]`,
  1655  			result: func(e *executor) any {
  1656  				script := []byte{0x51, 0xc5, 0x6b, 0xd, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x68, 0xf, 0x4e, 0x65, 0x6f, 0x2e, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x4c, 0x6f, 0x67, 0x61, 0x6c, 0x75, 0x66}
  1657  				return &result.Invoke{
  1658  					State:          "FAULT",
  1659  					GasConsumed:    60,
  1660  					Script:         script,
  1661  					Stack:          []stackitem.Item{},
  1662  					FaultException: "at instruction 0 (ROT): too big index",
  1663  					Notifications:  []state.NotificationEvent{},
  1664  					Diagnostics: &result.InvokeDiag{
  1665  						Changes: []dboper.Operation{},
  1666  						Invocations: []*invocations.Tree{{
  1667  							Current: hash.Hash160(script),
  1668  						}},
  1669  					},
  1670  				}
  1671  			},
  1672  		},
  1673  		{
  1674  			name: "positive, good witness",
  1675  			// script is base64-encoded `invokescript_contract.avm` representation, hashes are hex-encoded LE bytes of hashes used in the contract with `0x` prefix
  1676  			params: fmt.Sprintf(`["%s",["0x0000000009070e030d0f0e020d0c06050e030c01","0x090c060e00010205040307030102000902030f0d"]]`, invokescriptContractAVM),
  1677  			result: func(e *executor) any { return &result.Invoke{} },
  1678  			check: func(t *testing.T, e *executor, inv any) {
  1679  				res, ok := inv.(*result.Invoke)
  1680  				require.True(t, ok)
  1681  				assert.Equal(t, "HALT", res.State)
  1682  				require.Equal(t, 1, len(res.Stack))
  1683  				require.Equal(t, big.NewInt(3), res.Stack[0].Value())
  1684  			},
  1685  		},
  1686  		{
  1687  			name:   "positive, bad witness of second hash",
  1688  			params: fmt.Sprintf(`["%s",["0x0000000009070e030d0f0e020d0c06050e030c01"]]`, invokescriptContractAVM),
  1689  			result: func(e *executor) any { return &result.Invoke{} },
  1690  			check: func(t *testing.T, e *executor, inv any) {
  1691  				res, ok := inv.(*result.Invoke)
  1692  				require.True(t, ok)
  1693  				assert.Equal(t, "HALT", res.State)
  1694  				require.Equal(t, 1, len(res.Stack))
  1695  				require.Equal(t, big.NewInt(2), res.Stack[0].Value())
  1696  			},
  1697  		},
  1698  		{
  1699  			name:   "positive, no good hashes",
  1700  			params: fmt.Sprintf(`["%s"]`, invokescriptContractAVM),
  1701  			result: func(e *executor) any { return &result.Invoke{} },
  1702  			check: func(t *testing.T, e *executor, inv any) {
  1703  				res, ok := inv.(*result.Invoke)
  1704  				require.True(t, ok)
  1705  				assert.Equal(t, "HALT", res.State)
  1706  				require.Equal(t, 1, len(res.Stack))
  1707  				require.Equal(t, big.NewInt(1), res.Stack[0].Value())
  1708  			},
  1709  		},
  1710  		{
  1711  			name:   "positive, bad hashes witness",
  1712  			params: fmt.Sprintf(`["%s",["0x0000000009070e030d0f0e020d0c06050e030c02"]]`, invokescriptContractAVM),
  1713  			result: func(e *executor) any { return &result.Invoke{} },
  1714  			check: func(t *testing.T, e *executor, inv any) {
  1715  				res, ok := inv.(*result.Invoke)
  1716  				require.True(t, ok)
  1717  				assert.Equal(t, "HALT", res.State)
  1718  				assert.Equal(t, 1, len(res.Stack))
  1719  				assert.Equal(t, big.NewInt(1), res.Stack[0].Value())
  1720  			},
  1721  		},
  1722  		{
  1723  			name:    "no params",
  1724  			params:  `[]`,
  1725  			fail:    true,
  1726  			errCode: neorpc.InvalidParamsCode,
  1727  		},
  1728  		{
  1729  			name:    "not a string",
  1730  			params:  `[42]`,
  1731  			fail:    true,
  1732  			errCode: neorpc.InvalidParamsCode,
  1733  		},
  1734  		{
  1735  			name:    "bas string",
  1736  			params:  `["qwerty"]`,
  1737  			fail:    true,
  1738  			errCode: neorpc.InvalidParamsCode,
  1739  		},
  1740  	},
  1741  	"invokescripthistoric": {
  1742  		{
  1743  			name:   "positive, by index",
  1744  			params: `[20,"UcVrDUhlbGxvLCB3b3JsZCFoD05lby5SdW50aW1lLkxvZ2FsdWY="]`,
  1745  			result: func(e *executor) any { return &result.Invoke{} },
  1746  			check: func(t *testing.T, e *executor, inv any) {
  1747  				res, ok := inv.(*result.Invoke)
  1748  				require.True(t, ok)
  1749  				assert.NotEqual(t, "", res.Script)
  1750  				assert.NotEqual(t, "", res.State)
  1751  				assert.NotEqual(t, 0, res.GasConsumed)
  1752  			},
  1753  		},
  1754  		{
  1755  			name:   "positive, by stateroot",
  1756  			params: `["` + block20StateRootLE + `","UcVrDUhlbGxvLCB3b3JsZCFoD05lby5SdW50aW1lLkxvZ2FsdWY="]`,
  1757  			result: func(e *executor) any { return &result.Invoke{} },
  1758  			check: func(t *testing.T, e *executor, inv any) {
  1759  				res, ok := inv.(*result.Invoke)
  1760  				require.True(t, ok)
  1761  				assert.NotEqual(t, "", res.Script)
  1762  				assert.NotEqual(t, "", res.State)
  1763  				assert.NotEqual(t, 0, res.GasConsumed)
  1764  			},
  1765  		},
  1766  		{
  1767  			name:   "positive,verbose",
  1768  			params: `[20, "UcVrDUhlbGxvLCB3b3JsZCFoD05lby5SdW50aW1lLkxvZ2FsdWY=",[],true]`,
  1769  			result: func(e *executor) any {
  1770  				script := []byte{0x51, 0xc5, 0x6b, 0xd, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x68, 0xf, 0x4e, 0x65, 0x6f, 0x2e, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x4c, 0x6f, 0x67, 0x61, 0x6c, 0x75, 0x66}
  1771  				return &result.Invoke{
  1772  					State:          "FAULT",
  1773  					GasConsumed:    60,
  1774  					Script:         script,
  1775  					Stack:          []stackitem.Item{},
  1776  					FaultException: "at instruction 0 (ROT): too big index",
  1777  					Notifications:  []state.NotificationEvent{},
  1778  					Diagnostics: &result.InvokeDiag{
  1779  						Changes: []dboper.Operation{},
  1780  						Invocations: []*invocations.Tree{{
  1781  							Current: hash.Hash160(script),
  1782  						}},
  1783  					},
  1784  				}
  1785  			},
  1786  		},
  1787  		{
  1788  			name: "positive, good witness",
  1789  			// script is base64-encoded `invokescript_contract.avm` representation, hashes are hex-encoded LE bytes of hashes used in the contract with `0x` prefix
  1790  			params: fmt.Sprintf(`[20,"%s",["0x0000000009070e030d0f0e020d0c06050e030c01","0x090c060e00010205040307030102000902030f0d"]]`, invokescriptContractAVM),
  1791  			result: func(e *executor) any { return &result.Invoke{} },
  1792  			check: func(t *testing.T, e *executor, inv any) {
  1793  				res, ok := inv.(*result.Invoke)
  1794  				require.True(t, ok)
  1795  				assert.Equal(t, "HALT", res.State)
  1796  				require.Equal(t, 1, len(res.Stack))
  1797  				require.Equal(t, big.NewInt(3), res.Stack[0].Value())
  1798  			},
  1799  		},
  1800  		{
  1801  			name:   "positive, bad witness of second hash",
  1802  			params: fmt.Sprintf(`[20,"%s",["0x0000000009070e030d0f0e020d0c06050e030c01"]]`, invokescriptContractAVM),
  1803  			result: func(e *executor) any { return &result.Invoke{} },
  1804  			check: func(t *testing.T, e *executor, inv any) {
  1805  				res, ok := inv.(*result.Invoke)
  1806  				require.True(t, ok)
  1807  				assert.Equal(t, "HALT", res.State)
  1808  				require.Equal(t, 1, len(res.Stack))
  1809  				require.Equal(t, big.NewInt(2), res.Stack[0].Value())
  1810  			},
  1811  		},
  1812  		{
  1813  			name:   "positive, no good hashes",
  1814  			params: fmt.Sprintf(`[20,"%s"]`, invokescriptContractAVM),
  1815  			result: func(e *executor) any { return &result.Invoke{} },
  1816  			check: func(t *testing.T, e *executor, inv any) {
  1817  				res, ok := inv.(*result.Invoke)
  1818  				require.True(t, ok)
  1819  				assert.Equal(t, "HALT", res.State)
  1820  				require.Equal(t, 1, len(res.Stack))
  1821  				require.Equal(t, big.NewInt(1), res.Stack[0].Value())
  1822  			},
  1823  		},
  1824  		{
  1825  			name:   "positive, bad hashes witness",
  1826  			params: fmt.Sprintf(`[20,"%s",["0x0000000009070e030d0f0e020d0c06050e030c02"]]`, invokescriptContractAVM),
  1827  			result: func(e *executor) any { return &result.Invoke{} },
  1828  			check: func(t *testing.T, e *executor, inv any) {
  1829  				res, ok := inv.(*result.Invoke)
  1830  				require.True(t, ok)
  1831  				assert.Equal(t, "HALT", res.State)
  1832  				assert.Equal(t, 1, len(res.Stack))
  1833  				assert.Equal(t, big.NewInt(1), res.Stack[0].Value())
  1834  			},
  1835  		},
  1836  		{
  1837  			name:    "no params",
  1838  			params:  `[]`,
  1839  			fail:    true,
  1840  			errCode: neorpc.InvalidParamsCode,
  1841  		},
  1842  		{
  1843  			name:    "no script",
  1844  			params:  `[20]`,
  1845  			fail:    true,
  1846  			errCode: neorpc.InvalidParamsCode,
  1847  		},
  1848  		{
  1849  			name:    "not a string",
  1850  			params:  `[20,42]`,
  1851  			fail:    true,
  1852  			errCode: neorpc.InvalidParamsCode,
  1853  		},
  1854  		{
  1855  			name:    "bad string",
  1856  			params:  `[20, "qwerty"]`,
  1857  			fail:    true,
  1858  			errCode: neorpc.InvalidParamsCode,
  1859  		},
  1860  		{
  1861  			name:    "bad height",
  1862  			params:  `[100500,"qwerty"]`,
  1863  			fail:    true,
  1864  			errCode: neorpc.InvalidParamsCode,
  1865  		},
  1866  		{
  1867  			name:    "bad stateroot",
  1868  			params:  `["` + util.Uint256{1, 2, 3}.StringLE() + `","UcVrDUhlbGxvLCB3b3JsZCFoD05lby5SdW50aW1lLkxvZ2FsdWY="]`,
  1869  			fail:    true,
  1870  			errCode: neorpc.InvalidParamsCode,
  1871  		},
  1872  	},
  1873  	"invokecontractverify": {
  1874  		{
  1875  			name:   "positive",
  1876  			params: fmt.Sprintf(`["%s", [], [{"account":"%s"}]]`, verifyContractHash, testchain.PrivateKeyByID(0).PublicKey().GetScriptHash().StringLE()),
  1877  			result: func(e *executor) any { return &result.Invoke{} },
  1878  			check: func(t *testing.T, e *executor, inv any) {
  1879  				res, ok := inv.(*result.Invoke)
  1880  				require.True(t, ok)
  1881  				assert.Nil(t, res.Script) // empty witness invocation script (pushes args of `verify` on stack, but this `verify` don't have args)
  1882  				assert.Equal(t, "HALT", res.State)
  1883  				assert.NotEqual(t, 0, res.GasConsumed)
  1884  				assert.Equal(t, true, res.Stack[0].Value().(bool), fmt.Sprintf("check address in verification_contract.go: expected %s", testchain.PrivateKeyByID(0).Address()))
  1885  			},
  1886  		},
  1887  		{
  1888  			name:   "positive, no signers",
  1889  			params: fmt.Sprintf(`["%s", []]`, verifyContractHash),
  1890  			result: func(e *executor) any { return &result.Invoke{} },
  1891  			check: func(t *testing.T, e *executor, inv any) {
  1892  				res, ok := inv.(*result.Invoke)
  1893  				require.True(t, ok)
  1894  				assert.Nil(t, res.Script)
  1895  				assert.Equal(t, "HALT", res.State, res.FaultException)
  1896  				assert.NotEqual(t, 0, res.GasConsumed)
  1897  				assert.Equal(t, false, res.Stack[0].Value().(bool))
  1898  			},
  1899  		},
  1900  		{
  1901  			name:   "positive, no arguments",
  1902  			params: fmt.Sprintf(`["%s"]`, verifyContractHash),
  1903  			result: func(e *executor) any { return &result.Invoke{} },
  1904  			check: func(t *testing.T, e *executor, inv any) {
  1905  				res, ok := inv.(*result.Invoke)
  1906  				require.True(t, ok)
  1907  				assert.Nil(t, res.Script)
  1908  				assert.Equal(t, "HALT", res.State, res.FaultException)
  1909  				assert.NotEqual(t, 0, res.GasConsumed)
  1910  				assert.Equal(t, false, res.Stack[0].Value().(bool))
  1911  			},
  1912  		},
  1913  		{
  1914  			name:   "positive, with signers and scripts",
  1915  			params: fmt.Sprintf(`["%s", [], [{"account":"%s", "invocation":"MQo=", "verification": ""}]]`, verifyContractHash, testchain.PrivateKeyByID(0).PublicKey().GetScriptHash().StringLE()),
  1916  			result: func(e *executor) any { return &result.Invoke{} },
  1917  			check: func(t *testing.T, e *executor, inv any) {
  1918  				res, ok := inv.(*result.Invoke)
  1919  				require.True(t, ok)
  1920  				assert.Nil(t, res.Script)
  1921  				assert.Equal(t, "HALT", res.State)
  1922  				assert.NotEqual(t, 0, res.GasConsumed)
  1923  				assert.Equal(t, true, res.Stack[0].Value().(bool))
  1924  			},
  1925  		},
  1926  		{
  1927  			name:   "positive, with arguments, result=true",
  1928  			params: fmt.Sprintf(`["%s", [{"type": "String", "value": "good_string"}, {"type": "Integer", "value": "4"}, {"type":"Boolean", "value": false}]]`, verifyWithArgsContractHash),
  1929  			result: func(e *executor) any { return &result.Invoke{} },
  1930  			check: func(t *testing.T, e *executor, inv any) {
  1931  				res, ok := inv.(*result.Invoke)
  1932  				require.True(t, ok)
  1933  				expectedInvScript := io.NewBufBinWriter()
  1934  				emit.Bool(expectedInvScript.BinWriter, false)
  1935  				emit.Int(expectedInvScript.BinWriter, int64(4))
  1936  				emit.String(expectedInvScript.BinWriter, "good_string")
  1937  				require.NoError(t, expectedInvScript.Err)
  1938  				assert.Equal(t, expectedInvScript.Bytes(), res.Script) // witness invocation script (pushes args of `verify` on stack)
  1939  				assert.Equal(t, "HALT", res.State, res.FaultException)
  1940  				assert.NotEqual(t, 0, res.GasConsumed)
  1941  				assert.Equal(t, true, res.Stack[0].Value().(bool))
  1942  			},
  1943  		},
  1944  		{
  1945  			name:   "positive, with arguments, result=false",
  1946  			params: fmt.Sprintf(`["%s", [{"type": "String", "value": "invalid_string"}, {"type": "Integer", "value": "4"}, {"type":"Boolean", "value": false}]]`, verifyWithArgsContractHash),
  1947  			result: func(e *executor) any { return &result.Invoke{} },
  1948  			check: func(t *testing.T, e *executor, inv any) {
  1949  				res, ok := inv.(*result.Invoke)
  1950  				require.True(t, ok)
  1951  				expectedInvScript := io.NewBufBinWriter()
  1952  				emit.Bool(expectedInvScript.BinWriter, false)
  1953  				emit.Int(expectedInvScript.BinWriter, int64(4))
  1954  				emit.String(expectedInvScript.BinWriter, "invalid_string")
  1955  				require.NoError(t, expectedInvScript.Err)
  1956  				assert.Equal(t, expectedInvScript.Bytes(), res.Script)
  1957  				assert.Equal(t, "HALT", res.State, res.FaultException)
  1958  				assert.NotEqual(t, 0, res.GasConsumed)
  1959  				assert.Equal(t, false, res.Stack[0].Value().(bool))
  1960  			},
  1961  		},
  1962  		{
  1963  			name:    "invalid call args",
  1964  			params:  fmt.Sprintf(`["%s", [{"type":"Map","value":{"key":"value"}}]]`, verifyWithArgsContractHash),
  1965  			fail:    true,
  1966  			errCode: neorpc.InternalServerErrorCode,
  1967  		},
  1968  		{
  1969  			name:    "negative, wrong signer",
  1970  			params:  fmt.Sprintf(`["%s", [], [{"account":"aaa"}]]`, verifyContractHash),
  1971  			fail:    true,
  1972  			errCode: neorpc.InvalidParamsCode,
  1973  		},
  1974  		{
  1975  			name:    "unknown contract",
  1976  			params:  fmt.Sprintf(`["%s", []]`, util.Uint160{}.String()),
  1977  			fail:    true,
  1978  			errCode: neorpc.ErrUnknownContractCode,
  1979  		},
  1980  		{
  1981  			name:    "no params",
  1982  			params:  `[]`,
  1983  			fail:    true,
  1984  			errCode: neorpc.InvalidParamsCode,
  1985  		},
  1986  		{
  1987  			name:    "not a string",
  1988  			params:  `[42, []]`,
  1989  			fail:    true,
  1990  			errCode: neorpc.ErrUnknownContractCode,
  1991  		},
  1992  	},
  1993  	"invokecontractverifyhistoric": {
  1994  		{
  1995  			name:   "positive, by index",
  1996  			params: fmt.Sprintf(`[20,"%s", [], [{"account":"%s"}]]`, verifyContractHash, testchain.PrivateKeyByID(0).PublicKey().GetScriptHash().StringLE()),
  1997  			result: func(e *executor) any { return &result.Invoke{} },
  1998  			check: func(t *testing.T, e *executor, inv any) {
  1999  				res, ok := inv.(*result.Invoke)
  2000  				require.True(t, ok)
  2001  				assert.Nil(t, res.Script) // empty witness invocation script (pushes args of `verify` on stack, but this `verify` don't have args)
  2002  				assert.Equal(t, "HALT", res.State)
  2003  				assert.NotEqual(t, 0, res.GasConsumed)
  2004  				assert.Equal(t, true, res.Stack[0].Value().(bool), fmt.Sprintf("check address in verification_contract.go: expected %s", testchain.PrivateKeyByID(0).Address()))
  2005  			},
  2006  		},
  2007  		{
  2008  			name:   "positive, by stateroot",
  2009  			params: fmt.Sprintf(`["`+block20StateRootLE+`","%s", [], [{"account":"%s"}]]`, verifyContractHash, testchain.PrivateKeyByID(0).PublicKey().GetScriptHash().StringLE()),
  2010  			result: func(e *executor) any { return &result.Invoke{} },
  2011  			check: func(t *testing.T, e *executor, inv any) {
  2012  				res, ok := inv.(*result.Invoke)
  2013  				require.True(t, ok)
  2014  				assert.Nil(t, res.Script) // empty witness invocation script (pushes args of `verify` on stack, but this `verify` don't have args)
  2015  				assert.Equal(t, "HALT", res.State)
  2016  				assert.NotEqual(t, 0, res.GasConsumed)
  2017  				assert.Equal(t, true, res.Stack[0].Value().(bool), fmt.Sprintf("check address in verification_contract.go: expected %s", testchain.PrivateKeyByID(0).Address()))
  2018  			},
  2019  		},
  2020  		{
  2021  			name:   "positive, no signers",
  2022  			params: fmt.Sprintf(`[20,"%s", []]`, verifyContractHash),
  2023  			result: func(e *executor) any { return &result.Invoke{} },
  2024  			check: func(t *testing.T, e *executor, inv any) {
  2025  				res, ok := inv.(*result.Invoke)
  2026  				require.True(t, ok)
  2027  				assert.Nil(t, res.Script)
  2028  				assert.Equal(t, "HALT", res.State, res.FaultException)
  2029  				assert.NotEqual(t, 0, res.GasConsumed)
  2030  				assert.Equal(t, false, res.Stack[0].Value().(bool))
  2031  			},
  2032  		},
  2033  		{
  2034  			name:   "positive, no arguments",
  2035  			params: fmt.Sprintf(`[20,"%s"]`, verifyContractHash),
  2036  			result: func(e *executor) any { return &result.Invoke{} },
  2037  			check: func(t *testing.T, e *executor, inv any) {
  2038  				res, ok := inv.(*result.Invoke)
  2039  				require.True(t, ok)
  2040  				assert.Nil(t, res.Script)
  2041  				assert.Equal(t, "HALT", res.State, res.FaultException)
  2042  				assert.NotEqual(t, 0, res.GasConsumed)
  2043  				assert.Equal(t, false, res.Stack[0].Value().(bool))
  2044  			},
  2045  		},
  2046  		{
  2047  			name:   "positive, with signers and scripts",
  2048  			params: fmt.Sprintf(`[20,"%s", [], [{"account":"%s", "invocation":"MQo=", "verification": ""}]]`, verifyContractHash, testchain.PrivateKeyByID(0).PublicKey().GetScriptHash().StringLE()),
  2049  			result: func(e *executor) any { return &result.Invoke{} },
  2050  			check: func(t *testing.T, e *executor, inv any) {
  2051  				res, ok := inv.(*result.Invoke)
  2052  				require.True(t, ok)
  2053  				assert.Nil(t, res.Script)
  2054  				assert.Equal(t, "HALT", res.State)
  2055  				assert.NotEqual(t, 0, res.GasConsumed)
  2056  				assert.Equal(t, true, res.Stack[0].Value().(bool))
  2057  			},
  2058  		},
  2059  		{
  2060  			name:   "positive, with arguments, result=true",
  2061  			params: fmt.Sprintf(`[20,"%s", [{"type": "String", "value": "good_string"}, {"type": "Integer", "value": "4"}, {"type":"Boolean", "value": false}]]`, verifyWithArgsContractHash),
  2062  			result: func(e *executor) any { return &result.Invoke{} },
  2063  			check: func(t *testing.T, e *executor, inv any) {
  2064  				res, ok := inv.(*result.Invoke)
  2065  				require.True(t, ok)
  2066  				expectedInvScript := io.NewBufBinWriter()
  2067  				emit.Bool(expectedInvScript.BinWriter, false)
  2068  				emit.Int(expectedInvScript.BinWriter, int64(4))
  2069  				emit.String(expectedInvScript.BinWriter, "good_string")
  2070  				require.NoError(t, expectedInvScript.Err)
  2071  				assert.Equal(t, expectedInvScript.Bytes(), res.Script) // witness invocation script (pushes args of `verify` on stack)
  2072  				assert.Equal(t, "HALT", res.State, res.FaultException)
  2073  				assert.NotEqual(t, 0, res.GasConsumed)
  2074  				assert.Equal(t, true, res.Stack[0].Value().(bool))
  2075  			},
  2076  		},
  2077  		{
  2078  			name:   "positive, with arguments, result=false",
  2079  			params: fmt.Sprintf(`[20, "%s", [{"type": "String", "value": "invalid_string"}, {"type": "Integer", "value": "4"}, {"type":"Boolean", "value": false}]]`, verifyWithArgsContractHash),
  2080  			result: func(e *executor) any { return &result.Invoke{} },
  2081  			check: func(t *testing.T, e *executor, inv any) {
  2082  				res, ok := inv.(*result.Invoke)
  2083  				require.True(t, ok)
  2084  				expectedInvScript := io.NewBufBinWriter()
  2085  				emit.Bool(expectedInvScript.BinWriter, false)
  2086  				emit.Int(expectedInvScript.BinWriter, int64(4))
  2087  				emit.String(expectedInvScript.BinWriter, "invalid_string")
  2088  				require.NoError(t, expectedInvScript.Err)
  2089  				assert.Equal(t, expectedInvScript.Bytes(), res.Script)
  2090  				assert.Equal(t, "HALT", res.State, res.FaultException)
  2091  				assert.NotEqual(t, 0, res.GasConsumed)
  2092  				assert.Equal(t, false, res.Stack[0].Value().(bool))
  2093  			},
  2094  		},
  2095  		{
  2096  			name:    "unknown contract",
  2097  			params:  fmt.Sprintf(`[20, "%s", []]`, util.Uint160{}.String()),
  2098  			fail:    true,
  2099  			errCode: neorpc.ErrUnknownContractCode,
  2100  		},
  2101  		{
  2102  			name:    "no params",
  2103  			params:  `[]`,
  2104  			fail:    true,
  2105  			errCode: neorpc.InvalidParamsCode,
  2106  		},
  2107  		{
  2108  			name:    "no args",
  2109  			params:  `[20]`,
  2110  			fail:    true,
  2111  			errCode: neorpc.InvalidParamsCode,
  2112  		},
  2113  		{
  2114  			name:    "not a string",
  2115  			params:  `[20,42, []]`,
  2116  			fail:    true,
  2117  			errCode: neorpc.ErrUnknownContractCode,
  2118  		},
  2119  	},
  2120  	"sendrawtransaction": {
  2121  		{
  2122  			name:   "positive",
  2123  			params: `["AB0AAACWP5gAAAAAAEDaEgAAAAAAGAAAAAHunqIsJ+NL0BSPxBCOCPdOj1BIsoAAXgsDAOh2SBcAAAAMFBEmW7QXJQBBvgTo+iQOOPV8HlabDBTunqIsJ+NL0BSPxBCOCPdOj1BIshTAHwwIdHJhbnNmZXIMFPVj6kC8KD1NDgXEjqMFs/Kgc0DvQWJ9W1IBQgxAJ6norhWoZxp+Hj1JFhi+Z3qI9DUkLSbfsbaLSaJIqxTfdmPbNFDVK1G+oa+LWmpRp/bj9+QZM7yC+S6HXUI7rigMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwkFW57Mn"]`,
  2124  			result: func(e *executor) any { return &result.RelayResult{} },
  2125  			check: func(t *testing.T, e *executor, inv any) {
  2126  				res, ok := inv.(*result.RelayResult)
  2127  				require.True(t, ok)
  2128  				expectedHash := "c11861dec1dd0f188608b725095041fcfc90abe51eea044993f122f22472753e"
  2129  				assert.Equal(t, expectedHash, res.Hash.StringLE())
  2130  			},
  2131  		},
  2132  		{
  2133  			name:    "already in pool",
  2134  			params:  `["AB0AAACWP5gAAAAAAEDaEgAAAAAAGAAAAAHunqIsJ+NL0BSPxBCOCPdOj1BIsoAAXgsDAOh2SBcAAAAMFBEmW7QXJQBBvgTo+iQOOPV8HlabDBTunqIsJ+NL0BSPxBCOCPdOj1BIshTAHwwIdHJhbnNmZXIMFPVj6kC8KD1NDgXEjqMFs/Kgc0DvQWJ9W1IBQgxAJ6norhWoZxp+Hj1JFhi+Z3qI9DUkLSbfsbaLSaJIqxTfdmPbNFDVK1G+oa+LWmpRp/bj9+QZM7yC+S6HXUI7rigMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwkFW57Mn"]`,
  2135  			fail:    true,
  2136  			errCode: neorpc.ErrAlreadyInPoolCode,
  2137  		},
  2138  		{
  2139  			name:    "negative",
  2140  			params:  `["AAoAAAAxboUQOQGdOd/Cw31sP+4Z/VgJhwAAAAAAAAAA8q0FAAAAAACwBAAAAAExboUQOQGdOd/Cw31sP+4Z/VgJhwFdAwDodkgXAAAADBQgcoJ0r6/Db0OgcdMoz6PmKdnLsAwUMW6FEDkBnTnfwsN9bD/uGf1YCYcTwAwIdHJhbnNmZXIMFIl3INjNdvTwCr+jfA7diJwgj96bQWJ9W1I4AUIMQN+VMUEnEWlCHOurXSegFj4pTXx/LQUltEmHRTRIFP09bFxZHJsXI9BdQoVvQJrbCEz2esySHPr8YpEzpeteen4pDCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcILQQqQav8="]`,
  2141  			fail:    true,
  2142  			errCode: neorpc.InvalidParamsCode,
  2143  		},
  2144  		{
  2145  			name:    "no params",
  2146  			params:  `[]`,
  2147  			fail:    true,
  2148  			errCode: neorpc.InvalidParamsCode,
  2149  		},
  2150  		{
  2151  			name:    "invalid string",
  2152  			params:  `["notabase64%"]`,
  2153  			fail:    true,
  2154  			errCode: neorpc.InvalidParamsCode,
  2155  		},
  2156  		{
  2157  			name:    "invalid tx",
  2158  			params:  `["AnTXkgcmF3IGNvbnRyYWNw=="]`,
  2159  			fail:    true,
  2160  			errCode: neorpc.InvalidParamsCode,
  2161  		},
  2162  	},
  2163  	"submitblock": {
  2164  		{
  2165  			name:    "invalid base64",
  2166  			params:  `["%%%"]`,
  2167  			fail:    true,
  2168  			errCode: neorpc.InvalidParamsCode,
  2169  		},
  2170  		{
  2171  			name:    "invalid block bytes",
  2172  			params:  `["AAAAACc="]`,
  2173  			fail:    true,
  2174  			errCode: neorpc.InvalidParamsCode,
  2175  		},
  2176  		{
  2177  			name:    "no params",
  2178  			params:  `[]`,
  2179  			fail:    true,
  2180  			errCode: neorpc.InvalidParamsCode,
  2181  		},
  2182  	},
  2183  	"submitoracleresponse": {
  2184  		{
  2185  			name:    "no params",
  2186  			params:  `[]`,
  2187  			fail:    true,
  2188  			errCode: neorpc.ErrOracleDisabledCode,
  2189  		},
  2190  	},
  2191  	"submitnotaryrequest": {
  2192  		{
  2193  			name:    "no params",
  2194  			params:  `[]`,
  2195  			fail:    true,
  2196  			errCode: neorpc.InvalidParamsCode,
  2197  		},
  2198  	},
  2199  	"validateaddress": {
  2200  		{
  2201  			name:   "positive",
  2202  			params: `["Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2"]`,
  2203  			result: func(*executor) any { return &result.ValidateAddress{} },
  2204  			check: func(t *testing.T, e *executor, va any) {
  2205  				res, ok := va.(*result.ValidateAddress)
  2206  				require.True(t, ok)
  2207  				assert.Equal(t, "Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2", res.Address)
  2208  				assert.True(t, res.IsValid)
  2209  			},
  2210  		},
  2211  		{
  2212  			name:   "negative",
  2213  			params: "[1]",
  2214  			result: func(*executor) any {
  2215  				return &result.ValidateAddress{
  2216  					Address: float64(1),
  2217  					IsValid: false,
  2218  				}
  2219  			},
  2220  		},
  2221  		{
  2222  			name:    "no params",
  2223  			params:  "[]",
  2224  			fail:    true,
  2225  			errCode: neorpc.InvalidParamsCode,
  2226  		},
  2227  	},
  2228  }
  2229  
  2230  func TestRPC(t *testing.T) {
  2231  	t.Run("http", func(t *testing.T) {
  2232  		testRPCProtocol(t, doRPCCallOverHTTP)
  2233  	})
  2234  
  2235  	t.Run("websocket", func(t *testing.T) {
  2236  		testRPCProtocol(t, doRPCCallOverWS)
  2237  	})
  2238  }
  2239  
  2240  func TestSubmitOracle(t *testing.T) {
  2241  	rpc := `{"jsonrpc": "2.0", "id": 1, "method": "submitoracleresponse", "params": %s}`
  2242  
  2243  	t.Run("OracleDisabled", func(t *testing.T) {
  2244  		_, _, httpSrv := initClearServerWithCustomConfig(t, func(c *config.Config) {
  2245  			c.ApplicationConfiguration.Oracle.Enabled = false
  2246  		})
  2247  		req := fmt.Sprintf(rpc, "[]")
  2248  		body := doRPCCallOverHTTP(req, httpSrv.URL, t)
  2249  		checkErrGetResult(t, body, true, neorpc.ErrOracleDisabledCode)
  2250  	})
  2251  
  2252  	_, _, httpSrv := initClearServerWithServices(t, true, false, false)
  2253  
  2254  	runCase := func(t *testing.T, fail bool, errCode int64, params ...string) func(t *testing.T) {
  2255  		return func(t *testing.T) {
  2256  			ps := `[` + strings.Join(params, ",") + `]`
  2257  			req := fmt.Sprintf(rpc, ps)
  2258  			body := doRPCCallOverHTTP(req, httpSrv.URL, t)
  2259  			checkErrGetResult(t, body, fail, errCode)
  2260  		}
  2261  	}
  2262  	t.Run("MissingKey", runCase(t, true, neorpc.InvalidParamsCode))
  2263  	t.Run("InvalidKey", runCase(t, true, neorpc.InvalidParamsCode, `"1234"`))
  2264  
  2265  	priv, err := keys.NewPrivateKey()
  2266  	require.NoError(t, err)
  2267  	pubStr := `"` + base64.StdEncoding.EncodeToString(priv.PublicKey().Bytes()) + `"`
  2268  	t.Run("InvalidReqID", runCase(t, true, neorpc.InvalidParamsCode, pubStr, `"notanumber"`))
  2269  	t.Run("InvalidTxSignature", runCase(t, true, neorpc.InvalidParamsCode, pubStr, `1`, `"qwerty"`))
  2270  
  2271  	txSig := priv.Sign([]byte{1, 2, 3})
  2272  	txSigStr := `"` + base64.StdEncoding.EncodeToString(txSig) + `"`
  2273  	t.Run("MissingMsgSignature", runCase(t, true, neorpc.InvalidParamsCode, pubStr, `1`, txSigStr))
  2274  	t.Run("InvalidMsgSignature", runCase(t, true, neorpc.ErrInvalidSignatureCode, pubStr, `1`, txSigStr, `"0123"`))
  2275  
  2276  	msg := rpc2.GetMessage(priv.PublicKey().Bytes(), 1, txSig)
  2277  	msgSigStr := `"` + base64.StdEncoding.EncodeToString(priv.Sign(msg)) + `"`
  2278  	t.Run("Valid", runCase(t, false, 0, pubStr, `1`, txSigStr, msgSigStr))
  2279  }
  2280  
  2281  func TestNotaryRequestRPC(t *testing.T) {
  2282  	var notaryRequest1, notaryRequest2 *payload.P2PNotaryRequest
  2283  	rpcSubmit := `{"jsonrpc": "2.0", "id": 1, "method": "submitnotaryrequest", "params": %s}`
  2284  	rpcPool := `{"jsonrpc": "2.0", "id": 1, "method": "getrawnotarypool", "params": []}`
  2285  	rpcTx := `{"jsonrpc": "2.0", "id": 1, "method": "getrawnotarytransaction", "params": ["%s", %d]}`
  2286  
  2287  	t.Run("disabled P2PSigExtensions", func(t *testing.T) {
  2288  		_, _, httpSrv := initClearServerWithCustomConfig(t, func(c *config.Config) {
  2289  			c.ProtocolConfiguration.P2PSigExtensions = false
  2290  		})
  2291  		t.Run("submitnotaryrequest", func(t *testing.T) {
  2292  			body := doRPCCallOverHTTP(fmt.Sprintf(rpcSubmit, "[]"), httpSrv.URL, t)
  2293  			checkErrGetResult(t, body, true, neorpc.InternalServerErrorCode)
  2294  		})
  2295  		t.Run("getrawnotarypool", func(t *testing.T) {
  2296  			body := doRPCCallOverHTTP(rpcPool, httpSrv.URL, t)
  2297  			checkErrGetResult(t, body, true, neorpc.InternalServerErrorCode)
  2298  		})
  2299  		t.Run("getrawnotarytransaction", func(t *testing.T) {
  2300  			body := doRPCCallOverHTTP(fmt.Sprintf(rpcTx, " ", 1), httpSrv.URL, t)
  2301  			checkErrGetResult(t, body, true, neorpc.InternalServerErrorCode)
  2302  		})
  2303  	})
  2304  
  2305  	chain, _, httpSrv := initServerWithInMemoryChainAndServices(t, false, true, false)
  2306  
  2307  	submitNotaryRequest := func(t *testing.T, fail bool, errCode int64, params ...string) func(t *testing.T) {
  2308  		return func(t *testing.T) {
  2309  			ps := `[` + strings.Join(params, ",") + `]`
  2310  			req := fmt.Sprintf(rpcSubmit, ps)
  2311  			body := doRPCCallOverHTTP(req, httpSrv.URL, t)
  2312  			checkErrGetResult(t, body, fail, errCode)
  2313  		}
  2314  	}
  2315  
  2316  	t.Run("getrawnotarypool", func(t *testing.T) {
  2317  		t.Run("empty pool", func(t *testing.T) {
  2318  			body := doRPCCallOverHTTP(rpcPool, httpSrv.URL, t)
  2319  			res := checkErrGetResult(t, body, false, 0)
  2320  			actual := new(result.RawNotaryPool)
  2321  			require.NoError(t, json.Unmarshal(res, actual))
  2322  			require.Equal(t, 0, len(actual.Hashes))
  2323  		})
  2324  
  2325  		sender := testchain.PrivateKeyByID(0) // owner of the deposit in testchain
  2326  		notaryRequest1 = createValidNotaryRequest(chain, sender, 1, 2_0000_0000, nil)
  2327  		nrBytes, err := notaryRequest1.Bytes()
  2328  		require.NoError(t, err)
  2329  		str := fmt.Sprintf(`"%s"`, base64.StdEncoding.EncodeToString(nrBytes))
  2330  		submitNotaryRequest(t, false, 0, str)(t)
  2331  
  2332  		t.Run("nonempty pool", func(t *testing.T) {
  2333  			// get notary pool & check tx hashes
  2334  			body := doRPCCallOverHTTP(rpcPool, httpSrv.URL, t)
  2335  			res := checkErrGetResult(t, body, false, 0)
  2336  			actual := new(result.RawNotaryPool)
  2337  			require.NoError(t, json.Unmarshal(res, actual))
  2338  			require.Equal(t, 1, len(actual.Hashes))
  2339  			for actMain, actFallbacks := range actual.Hashes {
  2340  				require.Equal(t, notaryRequest1.MainTransaction.Hash(), actMain)
  2341  				require.Equal(t, 1, len(actFallbacks))
  2342  				require.Equal(t, notaryRequest1.FallbackTransaction.Hash(), actFallbacks[0])
  2343  			}
  2344  		})
  2345  
  2346  		notaryRequest2 = createValidNotaryRequest(chain, sender, 2, 3_0000_0000, notaryRequest1.MainTransaction)
  2347  		nrBytes2, err := notaryRequest2.Bytes()
  2348  		require.NoError(t, err)
  2349  		str2 := fmt.Sprintf(`"%s"`, base64.StdEncoding.EncodeToString(nrBytes2))
  2350  		submitNotaryRequest(t, false, 0, str2)(t)
  2351  
  2352  		t.Run("pool with 2", func(t *testing.T) {
  2353  			// get notary pool & check tx hashes
  2354  			body := doRPCCallOverHTTP(rpcPool, httpSrv.URL, t)
  2355  			res := checkErrGetResult(t, body, false, 0)
  2356  			actual := new(result.RawNotaryPool)
  2357  			require.NoError(t, json.Unmarshal(res, actual))
  2358  			require.Equal(t, 1, len(actual.Hashes))
  2359  			for actMain, actFallbacks := range actual.Hashes {
  2360  				require.Equal(t, notaryRequest1.MainTransaction.Hash(), actMain)
  2361  				require.Equal(t, 2, len(actFallbacks))
  2362  				// The second fallback transaction has higher priority, so it's first in the slice.
  2363  				require.Equal(t, notaryRequest1.FallbackTransaction.Hash(), actFallbacks[1])
  2364  				require.Equal(t, notaryRequest2.FallbackTransaction.Hash(), actFallbacks[0])
  2365  			}
  2366  		})
  2367  	})
  2368  
  2369  	t.Run("submitnotaryrequest", func(t *testing.T) {
  2370  		t.Run("missing request", submitNotaryRequest(t, true, neorpc.InvalidParamsCode))
  2371  		t.Run("not a base64", submitNotaryRequest(t, true, neorpc.InvalidParamsCode, `"not-a-base64$"`))
  2372  		t.Run("invalid request bytes", submitNotaryRequest(t, true, neorpc.InvalidParamsCode, `"not-a-request"`))
  2373  		t.Run("invalid request", func(t *testing.T) {
  2374  			mainTx := &transaction.Transaction{
  2375  				Attributes:      []transaction.Attribute{{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 1}}},
  2376  				Script:          []byte{byte(opcode.RET)},
  2377  				ValidUntilBlock: 123,
  2378  				Signers:         []transaction.Signer{{Account: util.Uint160{1, 5, 9}}},
  2379  				Scripts: []transaction.Witness{{
  2380  					InvocationScript:   []byte{1, 4, 7},
  2381  					VerificationScript: []byte{3, 6, 9},
  2382  				}},
  2383  			}
  2384  			fallbackTx := &transaction.Transaction{
  2385  				Script:          []byte{byte(opcode.RET)},
  2386  				ValidUntilBlock: 123,
  2387  				Attributes: []transaction.Attribute{
  2388  					{Type: transaction.NotValidBeforeT, Value: &transaction.NotValidBefore{Height: 123}},
  2389  					{Type: transaction.ConflictsT, Value: &transaction.Conflicts{Hash: mainTx.Hash()}},
  2390  					{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 0}},
  2391  				},
  2392  				Signers: []transaction.Signer{{Account: util.Uint160{1, 4, 7}}, {Account: util.Uint160{9, 8, 7}}},
  2393  				Scripts: []transaction.Witness{
  2394  					{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...), VerificationScript: make([]byte, 0)},
  2395  					{InvocationScript: []byte{1, 2, 3}, VerificationScript: []byte{1, 2, 3}}},
  2396  			}
  2397  			p := &payload.P2PNotaryRequest{
  2398  				MainTransaction:     mainTx,
  2399  				FallbackTransaction: fallbackTx,
  2400  				Witness: transaction.Witness{
  2401  					InvocationScript:   []byte{1, 2, 3},
  2402  					VerificationScript: []byte{7, 8, 9},
  2403  				},
  2404  			}
  2405  			nrBytes, err := p.Bytes()
  2406  			require.NoError(t, err)
  2407  			str := fmt.Sprintf(`"%s"`, base64.StdEncoding.EncodeToString(nrBytes))
  2408  			submitNotaryRequest(t, true, neorpc.ErrVerificationFailedCode, str)(t)
  2409  		})
  2410  		t.Run("valid request", func(t *testing.T) {
  2411  			sender := testchain.PrivateKeyByID(0) // owner of the deposit in testchain
  2412  			notaryRequest1 = createValidNotaryRequest(chain, sender, 3, 2_0000_0000, nil)
  2413  			nrBytes, err := notaryRequest1.Bytes()
  2414  			require.NoError(t, err)
  2415  			str := fmt.Sprintf(`"%s"`, base64.StdEncoding.EncodeToString(nrBytes))
  2416  			submitNotaryRequest(t, false, 0, str)(t)
  2417  		})
  2418  	})
  2419  
  2420  	t.Run("getrawnotarytransaction", func(t *testing.T) {
  2421  		t.Run("invalid param", func(t *testing.T) {
  2422  			req := fmt.Sprintf(rpcTx, "invalid", 1)
  2423  			body := doRPCCallOverHTTP(req, httpSrv.URL, t)
  2424  			checkErrGetResult(t, body, true, neorpc.InvalidParamsCode)
  2425  		})
  2426  		t.Run("unknown transaction", func(t *testing.T) {
  2427  			req := fmt.Sprintf(rpcTx, (util.Uint256{0, 0, 0}).StringLE(), 1)
  2428  			body := doRPCCallOverHTTP(req, httpSrv.URL, t)
  2429  			checkErrGetResult(t, body, true, neorpc.ErrUnknownTransactionCode)
  2430  		})
  2431  
  2432  		checkGetTxVerbose := func(t *testing.T, tx *transaction.Transaction) {
  2433  			req := fmt.Sprintf(rpcTx, tx.Hash().StringLE(), 1)
  2434  			body := doRPCCallOverHTTP(req, httpSrv.URL, t)
  2435  			res := checkErrGetResult(t, body, false, 0)
  2436  			actual := new(transaction.Transaction)
  2437  			require.NoError(t, json.Unmarshal(res, actual))
  2438  			_ = tx.Size()
  2439  			require.Equal(t, tx, actual)
  2440  		}
  2441  		t.Run("mainTx verbose", func(t *testing.T) {
  2442  			checkGetTxVerbose(t, notaryRequest1.MainTransaction)
  2443  		})
  2444  		t.Run("fallbackTx verbose", func(t *testing.T) {
  2445  			checkGetTxVerbose(t, notaryRequest1.FallbackTransaction)
  2446  			checkGetTxVerbose(t, notaryRequest2.FallbackTransaction)
  2447  		})
  2448  
  2449  		checkGetTxBytes := func(t *testing.T, tx *transaction.Transaction) {
  2450  			req := fmt.Sprintf(rpcTx, tx.Hash().StringLE(), 0)
  2451  			body := doRPCCallOverHTTP(req, httpSrv.URL, t)
  2452  			res := checkErrGetResult(t, body, false, 0)
  2453  
  2454  			var s string
  2455  			err := json.Unmarshal(res, &s)
  2456  			require.NoErrorf(t, err, "could not parse response: %s", res)
  2457  			txBin, err := testserdes.EncodeBinary(tx)
  2458  			require.NoError(t, err)
  2459  			expected := base64.StdEncoding.EncodeToString(txBin)
  2460  			assert.Equal(t, expected, s)
  2461  		}
  2462  		t.Run("mainTx bytes", func(t *testing.T) {
  2463  			checkGetTxBytes(t, notaryRequest1.MainTransaction)
  2464  		})
  2465  		t.Run("fallbackTx bytes", func(t *testing.T) {
  2466  			checkGetTxBytes(t, notaryRequest1.FallbackTransaction)
  2467  			checkGetTxBytes(t, notaryRequest2.FallbackTransaction)
  2468  		})
  2469  	})
  2470  }
  2471  
  2472  // createValidNotaryRequest creates and signs P2PNotaryRequest payload which can
  2473  // pass verification. It uses the provided mainTx if it's a nonempty structure.
  2474  func createValidNotaryRequest(chain *core.Blockchain, sender *keys.PrivateKey, nonce uint32, networkFee int64, mainTx *transaction.Transaction) *payload.P2PNotaryRequest {
  2475  	h := chain.BlockHeight()
  2476  	// If mainTx is nil, then generate it.
  2477  	if mainTx == nil {
  2478  		mainTx = &transaction.Transaction{
  2479  			Nonce:           nonce,
  2480  			Attributes:      []transaction.Attribute{{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 1}}},
  2481  			Script:          []byte{byte(opcode.RET)},
  2482  			ValidUntilBlock: h + 100,
  2483  			Signers:         []transaction.Signer{{Account: sender.GetScriptHash()}},
  2484  			Scripts: []transaction.Witness{{
  2485  				InvocationScript:   []byte{1, 4, 7},
  2486  				VerificationScript: []byte{3, 6, 9},
  2487  			}},
  2488  		}
  2489  	}
  2490  	fallbackTx := &transaction.Transaction{
  2491  		Script:          []byte{byte(opcode.RET)},
  2492  		ValidUntilBlock: h + 100,
  2493  		Attributes: []transaction.Attribute{
  2494  			{Type: transaction.NotValidBeforeT, Value: &transaction.NotValidBefore{Height: h + 50}},
  2495  			{Type: transaction.ConflictsT, Value: &transaction.Conflicts{Hash: mainTx.Hash()}},
  2496  			{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 0}},
  2497  		},
  2498  		Signers: []transaction.Signer{{Account: chain.GetNotaryContractScriptHash()}, {Account: sender.GetScriptHash()}},
  2499  		Scripts: []transaction.Witness{
  2500  			{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...), VerificationScript: []byte{}},
  2501  		},
  2502  		NetworkFee: networkFee,
  2503  	}
  2504  	fallbackTx.Scripts = append(fallbackTx.Scripts, transaction.Witness{
  2505  		InvocationScript:   append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, sender.SignHashable(uint32(testchain.Network()), fallbackTx)...),
  2506  		VerificationScript: sender.PublicKey().GetVerificationScript(),
  2507  	})
  2508  	p := &payload.P2PNotaryRequest{
  2509  		MainTransaction:     mainTx,
  2510  		FallbackTransaction: fallbackTx,
  2511  	}
  2512  	p.Witness = transaction.Witness{
  2513  		InvocationScript:   append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, sender.SignHashable(uint32(testchain.Network()), p)...),
  2514  		VerificationScript: sender.PublicKey().GetVerificationScript(),
  2515  	}
  2516  	return p
  2517  }
  2518  
  2519  func runTestCasesWithExecutor(t *testing.T, e *executor, rpcCall string, method string, testCases []rpcTestCase, doRPCCall func(string, string, *testing.T) []byte, checkErrResult func(t *testing.T, body []byte, expectingFail bool, expectedErrCode int64, expectedErr ...string) json.RawMessage) {
  2520  	t.Run(method, func(t *testing.T) {
  2521  		for _, tc := range testCases {
  2522  			t.Run(tc.name, func(t *testing.T) {
  2523  				body := doRPCCall(fmt.Sprintf(rpcCall, method, tc.params), e.httpSrv.URL, t)
  2524  				result := checkErrResult(t, body, tc.fail, tc.errCode)
  2525  				if tc.fail {
  2526  					return
  2527  				}
  2528  
  2529  				expected, res := tc.getResultPair(e)
  2530  				err := json.Unmarshal(result, res)
  2531  				require.NoErrorf(t, err, "could not parse response: %s", result)
  2532  
  2533  				if tc.check == nil {
  2534  					assert.Equal(t, expected, res)
  2535  				} else {
  2536  					tc.check(t, e, res)
  2537  				}
  2538  			})
  2539  		}
  2540  	})
  2541  }
  2542  
  2543  // testRPCProtocol runs a full set of tests using given callback to make actual
  2544  // calls. Some tests change the chain state, thus we reinitialize the chain from
  2545  // scratch here.
  2546  func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []byte) {
  2547  	chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t)
  2548  
  2549  	e := &executor{chain: chain, httpSrv: httpSrv}
  2550  	t.Run("single request", func(t *testing.T) {
  2551  		rpc := `{"jsonrpc": "2.0", "id": 1, "method": "%s", "params": %s}`
  2552  		for method, cases := range rpcTestCases {
  2553  			runTestCasesWithExecutor(t, e, rpc, method, cases, doRPCCall, checkErrGetResult)
  2554  		}
  2555  	})
  2556  	t.Run("batch with single request", func(t *testing.T) {
  2557  		for method, cases := range rpcTestCases {
  2558  			if method == "sendrawtransaction" {
  2559  				continue // cannot send the same transaction twice
  2560  			}
  2561  			rpc := `[{"jsonrpc": "2.0", "id": 1, "method": "%s", "params": %s}]`
  2562  			runTestCasesWithExecutor(t, e, rpc, method, cases, doRPCCall, checkErrGetBatchResult)
  2563  		}
  2564  	})
  2565  
  2566  	t.Run("batch with multiple requests", func(t *testing.T) {
  2567  		for method, cases := range rpcTestCases {
  2568  			if method == "sendrawtransaction" {
  2569  				continue // cannot send the same transaction twice
  2570  			}
  2571  			t.Run(method, func(t *testing.T) {
  2572  				rpc := `{"jsonrpc": "2.0", "id": %d, "method": "%s", "params": %s},`
  2573  				var resultRPC string
  2574  				for i, tc := range cases {
  2575  					resultRPC += fmt.Sprintf(rpc, i, method, tc.params)
  2576  				}
  2577  				resultRPC = `[` + resultRPC[:len(resultRPC)-1] + `]`
  2578  				body := doRPCCall(resultRPC, httpSrv.URL, t)
  2579  				var responses []neorpc.Response
  2580  				err := json.Unmarshal(body, &responses)
  2581  				require.Nil(t, err)
  2582  				for i, tc := range cases {
  2583  					var resp neorpc.Response
  2584  					for _, r := range responses {
  2585  						if bytes.Equal(r.ID, []byte(strconv.Itoa(i))) {
  2586  							resp = r
  2587  							break
  2588  						}
  2589  					}
  2590  					if tc.fail {
  2591  						require.NotNil(t, resp.Error)
  2592  						assert.NotEqual(t, 0, resp.Error.Code)
  2593  						assert.NotEqual(t, "", resp.Error.Message)
  2594  					} else {
  2595  						assert.Nil(t, resp.Error)
  2596  					}
  2597  					if tc.fail {
  2598  						return
  2599  					}
  2600  					expected, res := tc.getResultPair(e)
  2601  					err := json.Unmarshal(resp.Result, res)
  2602  					require.NoErrorf(t, err, "could not parse response: %s", resp.Result)
  2603  
  2604  					if tc.check == nil {
  2605  						assert.Equal(t, expected, res)
  2606  					} else {
  2607  						tc.check(t, e, res)
  2608  					}
  2609  				}
  2610  			})
  2611  		}
  2612  	})
  2613  
  2614  	t.Run("getapplicationlog for block", func(t *testing.T) {
  2615  		rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getapplicationlog", "params": ["%s"]}`
  2616  		body := doRPCCall(fmt.Sprintf(rpc, e.chain.GetHeaderHash(1).StringLE()), httpSrv.URL, t)
  2617  		data := checkErrGetResult(t, body, false, 0)
  2618  		var res result.ApplicationLog
  2619  		require.NoError(t, json.Unmarshal(data, &res))
  2620  		require.Equal(t, 2, len(res.Executions))
  2621  		require.Equal(t, trigger.OnPersist, res.Executions[0].Trigger)
  2622  		require.Equal(t, vmstate.Halt, res.Executions[0].VMState)
  2623  		require.Equal(t, trigger.PostPersist, res.Executions[1].Trigger)
  2624  		require.Equal(t, vmstate.Halt, res.Executions[1].VMState)
  2625  	})
  2626  	t.Run("submitblock", func(t *testing.T) {
  2627  		rpc := `{"jsonrpc": "2.0", "id": 1, "method": "submitblock", "params": ["%s"]}`
  2628  		t.Run("invalid signature", func(t *testing.T) {
  2629  			s := testchain.NewBlock(t, chain, 1, 0)
  2630  			s.Script.VerificationScript[8] ^= 0xff
  2631  			body := doRPCCall(fmt.Sprintf(rpc, encodeBinaryToString(t, s)), httpSrv.URL, t)
  2632  			checkErrGetResult(t, body, true, neorpc.ErrVerificationFailedCode)
  2633  		})
  2634  
  2635  		t.Run("invalid height", func(t *testing.T) {
  2636  			b := testchain.NewBlock(t, chain, 2, 0, newTxWithParams(t, chain, opcode.PUSH1, 10, 0, 1, false))
  2637  			body := doRPCCall(fmt.Sprintf(rpc, encodeBinaryToString(t, b)), httpSrv.URL, t)
  2638  			checkErrGetResult(t, body, true, neorpc.ErrAlreadyExistsCode)
  2639  		})
  2640  		t.Run("invalid script", func(t *testing.T) {
  2641  			b := testchain.NewBlock(t, chain, 1, 0, newTxWithParams(t, chain, 0xDD, 10, 0, 1, false))
  2642  			body := doRPCCall(fmt.Sprintf(rpc, encodeBinaryToString(t, b)), httpSrv.URL, t)
  2643  			checkErrGetResult(t, body, true, neorpc.ErrInvalidScriptCode)
  2644  		})
  2645  		t.Run("invalid ValidUntilBlock", func(t *testing.T) {
  2646  			b := testchain.NewBlock(t, chain, 1, 0, newTxWithParams(t, chain, opcode.PUSH1, 0, 0, 1, false))
  2647  			body := doRPCCall(fmt.Sprintf(rpc, encodeBinaryToString(t, b)), httpSrv.URL, t)
  2648  			checkErrGetResult(t, body, true, neorpc.ErrExpiredTransactionCode)
  2649  		})
  2650  		t.Run("invalid SystemFee", func(t *testing.T) {
  2651  			b := testchain.NewBlock(t, chain, 1, 0, newTxWithParams(t, chain, opcode.PUSH1, 10, 999999999999, 1, false))
  2652  			body := doRPCCall(fmt.Sprintf(rpc, encodeBinaryToString(t, b)), httpSrv.URL, t)
  2653  			checkErrGetResult(t, body, true, neorpc.ErrPolicyFailedCode)
  2654  		})
  2655  		t.Run("invalid NetworkFee", func(t *testing.T) {
  2656  			b := testchain.NewBlock(t, chain, 1, 0, newTxWithParams(t, chain, opcode.PUSH1, 10, 0, 0, false))
  2657  			body := doRPCCall(fmt.Sprintf(rpc, encodeBinaryToString(t, b)), httpSrv.URL, t)
  2658  			checkErrGetResult(t, body, true, neorpc.ErrInsufficientNetworkFeeCode)
  2659  		})
  2660  		t.Run("invalid attribute", func(t *testing.T) {
  2661  			b := testchain.NewBlock(t, chain, 1, 0, newTxWithParams(t, chain, opcode.PUSH1, 10, 0, 2, true))
  2662  			body := doRPCCall(fmt.Sprintf(rpc, encodeBinaryToString(t, b)), httpSrv.URL, t)
  2663  			checkErrGetResult(t, body, true, neorpc.ErrInvalidAttributeCode)
  2664  		})
  2665  		t.Run("insufficient funds", func(t *testing.T) {
  2666  			b := testchain.NewBlock(t, chain, 1, 0, newTxWithParams(t, chain, opcode.PUSH1, 10, 899999999999, 1, false))
  2667  			body := doRPCCall(fmt.Sprintf(rpc, encodeBinaryToString(t, b)), httpSrv.URL, t)
  2668  			checkErrGetResult(t, body, true, neorpc.ErrInsufficientFundsCode)
  2669  		})
  2670  		t.Run("positive", func(t *testing.T) {
  2671  			b := testchain.NewBlock(t, chain, 1, 0, newTxWithParams(t, chain, opcode.PUSH1, 10, 0, 1, false))
  2672  			body := doRPCCall(fmt.Sprintf(rpc, encodeBinaryToString(t, b)), httpSrv.URL, t)
  2673  			data := checkErrGetResult(t, body, false, 0)
  2674  			var res = new(result.RelayResult)
  2675  			require.NoError(t, json.Unmarshal(data, res))
  2676  			require.Equal(t, b.Hash(), res.Hash)
  2677  		})
  2678  	})
  2679  	t.Run("getproof", func(t *testing.T) {
  2680  		r, err := chain.GetStateModule().GetStateRoot(3)
  2681  		require.NoError(t, err)
  2682  
  2683  		rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getproof", "params": ["%s", "%s", "%s"]}`,
  2684  			r.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("testkey")))
  2685  		body := doRPCCall(rpc, httpSrv.URL, t)
  2686  		rawRes := checkErrGetResult(t, body, false, 0)
  2687  		res := new(result.ProofWithKey)
  2688  		require.NoError(t, json.Unmarshal(rawRes, res))
  2689  		h, _ := util.Uint160DecodeStringLE(testContractHash)
  2690  		skey := makeStorageKey(chain.GetContractState(h).ID, []byte("testkey"))
  2691  		require.Equal(t, skey, res.Key)
  2692  		require.True(t, len(res.Proof) > 0)
  2693  
  2694  		rpc = fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "verifyproof", "params": ["%s", "%s"]}`,
  2695  			r.Root.StringLE(), res.String())
  2696  		body = doRPCCall(rpc, httpSrv.URL, t)
  2697  		rawRes = checkErrGetResult(t, body, false, 0)
  2698  		vp := new(result.VerifyProof)
  2699  		require.NoError(t, json.Unmarshal(rawRes, vp))
  2700  		require.Equal(t, []byte("testvalue"), vp.Value)
  2701  	})
  2702  	t.Run("getstateroot", func(t *testing.T) {
  2703  		testRoot := func(t *testing.T, p string) {
  2704  			rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getstateroot", "params": [%s]}`, p)
  2705  			body := doRPCCall(rpc, httpSrv.URL, t)
  2706  			rawRes := checkErrGetResult(t, body, false, 0)
  2707  
  2708  			res := &state.MPTRoot{}
  2709  			require.NoError(t, json.Unmarshal(rawRes, res))
  2710  			require.NotEqual(t, util.Uint256{}, res.Root) // be sure this test uses valid height
  2711  
  2712  			expected, err := e.chain.GetStateModule().GetStateRoot(5)
  2713  			require.NoError(t, err)
  2714  			require.Equal(t, expected, res)
  2715  		}
  2716  		t.Run("ByHeight", func(t *testing.T) { testRoot(t, strconv.FormatInt(5, 10)) })
  2717  		t.Run("ByHash", func(t *testing.T) { testRoot(t, `"`+chain.GetHeaderHash(5).StringLE()+`"`) })
  2718  		t.Run("20", func(t *testing.T) {
  2719  			rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getstateroot", "params": [20]}`
  2720  			body := doRPCCall(rpc, httpSrv.URL, t)
  2721  			rawRes := checkErrGetResult(t, body, false, 0)
  2722  
  2723  			res := &state.MPTRoot{}
  2724  			require.NoError(t, json.Unmarshal(rawRes, res))
  2725  			require.Equal(t, block20StateRootLE, res.Root.StringLE())
  2726  		})
  2727  	})
  2728  	t.Run("getstate", func(t *testing.T) {
  2729  		rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getstate", "params": [%s]}`
  2730  		testGetState := func(t *testing.T, p string, expected string) {
  2731  			body := doRPCCall(fmt.Sprintf(rpc, p), httpSrv.URL, t)
  2732  			rawRes := checkErrGetResult(t, body, false, 0)
  2733  
  2734  			var actual string
  2735  			require.NoError(t, json.Unmarshal(rawRes, &actual))
  2736  			require.Equal(t, expected, actual)
  2737  		}
  2738  		t.Run("good: historical state", func(t *testing.T) {
  2739  			root, err := e.chain.GetStateModule().GetStateRoot(4)
  2740  			require.NoError(t, err)
  2741  			// `testkey`-`testvalue` pair was put to the contract storage at block #3
  2742  			params := fmt.Sprintf(`"%s", "%s", "%s"`, root.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("testkey")))
  2743  			testGetState(t, params, base64.StdEncoding.EncodeToString([]byte("testvalue")))
  2744  		})
  2745  		t.Run("negative: invalid key", func(t *testing.T) {
  2746  			root, err := e.chain.GetStateModule().GetStateRoot(4)
  2747  			require.NoError(t, err)
  2748  			// `testkey`-`testvalue` pair was put to the contract storage at block #3
  2749  			params := fmt.Sprintf(`"%s", "%s", "%s"`, root.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("invalidkey")))
  2750  			body := doRPCCall(fmt.Sprintf(rpc, params), httpSrv.URL, t)
  2751  			checkErrGetResult(t, body, true, neorpc.InvalidParamsCode)
  2752  		})
  2753  		t.Run("good: fresh state", func(t *testing.T) {
  2754  			root, err := e.chain.GetStateModule().GetStateRoot(16)
  2755  			require.NoError(t, err)
  2756  			// `testkey`-`newtestvalue` pair was put to the contract storage at block #16
  2757  			params := fmt.Sprintf(`"%s", "%s", "%s"`, root.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("testkey")))
  2758  			testGetState(t, params, base64.StdEncoding.EncodeToString([]byte("newtestvalue")))
  2759  		})
  2760  	})
  2761  	t.Run("findstates", func(t *testing.T) {
  2762  		testFindStates := func(t *testing.T, p string, root util.Uint256, expected result.FindStates) {
  2763  			rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "findstates", "params": [%s]}`, p)
  2764  			body := doRPCCall(rpc, httpSrv.URL, t)
  2765  			rawRes := checkErrGetResult(t, body, false, 0)
  2766  
  2767  			var actual result.FindStates
  2768  			require.NoError(t, json.Unmarshal(rawRes, &actual))
  2769  			require.Equal(t, expected.Results, actual.Results)
  2770  
  2771  			checkProof := func(t *testing.T, proof *result.ProofWithKey, value []byte) {
  2772  				rpc = fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "verifyproof", "params": ["%s", "%s"]}`,
  2773  					root.StringLE(), proof.String())
  2774  				body = doRPCCall(rpc, httpSrv.URL, t)
  2775  				rawRes = checkErrGetResult(t, body, false, 0)
  2776  				vp := new(result.VerifyProof)
  2777  				require.NoError(t, json.Unmarshal(rawRes, vp))
  2778  				require.Equal(t, value, vp.Value)
  2779  			}
  2780  			if len(actual.Results) > 0 {
  2781  				checkProof(t, actual.FirstProof, actual.Results[0].Value)
  2782  			}
  2783  			if len(actual.Results) > 1 {
  2784  				checkProof(t, actual.LastProof, actual.Results[len(actual.Results)-1].Value)
  2785  			}
  2786  			require.Equal(t, expected.Truncated, actual.Truncated)
  2787  		}
  2788  		t.Run("good: no prefix, no limit", func(t *testing.T) {
  2789  			// pairs for this test where put to the contract storage at block #16
  2790  			root, err := e.chain.GetStateModule().GetStateRoot(16)
  2791  			require.NoError(t, err)
  2792  			params := fmt.Sprintf(`"%s", "%s", "%s"`, root.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("aa")))
  2793  			testFindStates(t, params, root.Root, result.FindStates{
  2794  				Results: []result.KeyValue{
  2795  					{Key: []byte("aa10"), Value: []byte("v2")},
  2796  					{Key: []byte("aa50"), Value: []byte("v3")},
  2797  					{Key: []byte("aa"), Value: []byte("v1")},
  2798  				},
  2799  				Truncated: false,
  2800  			})
  2801  		})
  2802  		t.Run("good: empty prefix, no limit", func(t *testing.T) {
  2803  			// empty prefix should be considered as no prefix specified.
  2804  			root, err := e.chain.GetStateModule().GetStateRoot(16)
  2805  			require.NoError(t, err)
  2806  			params := fmt.Sprintf(`"%s", "%s", "%s", ""`, root.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("aa")))
  2807  			testFindStates(t, params, root.Root, result.FindStates{
  2808  				Results: []result.KeyValue{
  2809  					{Key: []byte("aa10"), Value: []byte("v2")},
  2810  					{Key: []byte("aa50"), Value: []byte("v3")},
  2811  					{Key: []byte("aa"), Value: []byte("v1")},
  2812  				},
  2813  				Truncated: false,
  2814  			})
  2815  		})
  2816  		t.Run("good: empty prefix, no limit, no data", func(t *testing.T) {
  2817  			// empty prefix should be considered as no prefix specified.
  2818  			root, err := e.chain.GetStateModule().GetStateRoot(20)
  2819  			require.NoError(t, err)
  2820  			stdHash, _ := e.chain.GetNativeContractScriptHash(nativenames.StdLib) // It has no data.
  2821  			params := fmt.Sprintf(`"%s", "%s", ""`, root.Root.StringLE(), stdHash.StringLE())
  2822  			testFindStates(t, params, root.Root, result.FindStates{
  2823  				Results:   []result.KeyValue{},
  2824  				Truncated: false,
  2825  			})
  2826  		})
  2827  		t.Run("good: with prefix, no limit", func(t *testing.T) {
  2828  			// pairs for this test where put to the contract storage at block #16
  2829  			root, err := e.chain.GetStateModule().GetStateRoot(16)
  2830  			require.NoError(t, err)
  2831  			params := fmt.Sprintf(`"%s", "%s", "%s", "%s"`, root.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("aa")), base64.StdEncoding.EncodeToString([]byte("aa10")))
  2832  			testFindStates(t, params, root.Root, result.FindStates{
  2833  				Results: []result.KeyValue{
  2834  					{Key: []byte("aa50"), Value: []byte("v3")},
  2835  				},
  2836  				Truncated: false,
  2837  			})
  2838  		})
  2839  		t.Run("good: empty prefix, with limit", func(t *testing.T) {
  2840  			for limit := 2; limit < 5; limit++ {
  2841  				// pairs for this test where put to the contract storage at block #16
  2842  				root, err := e.chain.GetStateModule().GetStateRoot(16)
  2843  				require.NoError(t, err)
  2844  				params := fmt.Sprintf(`"%s", "%s", "%s", "", %d`, root.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("aa")), limit)
  2845  				expected := result.FindStates{
  2846  					Results: []result.KeyValue{
  2847  						{Key: []byte("aa10"), Value: []byte("v2")},
  2848  						{Key: []byte("aa50"), Value: []byte("v3")},
  2849  					},
  2850  					Truncated: limit == 2,
  2851  				}
  2852  				if limit != 2 {
  2853  					expected.Results = append(expected.Results, result.KeyValue{Key: []byte("aa"), Value: []byte("v1")})
  2854  				}
  2855  				testFindStates(t, params, root.Root, expected)
  2856  			}
  2857  		})
  2858  		t.Run("good: with prefix, with limit", func(t *testing.T) {
  2859  			// pairs for this test where put to the contract storage at block #16
  2860  			root, err := e.chain.GetStateModule().GetStateRoot(16)
  2861  			require.NoError(t, err)
  2862  			params := fmt.Sprintf(`"%s", "%s", "%s", "%s", %d`, root.Root.StringLE(), testContractHash, base64.StdEncoding.EncodeToString([]byte("aa")), base64.StdEncoding.EncodeToString([]byte("aa00")), 1)
  2863  			testFindStates(t, params, root.Root, result.FindStates{
  2864  				Results: []result.KeyValue{
  2865  					{Key: []byte("aa10"), Value: []byte("v2")},
  2866  				},
  2867  				Truncated: true,
  2868  			})
  2869  		})
  2870  	})
  2871  
  2872  	t.Run("getrawtransaction", func(t *testing.T) {
  2873  		block, _ := chain.GetBlock(chain.GetHeaderHash(1))
  2874  		tx := block.Transactions[0]
  2875  		rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s"]}"`, tx.Hash().StringLE())
  2876  		body := doRPCCall(rpc, httpSrv.URL, t)
  2877  		result := checkErrGetResult(t, body, false, 0)
  2878  		var res string
  2879  		err := json.Unmarshal(result, &res)
  2880  		require.NoErrorf(t, err, "could not parse response: %s", result)
  2881  		txBin, err := testserdes.EncodeBinary(tx)
  2882  		require.NoError(t, err)
  2883  		expected := base64.StdEncoding.EncodeToString(txBin)
  2884  		assert.Equal(t, expected, res)
  2885  	})
  2886  
  2887  	t.Run("getrawtransaction 2 arguments", func(t *testing.T) {
  2888  		block, _ := chain.GetBlock(chain.GetHeaderHash(1))
  2889  		tx := block.Transactions[0]
  2890  		rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s", 0]}"`, tx.Hash().StringLE())
  2891  		body := doRPCCall(rpc, httpSrv.URL, t)
  2892  		result := checkErrGetResult(t, body, false, 0)
  2893  		var res string
  2894  		err := json.Unmarshal(result, &res)
  2895  		require.NoErrorf(t, err, "could not parse response: %s", result)
  2896  		txBin, err := testserdes.EncodeBinary(tx)
  2897  		require.NoError(t, err)
  2898  		expected := base64.StdEncoding.EncodeToString(txBin)
  2899  		assert.Equal(t, expected, res)
  2900  	})
  2901  
  2902  	t.Run("getrawtransaction 2 arguments, verbose", func(t *testing.T) {
  2903  		block, _ := chain.GetBlock(chain.GetHeaderHash(1))
  2904  		TXHash := block.Transactions[0].Hash()
  2905  		_ = block.Transactions[0].Size()
  2906  		rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s", 1]}"`, TXHash.StringLE())
  2907  		body := doRPCCall(rpc, httpSrv.URL, t)
  2908  		txOut := checkErrGetResult(t, body, false, 0)
  2909  		actual := result.TransactionOutputRaw{Transaction: transaction.Transaction{}}
  2910  		err := json.Unmarshal(txOut, &actual)
  2911  		require.NoErrorf(t, err, "could not parse response: %s", txOut)
  2912  
  2913  		assert.Equal(t, *block.Transactions[0], actual.Transaction)
  2914  		assert.Equal(t, 24, actual.Confirmations)
  2915  		assert.Equal(t, TXHash, actual.Transaction.Hash())
  2916  	})
  2917  
  2918  	t.Run("getblockheader_positive", func(t *testing.T) {
  2919  		rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getblockheader", "params": %s}`
  2920  		testHeaderHash := chain.GetHeaderHash(1).StringLE()
  2921  		hdr := e.getHeader(testHeaderHash)
  2922  
  2923  		runCase := func(t *testing.T, rpc string, expected, actual any) {
  2924  			body := doRPCCall(rpc, httpSrv.URL, t)
  2925  			data := checkErrGetResult(t, body, false, 0)
  2926  			require.NoError(t, json.Unmarshal(data, actual))
  2927  			require.Equal(t, expected, actual)
  2928  		}
  2929  
  2930  		t.Run("no verbose", func(t *testing.T) {
  2931  			w := io.NewBufBinWriter()
  2932  			hdr.EncodeBinary(w.BinWriter)
  2933  			require.NoError(t, w.Err)
  2934  			encoded := base64.StdEncoding.EncodeToString(w.Bytes())
  2935  
  2936  			t.Run("missing", func(t *testing.T) {
  2937  				runCase(t, fmt.Sprintf(rpc, `["`+testHeaderHash+`"]`), &encoded, new(string))
  2938  			})
  2939  
  2940  			t.Run("verbose=0", func(t *testing.T) {
  2941  				runCase(t, fmt.Sprintf(rpc, `["`+testHeaderHash+`", 0]`), &encoded, new(string))
  2942  			})
  2943  
  2944  			t.Run("by number", func(t *testing.T) {
  2945  				runCase(t, fmt.Sprintf(rpc, `[1]`), &encoded, new(string))
  2946  			})
  2947  		})
  2948  
  2949  		t.Run("verbose != 0", func(t *testing.T) {
  2950  			nextHash := chain.GetHeaderHash(hdr.Index + 1)
  2951  			expected := &result.Header{
  2952  				Header: *hdr,
  2953  				BlockMetadata: result.BlockMetadata{
  2954  					Size:          io.GetVarSize(hdr),
  2955  					NextBlockHash: &nextHash,
  2956  					Confirmations: e.chain.BlockHeight() - hdr.Index + 1,
  2957  				},
  2958  			}
  2959  
  2960  			rpc := fmt.Sprintf(rpc, `["`+testHeaderHash+`", 2]`)
  2961  			runCase(t, rpc, expected, new(result.Header))
  2962  		})
  2963  	})
  2964  
  2965  	t.Run("getrawmempool", func(t *testing.T) {
  2966  		mp := chain.GetMemPool()
  2967  		// `expected` stores hashes of previously added txs
  2968  		expected := make([]util.Uint256, 0)
  2969  		for _, tx := range mp.GetVerifiedTransactions() {
  2970  			expected = append(expected, tx.Hash())
  2971  		}
  2972  		for i := 0; i < 5; i++ {
  2973  			tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
  2974  			tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
  2975  			assert.NoError(t, mp.Add(tx, &FeerStub{}))
  2976  			expected = append(expected, tx.Hash())
  2977  		}
  2978  
  2979  		rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getrawmempool", "params": []}`
  2980  		body := doRPCCall(rpc, httpSrv.URL, t)
  2981  		res := checkErrGetResult(t, body, false, 0)
  2982  
  2983  		var actual []util.Uint256
  2984  		err := json.Unmarshal(res, &actual)
  2985  		require.NoErrorf(t, err, "could not parse response: %s", res)
  2986  
  2987  		assert.ElementsMatch(t, expected, actual)
  2988  	})
  2989  
  2990  	t.Run("getnep17transfers", func(t *testing.T) {
  2991  		testNEP17T := func(t *testing.T, start, stop, limit, page int, sent, rcvd []int) {
  2992  			ps := []string{`"` + testchain.PrivateKeyByID(0).Address() + `"`}
  2993  			if start != 0 {
  2994  				h, err := e.chain.GetHeader(e.chain.GetHeaderHash(uint32(start)))
  2995  				var ts uint64
  2996  				if err == nil {
  2997  					ts = h.Timestamp
  2998  				} else {
  2999  					ts = uint64(time.Now().UnixNano() / 1_000_000)
  3000  				}
  3001  				ps = append(ps, strconv.FormatUint(ts, 10))
  3002  			}
  3003  			if stop != 0 {
  3004  				h, err := e.chain.GetHeader(e.chain.GetHeaderHash(uint32(stop)))
  3005  				var ts uint64
  3006  				if err == nil {
  3007  					ts = h.Timestamp
  3008  				} else {
  3009  					ts = uint64(time.Now().UnixNano() / 1_000_000)
  3010  				}
  3011  				ps = append(ps, strconv.FormatUint(ts, 10))
  3012  			}
  3013  			if limit != 0 {
  3014  				ps = append(ps, strconv.FormatInt(int64(limit), 10))
  3015  			}
  3016  			if page != 0 {
  3017  				ps = append(ps, strconv.FormatInt(int64(page), 10))
  3018  			}
  3019  			p := strings.Join(ps, ", ")
  3020  			rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getnep17transfers", "params": [%s]}`, p)
  3021  			body := doRPCCall(rpc, httpSrv.URL, t)
  3022  			res := checkErrGetResult(t, body, false, 0)
  3023  			actual := new(result.NEP17Transfers)
  3024  			require.NoError(t, json.Unmarshal(res, actual))
  3025  			checkNep17TransfersAux(t, e, actual, sent, rcvd)
  3026  		}
  3027  		t.Run("time frame only", func(t *testing.T) { testNEP17T(t, 4, 5, 0, 0, []int{19, 20, 21, 22}, []int{3, 4}) })
  3028  		t.Run("no res", func(t *testing.T) { testNEP17T(t, 100, 100, 0, 0, []int{}, []int{}) })
  3029  		t.Run("limit", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 0, []int{16, 17}, []int{2}) })
  3030  		t.Run("limit 2", func(t *testing.T) { testNEP17T(t, 4, 5, 2, 0, []int{19}, []int{3}) })
  3031  		t.Run("limit with page", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 1, []int{18, 19}, []int{3}) })
  3032  		t.Run("limit with page 2", func(t *testing.T) { testNEP17T(t, 1, 7, 3, 2, []int{20, 21}, []int{4}) })
  3033  	})
  3034  
  3035  	prepareIteratorSession := func(t *testing.T) (uuid.UUID, uuid.UUID) {
  3036  		rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "invokefunction", "params": ["%s", "iterateOverValues"]}"`, storageContractHash)
  3037  		body := doRPCCall(rpc, httpSrv.URL, t)
  3038  		resp := checkErrGetResult(t, body, false, 0)
  3039  		res := new(result.Invoke)
  3040  		err := json.Unmarshal(resp, &res)
  3041  		require.NoErrorf(t, err, "could not parse response: %s", resp)
  3042  		require.NotEmpty(t, res.Session)
  3043  		require.Equal(t, 1, len(res.Stack))
  3044  		require.Equal(t, stackitem.InteropT, res.Stack[0].Type())
  3045  		iterator, ok := res.Stack[0].Value().(result.Iterator)
  3046  		require.True(t, ok)
  3047  		require.NotEmpty(t, iterator.ID)
  3048  		return res.Session, *iterator.ID
  3049  	}
  3050  	t.Run("traverseiterator", func(t *testing.T) {
  3051  		t.Run("sessions disabled", func(t *testing.T) {
  3052  			_, _, httpSrv2 := initClearServerWithCustomConfig(t, func(c *config.Config) {
  3053  				c.ApplicationConfiguration.RPC.SessionEnabled = false
  3054  			})
  3055  			body := doRPCCall(`{"jsonrpc": "2.0", "id": 1, "method": "traverseiterator", "params": []}"`, httpSrv2.URL, t)
  3056  			checkErrGetResult(t, body, true, neorpc.ErrSessionsDisabledCode)
  3057  		})
  3058  		t.Run("good", func(t *testing.T) {
  3059  			sID, iID := prepareIteratorSession(t)
  3060  			expectedCount := 99
  3061  			rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "traverseiterator", "params": ["%s", "%s", %d]}"`, sID.String(), iID.String(), expectedCount)
  3062  			body := doRPCCall(rpc, httpSrv.URL, t)
  3063  			resp := checkErrGetResult(t, body, false, 0)
  3064  			res := new([]json.RawMessage)
  3065  			require.NoError(t, json.Unmarshal(resp, res))
  3066  			require.Equal(t, expectedCount, len(*res))
  3067  		})
  3068  		t.Run("invalid session id", func(t *testing.T) {
  3069  			_, iID := prepareIteratorSession(t)
  3070  			rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "traverseiterator", "params": ["not-a-uuid", "%s", %d]}"`, iID.String(), 1)
  3071  			body := doRPCCall(rpc, httpSrv.URL, t)
  3072  			checkErrGetResult(t, body, true, neorpc.InvalidParamsCode, "invalid session ID: not a valid UUID")
  3073  		})
  3074  		t.Run("invalid iterator id", func(t *testing.T) {
  3075  			sID, _ := prepareIteratorSession(t)
  3076  			rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "traverseiterator", "params": ["%s", "not-a-uuid", %d]}"`, sID.String(), 1)
  3077  			body := doRPCCall(rpc, httpSrv.URL, t)
  3078  			checkErrGetResult(t, body, true, neorpc.InvalidParamsCode, "invalid iterator ID: not a valid UUID")
  3079  		})
  3080  		t.Run("invalid items count", func(t *testing.T) {
  3081  			sID, iID := prepareIteratorSession(t)
  3082  			rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "traverseiterator", "params": ["%s", "%s"]}"`, sID.String(), iID.String())
  3083  			body := doRPCCall(rpc, httpSrv.URL, t)
  3084  			checkErrGetResult(t, body, true, neorpc.InvalidParamsCode, "invalid iterator items count")
  3085  		})
  3086  		t.Run("items count is not an int32", func(t *testing.T) {
  3087  			sID, iID := prepareIteratorSession(t)
  3088  			rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "traverseiterator", "params": ["%s", "%s", %d]}"`, sID.String(), iID.String(), math.MaxInt32+1)
  3089  			body := doRPCCall(rpc, httpSrv.URL, t)
  3090  			checkErrGetResult(t, body, true, neorpc.InvalidParamsCode, "invalid iterator items count: not an int32")
  3091  		})
  3092  		t.Run("count is out of range", func(t *testing.T) {
  3093  			sID, iID := prepareIteratorSession(t)
  3094  			rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "traverseiterator", "params": ["%s", "%s", %d]}"`, sID.String(), iID.String(), config.DefaultMaxIteratorResultItems+1)
  3095  			body := doRPCCall(rpc, httpSrv.URL, t)
  3096  			checkErrGetResult(t, body, true, neorpc.InvalidParamsCode, fmt.Sprintf("iterator items count (%d) is out of range (%d at max)", config.DefaultMaxIteratorResultItems+1, config.DefaultMaxIteratorResultItems))
  3097  		})
  3098  		t.Run("unknown session", func(t *testing.T) {
  3099  			_, iID := prepareIteratorSession(t)
  3100  			rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "traverseiterator", "params": ["%s", "%s", %d]}"`, uuid.NewString(), iID.String(), 1)
  3101  			body := doRPCCall(rpc, httpSrv.URL, t)
  3102  			checkErrGetResult(t, body, true, neorpc.ErrUnknownSessionCode)
  3103  		})
  3104  		t.Run("unknown iterator", func(t *testing.T) {
  3105  			sID, _ := prepareIteratorSession(t)
  3106  			rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "traverseiterator", "params": ["%s", "%s", %d]}"`, sID.String(), uuid.NewString(), 1)
  3107  			body := doRPCCall(rpc, httpSrv.URL, t)
  3108  			checkErrGetResult(t, body, true, neorpc.ErrUnknownIteratorCode)
  3109  		})
  3110  	})
  3111  	t.Run("terminatesession", func(t *testing.T) {
  3112  		rpc := `{"jsonrpc": "2.0", "id": 1, "method": "terminatesession", "params": ["%s"]}"`
  3113  		t.Run("sessions disabled", func(t *testing.T) {
  3114  			_, _, httpSrv2 := initClearServerWithCustomConfig(t, func(c *config.Config) {
  3115  				c.ApplicationConfiguration.RPC.SessionEnabled = false
  3116  			})
  3117  			body := doRPCCall(fmt.Sprintf(rpc, uuid.NewString()), httpSrv2.URL, t)
  3118  			checkErrGetResult(t, body, true, neorpc.ErrSessionsDisabledCode)
  3119  		})
  3120  		t.Run("true", func(t *testing.T) {
  3121  			sID, _ := prepareIteratorSession(t)
  3122  			body := doRPCCall(fmt.Sprintf(rpc, sID.String()), httpSrv.URL, t)
  3123  			resp := checkErrGetResult(t, body, false, 0)
  3124  			res := new(bool)
  3125  			require.NoError(t, json.Unmarshal(resp, res))
  3126  			require.Equal(t, true, *res)
  3127  		})
  3128  		t.Run("false", func(t *testing.T) {
  3129  			body := doRPCCall(fmt.Sprintf(rpc, uuid.NewString()), httpSrv.URL, t)
  3130  			checkErrGetResult(t, body, true, neorpc.ErrUnknownSessionCode)
  3131  		})
  3132  		t.Run("expired", func(t *testing.T) {
  3133  			_, _ = prepareIteratorSession(t)
  3134  			// Wait until session is terminated by timer.
  3135  			require.Eventually(t, func() bool {
  3136  				rpcSrv.sessionsLock.Lock()
  3137  				defer rpcSrv.sessionsLock.Unlock()
  3138  				return len(rpcSrv.sessions) == 0
  3139  			}, 2*time.Duration(rpcSrv.config.SessionExpirationTime)*time.Second, 10*time.Millisecond)
  3140  		})
  3141  	})
  3142  	t.Run("calculatenetworkfee", func(t *testing.T) {
  3143  		t.Run("no parameters", func(t *testing.T) {
  3144  			body := doRPCCall(`{"jsonrpc": "2.0", "id": 1, "method": "calculatenetworkfee", "params": []}"`, httpSrv.URL, t)
  3145  			_ = checkErrGetResult(t, body, true, neorpc.InvalidParamsCode, "Invalid params")
  3146  		})
  3147  		t.Run("non-base64 parameter", func(t *testing.T) {
  3148  			body := doRPCCall(`{"jsonrpc": "2.0", "id": 1, "method": "calculatenetworkfee", "params": ["noatbase64"]}"`, httpSrv.URL, t)
  3149  			_ = checkErrGetResult(t, body, true, neorpc.InvalidParamsCode, "Invalid params")
  3150  		})
  3151  		t.Run("non-transaction parameter", func(t *testing.T) {
  3152  			body := doRPCCall(`{"jsonrpc": "2.0", "id": 1, "method": "calculatenetworkfee", "params": ["bm90IGEgdHJhbnNhY3Rpb24K"]}"`, httpSrv.URL, t)
  3153  			_ = checkErrGetResult(t, body, true, neorpc.InvalidParamsCode, "Invalid params")
  3154  		})
  3155  		calcReqExactly := func(t *testing.T, tx string) []byte {
  3156  			rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "calculatenetworkfee", "params": ["%s"]}"`, tx)
  3157  			return doRPCCall(rpc, httpSrv.URL, t)
  3158  		}
  3159  		calcReq := func(t *testing.T, tx *transaction.Transaction) []byte {
  3160  			return calcReqExactly(t, base64.StdEncoding.EncodeToString(tx.Bytes()))
  3161  		}
  3162  		t.Run("non-contract with zero verification", func(t *testing.T) {
  3163  			tx := &transaction.Transaction{
  3164  				Script:  []byte{byte(opcode.RET)},
  3165  				Signers: []transaction.Signer{{Account: util.Uint160{1, 2, 3}, Scopes: transaction.CalledByEntry}},
  3166  				Scripts: []transaction.Witness{{
  3167  					InvocationScript:   []byte{},
  3168  					VerificationScript: []byte{},
  3169  				}},
  3170  			}
  3171  			body := calcReq(t, tx)
  3172  			_ = checkErrGetResult(t, body, true, neorpc.ErrInvalidVerificationFunctionCode, "signer 0 has no verification script and no deployed contract")
  3173  		})
  3174  		t.Run("contract with no verify", func(t *testing.T) {
  3175  			tx := &transaction.Transaction{
  3176  				Script:  []byte{byte(opcode.RET)},
  3177  				Signers: []transaction.Signer{{Account: nnsHash, Scopes: transaction.CalledByEntry}},
  3178  				Scripts: []transaction.Witness{{
  3179  					InvocationScript:   []byte{},
  3180  					VerificationScript: []byte{},
  3181  				}},
  3182  			}
  3183  			body := calcReq(t, tx)
  3184  			_ = checkErrGetResult(t, body, true, neorpc.ErrInvalidVerificationFunctionCode, "signer 0 has no verify method in deployed contract")
  3185  		})
  3186  		t.Run("execution limit, fail", func(t *testing.T) {
  3187  			// 1_6000_0000 GAS with the default 1.5 allowed by Policy
  3188  			verifScript := []byte{byte(opcode.PUSHINT32), 0x00, 0x58, 0x89, 0x09, byte(opcode.SYSCALL), 0, 0, 0, 0, byte(opcode.PUSHT)}
  3189  			binary.LittleEndian.PutUint32(verifScript[6:], interopnames.ToID([]byte(interopnames.SystemRuntimeBurnGas)))
  3190  			tx := &transaction.Transaction{
  3191  				Script:  []byte{byte(opcode.RET)},
  3192  				Signers: []transaction.Signer{{Account: hash.Hash160(verifScript)}},
  3193  				Scripts: []transaction.Witness{{
  3194  					InvocationScript:   []byte{byte(opcode.NOP)},
  3195  					VerificationScript: verifScript,
  3196  				}},
  3197  			}
  3198  			body := calcReq(t, tx)
  3199  			_ = checkErrGetResult(t, body, true, neorpc.ErrInvalidSignatureCode, "GAS limit exceeded")
  3200  		})
  3201  		checkCalc := func(t *testing.T, tx *transaction.Transaction, fee int64) {
  3202  			resp := checkErrGetResult(t, calcReq(t, tx), false, 0)
  3203  			res := new(result.NetworkFee)
  3204  			require.NoError(t, json.Unmarshal(resp, res))
  3205  			require.Equal(t, fee, res.Value)
  3206  		}
  3207  		t.Run("simple GAS transfer", func(t *testing.T) {
  3208  			priv0 := testchain.PrivateKeyByID(0)
  3209  			script, err := smartcontract.CreateCallWithAssertScript(chain.UtilityTokenHash(), "transfer",
  3210  				priv0.GetScriptHash(), priv0.GetScriptHash(), 1, nil)
  3211  			require.NoError(t, err)
  3212  			tx := &transaction.Transaction{
  3213  				Script:  script,
  3214  				Signers: []transaction.Signer{{Account: priv0.GetScriptHash(), Scopes: transaction.CalledByEntry}},
  3215  				Scripts: []transaction.Witness{{
  3216  					InvocationScript:   []byte{},
  3217  					VerificationScript: priv0.PublicKey().GetVerificationScript(),
  3218  				}},
  3219  			}
  3220  			checkCalc(t, tx, 1228520) // Perfectly matches FeeIsSignatureContractDetailed() C# test.
  3221  		})
  3222  		t.Run("multisignature tx", func(t *testing.T) {
  3223  			priv0 := testchain.PrivateKeyByID(0)
  3224  			priv1 := testchain.PrivateKeyByID(1)
  3225  			accScript, err := smartcontract.CreateDefaultMultiSigRedeemScript(keys.PublicKeys{priv0.PublicKey(), priv1.PublicKey()})
  3226  			require.NoError(t, err)
  3227  			multiAcc := hash.Hash160(accScript)
  3228  			txScript, err := smartcontract.CreateCallWithAssertScript(chain.UtilityTokenHash(), "transfer",
  3229  				multiAcc, priv0.GetScriptHash(), 1, nil)
  3230  			require.NoError(t, err)
  3231  			tx := &transaction.Transaction{
  3232  				Script:  txScript,
  3233  				Signers: []transaction.Signer{{Account: multiAcc, Scopes: transaction.CalledByEntry}},
  3234  				Scripts: []transaction.Witness{{
  3235  					InvocationScript:   []byte{},
  3236  					VerificationScript: accScript,
  3237  				}},
  3238  			}
  3239  			checkCalc(t, tx, 2315100) // Perfectly matches FeeIsMultiSigContract() C# test.
  3240  		})
  3241  		t.Run("Koblitz custom multisignature witness", func(t *testing.T) {
  3242  			tx := "AAIAAACWP5gAAAAAAAAAAAAAAAAAAgAAAAEGyZgQyJQyWjzvqUZochi8rGE9RQEAVgsVDBQBAgMAAAAAAAAAAAAAAAAAAAAAAAwUBsmYEMiUMlo876lGaHIYvKxhPUUUwB8MCHRyYW5zZmVyDBTPduKL0AYsSkeO41VhARMZ88+k0kFifVtSAcYMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD9CAETDCECbSVpJ0BN2xiveIGNU0LzEz4V7FUAp1NV8s7YI4kq9eQMIQOaHY3bh+3OmXuU9t72Pj62loLM7gZDgXJwnBV2zO4u1wwhAsvP18ohoYHcHBPt4wwPqAOstOhazEegr4klYmDlWpzeDCED9EsK7L0qFMP1QpBBfKMMVXPLa894ONINLRtjtBLBM6oUVwcAdW3AcXZDbigDOG7AcEHF+6DgAwAAAAABAAAAnhSNQS1RCDAQzotyEHMQdGtuuGxtuJIkQgB6aGvOaWzOahTAEAwPdmVyaWZ5V2l0aEVDRHNhDBQb9XWrEYlohBNhCjWhKIbN4LZsckFifVtSa55zbJx0IrlrbrM="
  3243  			resp := checkErrGetResult(t, calcReqExactly(t, tx), false, 0)
  3244  			res := new(result.NetworkFee)
  3245  			require.NoError(t, json.Unmarshal(resp, res))
  3246  			require.Equal(t, int64(8992070), res.Value)
  3247  		})
  3248  		checkContract := func(t *testing.T, verAcc util.Uint160, invoc []byte, fee int64) {
  3249  			txScript, err := smartcontract.CreateCallWithAssertScript(chain.UtilityTokenHash(), "transfer",
  3250  				verAcc, verAcc, 1, nil)
  3251  			require.NoError(t, err)
  3252  			tx := &transaction.Transaction{
  3253  				Script:  txScript,
  3254  				Signers: []transaction.Signer{{Account: verAcc, Scopes: transaction.CalledByEntry}},
  3255  				Scripts: []transaction.Witness{{
  3256  					InvocationScript:   invoc,
  3257  					VerificationScript: []byte{},
  3258  				}},
  3259  			}
  3260  			checkCalc(t, tx, fee)
  3261  		}
  3262  		t.Run("contract-based verification", func(t *testing.T) {
  3263  			verAcc, err := util.Uint160DecodeStringLE(verifyContractHash)
  3264  			require.NoError(t, err)
  3265  			checkContract(t, verAcc, []byte{}, 636610) // No C# match, but we believe it's OK.
  3266  		})
  3267  		t.Run("contract-based verification with parameters", func(t *testing.T) {
  3268  			verAcc, err := util.Uint160DecodeStringLE(verifyWithArgsContractHash)
  3269  			require.NoError(t, err)
  3270  			checkContract(t, verAcc, []byte{}, 244130) // No C# match, but we believe it's OK and it differs from the one above.
  3271  		})
  3272  		t.Run("contract-based verification with invocation script", func(t *testing.T) {
  3273  			verAcc, err := util.Uint160DecodeStringLE(verifyWithArgsContractHash)
  3274  			require.NoError(t, err)
  3275  			invocWriter := io.NewBufBinWriter()
  3276  			emit.Bool(invocWriter.BinWriter, false)
  3277  			emit.Int(invocWriter.BinWriter, 5)
  3278  			emit.String(invocWriter.BinWriter, "")
  3279  			invocScript := invocWriter.Bytes()
  3280  			checkContract(t, verAcc, invocScript, 146960) // No C# match, but we believe it's OK and it has a specific invocation script overriding anything server-side.
  3281  		})
  3282  		t.Run("execution limit, ok", func(t *testing.T) {
  3283  			// 1_4000_0000 GAS with the default 1.5 allowed by Policy
  3284  			verifScript := []byte{byte(opcode.PUSHINT32), 0x00, 0x3b, 0x58, 0x08, byte(opcode.SYSCALL), 0, 0, 0, 0, byte(opcode.PUSHT)}
  3285  			binary.LittleEndian.PutUint32(verifScript[6:], interopnames.ToID([]byte(interopnames.SystemRuntimeBurnGas)))
  3286  			tx := &transaction.Transaction{
  3287  				Script:  []byte{byte(opcode.RET)},
  3288  				Signers: []transaction.Signer{{Account: hash.Hash160(verifScript)}},
  3289  				Scripts: []transaction.Witness{{
  3290  					InvocationScript:   []byte{byte(opcode.NOP)},
  3291  					VerificationScript: verifScript,
  3292  				}},
  3293  			}
  3294  			checkCalc(t, tx, 140065570)
  3295  		})
  3296  	})
  3297  	t.Run("sendrawtransaction", func(t *testing.T) {
  3298  		rpc := `{"jsonrpc": "2.0", "id": 1, "method": "sendrawtransaction", "params": ["%s"]}`
  3299  		t.Run("invalid signature", func(t *testing.T) {
  3300  			tx := newTxWithParams(t, chain, opcode.PUSH1, 10, 1, 1, false)
  3301  			tx.Scripts[0].InvocationScript[10] = ^tx.Scripts[0].InvocationScript[10]
  3302  			rawTx := encodeBinaryToString(t, tx)
  3303  			body := doRPCCall(fmt.Sprintf(rpc, rawTx), httpSrv.URL, t)
  3304  			checkErrGetResult(t, body, true, neorpc.ErrInvalidSignatureCode)
  3305  		})
  3306  		t.Run("too big tx", func(t *testing.T) {
  3307  			script := make([]byte, transaction.MaxScriptLength)
  3308  			for i := range script {
  3309  				script[i] = byte(opcode.PUSH0)
  3310  			}
  3311  			groups := make([]*keys.PublicKey, 16)
  3312  			for i := range groups {
  3313  				pk, _ := keys.NewPrivateKey()
  3314  				groups[i] = pk.PublicKey()
  3315  			}
  3316  			signers := make([]transaction.Signer, transaction.MaxAttributes)
  3317  			for i := range signers {
  3318  				signers[i] = transaction.Signer{
  3319  					Account:          random.Uint160(),
  3320  					Scopes:           transaction.CustomContracts | transaction.CustomGroups,
  3321  					AllowedContracts: make([]util.Uint160, 16),
  3322  					AllowedGroups:    groups,
  3323  				}
  3324  			}
  3325  			scripts := make([]transaction.Witness, len(signers))
  3326  			for i := range scripts {
  3327  				scripts[i] = transaction.Witness{
  3328  					InvocationScript:   random.Bytes(transaction.MaxInvocationScript),
  3329  					VerificationScript: random.Bytes(transaction.MaxVerificationScript),
  3330  				}
  3331  			}
  3332  			tx := &transaction.Transaction{
  3333  				ValidUntilBlock: chain.BlockHeight() + 1,
  3334  				Script:          script,
  3335  				Attributes:      []transaction.Attribute{},
  3336  				Signers:         signers,
  3337  				Scripts:         scripts,
  3338  			}
  3339  			rawTx := encodeBinaryToString(t, tx)
  3340  			body := doRPCCall(fmt.Sprintf(rpc, rawTx), httpSrv.URL, t)
  3341  			checkErrGetResult(t, body, true, neorpc.ErrInvalidSizeCode)
  3342  		})
  3343  		t.Run("mempool OOM", func(t *testing.T) {
  3344  			chain, _, httpSrv := initClearServerWithCustomConfig(t, func(c *config.Config) {
  3345  				c.ProtocolConfiguration.MemPoolSize = 1
  3346  			})
  3347  
  3348  			// create and push the first (prioritized) transaction with increased networkFee
  3349  			tx := newTxWithParams(t, chain, opcode.PUSH1, 10, 1, 2, false)
  3350  			rawTx := encodeBinaryToString(t, tx)
  3351  			body := doRPCCall(fmt.Sprintf(rpc, rawTx), httpSrv.URL, t)
  3352  			checkErrGetResult(t, body, false, 0)
  3353  
  3354  			// create and push the second transaction with standard networkFee
  3355  			tx2 := newTxWithParams(t, chain, opcode.PUSH1, 10, 1, 1, false)
  3356  			rawTx2 := encodeBinaryToString(t, tx2)
  3357  			body2 := doRPCCall(fmt.Sprintf(rpc, rawTx2), httpSrv.URL, t)
  3358  			checkErrGetResult(t, body2, true, neorpc.ErrMempoolCapReachedCode)
  3359  		})
  3360  		t.Run("mempool conflict", func(t *testing.T) {
  3361  			chain, _, httpSrv := initClearServerWithCustomConfig(t, func(c *config.Config) {
  3362  				c.ProtocolConfiguration.MemPoolSize = 2
  3363  			})
  3364  
  3365  			// Create and push the first transaction with large network fee.
  3366  			tx := newTxWithParams(t, chain, opcode.PUSH1, 10, 1, 25, false)
  3367  			rawTx := encodeBinaryToString(t, tx)
  3368  			body := doRPCCall(fmt.Sprintf(rpc, rawTx), httpSrv.URL, t)
  3369  			checkErrGetResult(t, body, false, 0)
  3370  
  3371  			// Create and push the second transaction, sender doesn't have enough balance to pay for two transactions.
  3372  			tx2 := newTxWithParams(t, chain, opcode.PUSH1, 10, 1, 25, false)
  3373  			rawTx2 := encodeBinaryToString(t, tx2)
  3374  			body2 := doRPCCall(fmt.Sprintf(rpc, rawTx2), httpSrv.URL, t)
  3375  			checkErrGetResult(t, body2, true, neorpc.ErrInsufficientFundsCode)
  3376  		})
  3377  	})
  3378  	t.Run("test functions with unsupported states", func(t *testing.T) {
  3379  		chain, _, httpSrv := initClearServerWithCustomConfig(t, func(c *config.Config) {
  3380  			c.ApplicationConfiguration.Ledger.KeepOnlyLatestState = true
  3381  		})
  3382  
  3383  		e := &executor{chain: chain, httpSrv: httpSrv}
  3384  		rpc := `{"jsonrpc": "2.0", "id": 1, "method": "%s", "params": %s}`
  3385  		for method, cases := range rpcFunctionsWithUnsupportedStatesTestCases {
  3386  			runTestCasesWithExecutor(t, e, rpc, method, cases, doRPCCall, checkErrGetResult)
  3387  		}
  3388  	})
  3389  }
  3390  
  3391  func (e *executor) getHeader(s string) *block.Header {
  3392  	hash, err := util.Uint256DecodeStringLE(s)
  3393  	if err != nil {
  3394  		panic("can not decode hash parameter")
  3395  	}
  3396  	block, err := e.chain.GetBlock(hash)
  3397  	if err != nil {
  3398  		panic("unknown block (update block hash)")
  3399  	}
  3400  	return &block.Header
  3401  }
  3402  
  3403  func encodeBinaryToString(t *testing.T, a io.Serializable) string {
  3404  	bytes, err := testserdes.EncodeBinary(a)
  3405  	require.NoError(t, err)
  3406  	return base64.StdEncoding.EncodeToString(bytes)
  3407  }
  3408  
  3409  func newTxWithParams(t *testing.T, chain *core.Blockchain, code opcode.Opcode, validUntilIncr uint32, systemFee int64,
  3410  	networkFeeMultiplier int64, addAttrNotValidBeforeT bool) *transaction.Transaction {
  3411  	priv0 := testchain.PrivateKeyByID(0)
  3412  	acc0 := wallet.NewAccountFromPrivateKey(priv0)
  3413  
  3414  	height := chain.BlockHeight()
  3415  	tx := transaction.New([]byte{byte(code)}, 0)
  3416  	tx.Nonce = uint32(random.Int(0, math.MaxUint32))
  3417  	tx.ValidUntilBlock = height + validUntilIncr
  3418  	tx.Signers = []transaction.Signer{{Account: acc0.PrivateKey().GetScriptHash()}}
  3419  	tx.SystemFee = systemFee
  3420  	// add network fee
  3421  	size := io.GetVarSize(tx)
  3422  	netFee, sizeDelta := fee.Calculate(chain.GetBaseExecFee(), acc0.Contract.Script)
  3423  	tx.NetworkFee += netFee
  3424  	size += sizeDelta
  3425  	tx.NetworkFee += int64(size) * chain.FeePerByte()
  3426  	tx.NetworkFee = tx.NetworkFee * networkFeeMultiplier
  3427  	if addAttrNotValidBeforeT {
  3428  		tx.Attributes = []transaction.Attribute{
  3429  			{Type: transaction.NotValidBeforeT, Value: &transaction.NotValidBefore{Height: height + 1}},
  3430  		}
  3431  	}
  3432  	require.NoError(t, acc0.SignTx(testchain.Network(), tx))
  3433  	return tx
  3434  }
  3435  
  3436  func (tc rpcTestCase) getResultPair(e *executor) (expected any, res any) {
  3437  	expected = tc.result(e)
  3438  	resVal := reflect.New(reflect.TypeOf(expected).Elem())
  3439  	res = resVal.Interface()
  3440  	return expected, res
  3441  }
  3442  
  3443  func checkErrGetResult(t *testing.T, body []byte, expectingFail bool, expectedErrCode int64, expectedErr ...string) json.RawMessage {
  3444  	var resp neorpc.Response
  3445  	err := json.Unmarshal(body, &resp)
  3446  	require.Nil(t, err)
  3447  	if expectingFail {
  3448  		require.NotNil(t, resp.Error)
  3449  		assert.NotEqual(t, 0, resp.Error.Code)
  3450  		assert.Equal(t, expectedErrCode, resp.Error.Code)
  3451  		assert.NotEqual(t, "", resp.Error.Message)
  3452  		if len(expectedErr) != 0 {
  3453  			assert.True(t, strings.Contains(resp.Error.Error(), expectedErr[0]), fmt.Sprintf("expected: %s, got: %s", expectedErr[0], resp.Error.Error()))
  3454  		}
  3455  	} else {
  3456  		assert.Nil(t, resp.Error)
  3457  	}
  3458  	return resp.Result
  3459  }
  3460  
  3461  func checkErrGetBatchResult(t *testing.T, body []byte, expectingFail bool, expectedErrCode int64, expectedErr ...string) json.RawMessage {
  3462  	var resp []neorpc.Response
  3463  	err := json.Unmarshal(body, &resp)
  3464  	require.Nil(t, err)
  3465  	require.Equal(t, 1, len(resp))
  3466  	if expectingFail {
  3467  		require.NotNil(t, resp[0].Error)
  3468  		assert.NotEqual(t, 0, resp[0].Error.Code)
  3469  		assert.NotEqual(t, "", resp[0].Error.Message)
  3470  	} else {
  3471  		assert.Nil(t, resp[0].Error)
  3472  	}
  3473  	return resp[0].Result
  3474  }
  3475  
  3476  func doRPCCallOverWS(rpcCall string, url string, t *testing.T) []byte {
  3477  	dialer := websocket.Dialer{
  3478  		HandshakeTimeout: 5 * time.Second,
  3479  		NetDialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
  3480  			dialer := net.Dialer{Timeout: 5 * time.Second,
  3481  				KeepAlive:     30 * time.Second,
  3482  				FallbackDelay: -1}
  3483  			return dialer.DialContext(ctx, "tcp4", addr)
  3484  		},
  3485  	}
  3486  	url = "ws" + strings.TrimPrefix(url, "http")
  3487  	c, r, err := dialer.Dial(url+"/ws", nil)
  3488  	require.NoError(t, err)
  3489  	defer r.Body.Close()
  3490  	err = c.SetWriteDeadline(time.Now().Add(5 * time.Second))
  3491  	require.NoError(t, err)
  3492  	require.NoError(t, c.WriteMessage(1, []byte(rpcCall)))
  3493  	err = c.SetReadDeadline(time.Now().Add(5 * time.Second))
  3494  	require.NoError(t, err)
  3495  	_, body, err := c.ReadMessage()
  3496  	require.NoError(t, err)
  3497  	require.NoError(t, c.Close())
  3498  	return bytes.TrimSpace(body)
  3499  }
  3500  
  3501  func doRPCCallOverHTTP(rpcCall string, url string, t *testing.T) []byte {
  3502  	cl := http.Client{Timeout: 5 * time.Second, Transport: &http.Transport{
  3503  		MaxIdleConns:        50,
  3504  		MaxConnsPerHost:     50,
  3505  		MaxIdleConnsPerHost: 50,
  3506  		IdleConnTimeout:     5 * time.Second,
  3507  		DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
  3508  			dialer := net.Dialer{Timeout: 5 * time.Second,
  3509  				KeepAlive:     30 * time.Second,
  3510  				FallbackDelay: -1}
  3511  			return dialer.DialContext(ctx, "tcp4", addr)
  3512  		},
  3513  	}}
  3514  	resp, err := cl.Post(url, "application/json", strings.NewReader(rpcCall))
  3515  	require.NoErrorf(t, err, "could not make a POST request")
  3516  	body, err := gio.ReadAll(resp.Body)
  3517  	resp.Body.Close()
  3518  	assert.NoErrorf(t, err, "could not read response from the request: %s", rpcCall)
  3519  	cl.CloseIdleConnections()
  3520  	return bytes.TrimSpace(body)
  3521  }
  3522  
  3523  func checkNep11Balances(t *testing.T, e *executor, acc any) {
  3524  	res, ok := acc.(*result.NEP11Balances)
  3525  	require.True(t, ok)
  3526  
  3527  	expected := result.NEP11Balances{
  3528  		Balances: []result.NEP11AssetBalance{
  3529  			{
  3530  				Asset:  nnsHash,
  3531  				Name:   "NameService",
  3532  				Symbol: "NNS",
  3533  				Tokens: []result.NEP11TokenBalance{
  3534  					{
  3535  						ID:          nnsToken1ID,
  3536  						Amount:      "1",
  3537  						LastUpdated: 14,
  3538  					},
  3539  				},
  3540  			},
  3541  			{
  3542  				Asset:    nfsoHash,
  3543  				Decimals: 2,
  3544  				Name:     "NeoFS Object NFT",
  3545  				Symbol:   "NFSO",
  3546  				Tokens: []result.NEP11TokenBalance{
  3547  					{
  3548  						ID:          nfsoToken1ID,
  3549  						Amount:      "80",
  3550  						LastUpdated: 21,
  3551  					},
  3552  				},
  3553  			},
  3554  		},
  3555  		Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(),
  3556  	}
  3557  	require.Equal(t, testchain.PrivateKeyByID(0).Address(), res.Address)
  3558  	require.ElementsMatch(t, expected.Balances, res.Balances)
  3559  }
  3560  
  3561  func checkNep17Balances(t *testing.T, e *executor, acc any) {
  3562  	res, ok := acc.(*result.NEP17Balances)
  3563  	require.True(t, ok)
  3564  	rubles, err := util.Uint160DecodeStringLE(testContractHash)
  3565  	require.NoError(t, err)
  3566  	expected := result.NEP17Balances{
  3567  		Balances: []result.NEP17Balance{
  3568  			{
  3569  				Asset:       rubles,
  3570  				Amount:      "877",
  3571  				Decimals:    2,
  3572  				LastUpdated: 6,
  3573  				Name:        "Rubl",
  3574  				Symbol:      "RUB",
  3575  			},
  3576  			{
  3577  				Asset:       e.chain.GoverningTokenHash(),
  3578  				Amount:      "99998000",
  3579  				LastUpdated: 4,
  3580  				Name:        "NeoToken",
  3581  				Symbol:      "NEO",
  3582  			},
  3583  			{
  3584  				Asset:       e.chain.UtilityTokenHash(),
  3585  				Amount:      "37106285100",
  3586  				LastUpdated: 23,
  3587  				Decimals:    8,
  3588  				Name:        "GasToken",
  3589  				Symbol:      "GAS",
  3590  			}},
  3591  		Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(),
  3592  	}
  3593  	require.Equal(t, testchain.PrivateKeyByID(0).Address(), res.Address)
  3594  	require.ElementsMatch(t, expected.Balances, res.Balances)
  3595  }
  3596  
  3597  func checkNep11Transfers(t *testing.T, e *executor, acc any) {
  3598  	checkNep11TransfersAux(t, e, acc, []int{0}, []int{0, 1, 2})
  3599  }
  3600  
  3601  func checkNep11TransfersAux(t *testing.T, e *executor, acc any, sent, rcvd []int) {
  3602  	res, ok := acc.(*result.NEP11Transfers)
  3603  	require.True(t, ok)
  3604  
  3605  	blockReceiveNFSO, err := e.chain.GetBlock(e.chain.GetHeaderHash(21)) // transfer 0.05 NFSO from priv1 back to priv0.
  3606  	require.NoError(t, err)
  3607  	require.Equal(t, 1, len(blockReceiveNFSO.Transactions))
  3608  	txReceiveNFSO := blockReceiveNFSO.Transactions[0]
  3609  
  3610  	blockSendNFSO, err := e.chain.GetBlock(e.chain.GetHeaderHash(19)) // transfer 0.25 NFSO from priv0 to priv1.
  3611  	require.NoError(t, err)
  3612  	require.Equal(t, 1, len(blockSendNFSO.Transactions))
  3613  	txSendNFSO := blockSendNFSO.Transactions[0]
  3614  
  3615  	blockMintNFSO, err := e.chain.GetBlock(e.chain.GetHeaderHash(18)) // mint 1.00 NFSO token by transferring 10 GAS to NFSO contract.
  3616  	require.NoError(t, err)
  3617  	require.Equal(t, 1, len(blockMintNFSO.Transactions))
  3618  	txMintNFSO := blockMintNFSO.Transactions[0]
  3619  
  3620  	blockRegisterNSRecordA, err := e.chain.GetBlock(e.chain.GetHeaderHash(14)) // register `neo.com` with A record type and priv0 owner via NS
  3621  	require.NoError(t, err)
  3622  	require.Equal(t, 1, len(blockRegisterNSRecordA.Transactions))
  3623  	txRegisterNSRecordA := blockRegisterNSRecordA.Transactions[0]
  3624  
  3625  	// These are laid out here explicitly for 2 purposes:
  3626  	//  * to be able to reference any particular event for paging
  3627  	//  * to check chain events consistency
  3628  	// Technically these could be retrieved from application log, but that would almost
  3629  	// duplicate the Server method.
  3630  	expected := result.NEP11Transfers{
  3631  		Sent: []result.NEP11Transfer{
  3632  			{
  3633  				Timestamp: blockSendNFSO.Timestamp,
  3634  				Asset:     nfsoHash,
  3635  				Address:   testchain.PrivateKeyByID(1).Address(), // to priv1
  3636  				ID:        nfsoToken1ID,                          // NFSO ID
  3637  				Amount:    big.NewInt(25).String(),
  3638  				Index:     19,
  3639  				TxHash:    txSendNFSO.Hash(),
  3640  			},
  3641  		},
  3642  		Received: []result.NEP11Transfer{
  3643  			{
  3644  				Timestamp: blockReceiveNFSO.Timestamp,
  3645  				Asset:     nfsoHash,
  3646  				ID:        nfsoToken1ID,
  3647  				Address:   testchain.PrivateKeyByID(1).Address(), // from priv1
  3648  				Amount:    "5",
  3649  				Index:     21,
  3650  				TxHash:    txReceiveNFSO.Hash(),
  3651  			},
  3652  			{
  3653  				Timestamp: blockMintNFSO.Timestamp,
  3654  				Asset:     nfsoHash,
  3655  				ID:        nfsoToken1ID,
  3656  				Address:   "", // minting
  3657  				Amount:    "100",
  3658  				Index:     18,
  3659  				TxHash:    txMintNFSO.Hash(),
  3660  			},
  3661  			{
  3662  				Timestamp: blockRegisterNSRecordA.Timestamp,
  3663  				Asset:     nnsHash,
  3664  				ID:        nnsToken1ID,
  3665  				Address:   "", // minting
  3666  				Amount:    "1",
  3667  				Index:     14,
  3668  				TxHash:    txRegisterNSRecordA.Hash(),
  3669  			},
  3670  		},
  3671  		Address: testchain.PrivateKeyByID(0).Address(),
  3672  	}
  3673  
  3674  	require.Equal(t, expected.Address, res.Address)
  3675  
  3676  	arr := make([]result.NEP11Transfer, 0, len(expected.Sent))
  3677  	for i := range expected.Sent {
  3678  		for _, j := range sent {
  3679  			if i == j {
  3680  				arr = append(arr, expected.Sent[i])
  3681  				break
  3682  			}
  3683  		}
  3684  	}
  3685  	require.Equal(t, arr, res.Sent)
  3686  
  3687  	arr = arr[:0]
  3688  	for i := range expected.Received {
  3689  		for _, j := range rcvd {
  3690  			if i == j {
  3691  				arr = append(arr, expected.Received[i])
  3692  				break
  3693  			}
  3694  		}
  3695  	}
  3696  	require.Equal(t, arr, res.Received)
  3697  }
  3698  
  3699  func checkNep17Transfers(t *testing.T, e *executor, acc any) {
  3700  	checkNep17TransfersAux(t, e, acc, []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}, []int{0, 1, 2, 3, 4, 5, 6, 7, 8})
  3701  }
  3702  
  3703  func checkNep17TransfersAux(t *testing.T, e *executor, acc any, sent, rcvd []int) {
  3704  	res, ok := acc.(*result.NEP17Transfers)
  3705  	require.True(t, ok)
  3706  	rublesHash, err := util.Uint160DecodeStringLE(testContractHash)
  3707  	require.NoError(t, err)
  3708  
  3709  	blockWithFAULTedTx, err := e.chain.GetBlock(e.chain.GetHeaderHash(faultedTxBlock)) // Transaction with ABORT inside.
  3710  	require.NoError(t, err)
  3711  	require.Equal(t, 1, len(blockWithFAULTedTx.Transactions))
  3712  	txFAULTed := blockWithFAULTedTx.Transactions[0]
  3713  
  3714  	blockDeploy6, err := e.chain.GetBlock(e.chain.GetHeaderHash(22)) // deploy Storage contract (storage_contract.go)
  3715  	require.NoError(t, err)
  3716  	require.Equal(t, 1, len(blockDeploy6.Transactions))
  3717  	txDeploy6 := blockDeploy6.Transactions[0]
  3718  
  3719  	blockTransferNFSO, err := e.chain.GetBlock(e.chain.GetHeaderHash(19)) // transfer 0.25 NFSO from priv0 to priv1.
  3720  	require.NoError(t, err)
  3721  	require.Equal(t, 1, len(blockTransferNFSO.Transactions))
  3722  	txTransferNFSO := blockTransferNFSO.Transactions[0]
  3723  
  3724  	blockMintNFSO, err := e.chain.GetBlock(e.chain.GetHeaderHash(18)) // mint 1.00 NFSO token for priv0 by transferring 10 GAS to NFSO contract.
  3725  	require.NoError(t, err)
  3726  	require.Equal(t, 1, len(blockMintNFSO.Transactions))
  3727  	txMintNFSO := blockMintNFSO.Transactions[0]
  3728  
  3729  	blockDeploy5, err := e.chain.GetBlock(e.chain.GetHeaderHash(17)) // deploy NeoFS Object contract (NEP11-Divisible)
  3730  	require.NoError(t, err)
  3731  	require.Equal(t, 1, len(blockDeploy5.Transactions))
  3732  	txDeploy5 := blockDeploy5.Transactions[0]
  3733  
  3734  	blockPutNewTestValue, err := e.chain.GetBlock(e.chain.GetHeaderHash(16)) // invoke `put` method of `test_contract.go` with `testkey`, `newtestvalue` args
  3735  	require.NoError(t, err)
  3736  	require.Equal(t, 4, len(blockPutNewTestValue.Transactions))
  3737  	txPutNewTestValue := blockPutNewTestValue.Transactions[0]
  3738  	txPutValue1 := blockPutNewTestValue.Transactions[1] // invoke `put` method of `test_contract.go` with `aa`, `v1` args
  3739  	txPutValue2 := blockPutNewTestValue.Transactions[2] // invoke `put` method of `test_contract.go` with `aa10`, `v2` args
  3740  	txPutValue3 := blockPutNewTestValue.Transactions[3] // invoke `put` method of `test_contract.go` with `aa50`, `v3` args
  3741  
  3742  	blockSetRecord, err := e.chain.GetBlock(e.chain.GetHeaderHash(15)) // add type A record to `neo.com` domain via NNS
  3743  	require.NoError(t, err)
  3744  	require.Equal(t, 1, len(blockSetRecord.Transactions))
  3745  	txSetRecord := blockSetRecord.Transactions[0]
  3746  
  3747  	blockRegisterDomain, err := e.chain.GetBlock(e.chain.GetHeaderHash(14)) // register `neo.com` domain via NNS
  3748  	require.NoError(t, err)
  3749  	require.Equal(t, 1, len(blockRegisterDomain.Transactions))
  3750  	txRegisterDomain := blockRegisterDomain.Transactions[0]
  3751  
  3752  	blockGASBounty2, err := e.chain.GetBlock(e.chain.GetHeaderHash(12)) // size of committee = 6
  3753  	require.NoError(t, err)
  3754  
  3755  	blockDeploy4, err := e.chain.GetBlock(e.chain.GetHeaderHash(11)) // deploy ns.go (non-native neo name service contract)
  3756  	require.NoError(t, err)
  3757  	require.Equal(t, 1, len(blockDeploy4.Transactions))
  3758  	txDeploy4 := blockDeploy4.Transactions[0]
  3759  
  3760  	blockDeploy3, err := e.chain.GetBlock(e.chain.GetHeaderHash(10)) // deploy verification_with_args_contract.go
  3761  	require.NoError(t, err)
  3762  	require.Equal(t, 1, len(blockDeploy3.Transactions))
  3763  	txDeploy3 := blockDeploy3.Transactions[0]
  3764  
  3765  	blockDepositGAS, err := e.chain.GetBlock(e.chain.GetHeaderHash(8))
  3766  	require.NoError(t, err)
  3767  	require.Equal(t, 1, len(blockDepositGAS.Transactions))
  3768  	txDepositGAS := blockDepositGAS.Transactions[0]
  3769  
  3770  	blockDeploy2, err := e.chain.GetBlock(e.chain.GetHeaderHash(7)) // deploy verification_contract.go
  3771  	require.NoError(t, err)
  3772  	require.Equal(t, 1, len(blockDeploy2.Transactions))
  3773  	txDeploy2 := blockDeploy2.Transactions[0]
  3774  
  3775  	blockSendRubles, err := e.chain.GetBlock(e.chain.GetHeaderHash(6))
  3776  	require.NoError(t, err)
  3777  	require.Equal(t, 1, len(blockSendRubles.Transactions))
  3778  	txSendRubles := blockSendRubles.Transactions[0]
  3779  	blockGASBounty1 := blockSendRubles // index 6 = size of committee
  3780  
  3781  	blockReceiveRubles, err := e.chain.GetBlock(e.chain.GetHeaderHash(5))
  3782  	require.NoError(t, err)
  3783  	require.Equal(t, 2, len(blockReceiveRubles.Transactions))
  3784  	txInitCall := blockReceiveRubles.Transactions[0]
  3785  	txReceiveRubles := blockReceiveRubles.Transactions[1]
  3786  
  3787  	blockSendNEO, err := e.chain.GetBlock(e.chain.GetHeaderHash(4))
  3788  	require.NoError(t, err)
  3789  	require.Equal(t, 1, len(blockSendNEO.Transactions))
  3790  	txSendNEO := blockSendNEO.Transactions[0]
  3791  
  3792  	blockCtrInv1, err := e.chain.GetBlock(e.chain.GetHeaderHash(3))
  3793  	require.NoError(t, err)
  3794  	require.Equal(t, 1, len(blockCtrInv1.Transactions))
  3795  	txCtrInv1 := blockCtrInv1.Transactions[0]
  3796  
  3797  	blockCtrDeploy, err := e.chain.GetBlock(e.chain.GetHeaderHash(2))
  3798  	require.NoError(t, err)
  3799  	require.Equal(t, 1, len(blockCtrDeploy.Transactions))
  3800  	txCtrDeploy := blockCtrDeploy.Transactions[0]
  3801  
  3802  	blockReceiveGAS, err := e.chain.GetBlock(e.chain.GetHeaderHash(1))
  3803  	require.NoError(t, err)
  3804  	require.Equal(t, 2, len(blockReceiveGAS.Transactions))
  3805  	txReceiveNEO := blockReceiveGAS.Transactions[0]
  3806  	txReceiveGAS := blockReceiveGAS.Transactions[1]
  3807  
  3808  	blockGASBounty0, err := e.chain.GetBlock(e.chain.GetHeaderHash(0))
  3809  	require.NoError(t, err)
  3810  
  3811  	// These are laid out here explicitly for 2 purposes:
  3812  	//  * to be able to reference any particular event for paging
  3813  	//  * to check chain events consistency
  3814  	// Technically these could be retrieved from application log, but that would almost
  3815  	// duplicate the Server method.
  3816  	expected := result.NEP17Transfers{
  3817  		Sent: []result.NEP17Transfer{
  3818  			{
  3819  				Timestamp: blockWithFAULTedTx.Timestamp,
  3820  				Asset:     e.chain.UtilityTokenHash(),
  3821  				Address:   "", // burn
  3822  				Amount:    big.NewInt(txFAULTed.SystemFee + txFAULTed.NetworkFee).String(),
  3823  				Index:     23,
  3824  				TxHash:    blockWithFAULTedTx.Hash(),
  3825  			},
  3826  			{
  3827  				Timestamp: blockDeploy6.Timestamp,
  3828  				Asset:     e.chain.UtilityTokenHash(),
  3829  				Address:   "", // burn
  3830  				Amount:    big.NewInt(txDeploy6.SystemFee + txDeploy6.NetworkFee).String(),
  3831  				Index:     22,
  3832  				TxHash:    blockDeploy6.Hash(),
  3833  			},
  3834  			{
  3835  				Timestamp: blockTransferNFSO.Timestamp,
  3836  				Asset:     e.chain.UtilityTokenHash(),
  3837  				Address:   "", // burn
  3838  				Amount:    big.NewInt(txTransferNFSO.SystemFee + txTransferNFSO.NetworkFee).String(),
  3839  				Index:     19,
  3840  				TxHash:    blockTransferNFSO.Hash(),
  3841  			},
  3842  			{
  3843  				Timestamp:   blockMintNFSO.Timestamp,
  3844  				Asset:       e.chain.UtilityTokenHash(),
  3845  				Address:     address.Uint160ToString(nfsoHash),
  3846  				Amount:      "1000000000",
  3847  				Index:       18,
  3848  				NotifyIndex: 0,
  3849  				TxHash:      txMintNFSO.Hash(),
  3850  			},
  3851  			{
  3852  				Timestamp: blockMintNFSO.Timestamp,
  3853  				Asset:     e.chain.UtilityTokenHash(),
  3854  				Address:   "", // burn
  3855  				Amount:    big.NewInt(txMintNFSO.SystemFee + txMintNFSO.NetworkFee).String(),
  3856  				Index:     18,
  3857  				TxHash:    blockMintNFSO.Hash(),
  3858  			},
  3859  			{
  3860  				Timestamp: blockDeploy5.Timestamp,
  3861  				Asset:     e.chain.UtilityTokenHash(),
  3862  				Address:   "", // burn
  3863  				Amount:    big.NewInt(txDeploy5.SystemFee + txDeploy5.NetworkFee).String(),
  3864  				Index:     17,
  3865  				TxHash:    blockDeploy5.Hash(),
  3866  			},
  3867  			{
  3868  				Timestamp: blockPutNewTestValue.Timestamp,
  3869  				Asset:     e.chain.UtilityTokenHash(),
  3870  				Address:   "", // burn
  3871  				Amount:    big.NewInt(txPutValue3.SystemFee + txPutValue3.NetworkFee).String(),
  3872  				Index:     16,
  3873  				TxHash:    blockPutNewTestValue.Hash(),
  3874  			},
  3875  			{
  3876  				Timestamp: blockPutNewTestValue.Timestamp,
  3877  				Asset:     e.chain.UtilityTokenHash(),
  3878  				Address:   "", // burn
  3879  				Amount:    big.NewInt(txPutValue2.SystemFee + txPutValue2.NetworkFee).String(),
  3880  				Index:     16,
  3881  				TxHash:    blockPutNewTestValue.Hash(),
  3882  			},
  3883  			{
  3884  				Timestamp: blockPutNewTestValue.Timestamp,
  3885  				Asset:     e.chain.UtilityTokenHash(),
  3886  				Address:   "", // burn
  3887  				Amount:    big.NewInt(txPutValue1.SystemFee + txPutValue1.NetworkFee).String(),
  3888  				Index:     16,
  3889  				TxHash:    blockPutNewTestValue.Hash(),
  3890  			},
  3891  			{
  3892  				Timestamp: blockPutNewTestValue.Timestamp,
  3893  				Asset:     e.chain.UtilityTokenHash(),
  3894  				Address:   "", // burn
  3895  				Amount:    big.NewInt(txPutNewTestValue.SystemFee + txPutNewTestValue.NetworkFee).String(),
  3896  				Index:     16,
  3897  				TxHash:    blockPutNewTestValue.Hash(),
  3898  			},
  3899  			{
  3900  				Timestamp: blockSetRecord.Timestamp,
  3901  				Asset:     e.chain.UtilityTokenHash(),
  3902  				Address:   "", // burn
  3903  				Amount:    big.NewInt(txSetRecord.SystemFee + txSetRecord.NetworkFee).String(),
  3904  				Index:     15,
  3905  				TxHash:    blockSetRecord.Hash(),
  3906  			},
  3907  			{
  3908  				Timestamp: blockRegisterDomain.Timestamp,
  3909  				Asset:     e.chain.UtilityTokenHash(),
  3910  				Address:   "", // burn
  3911  				Amount:    big.NewInt(txRegisterDomain.SystemFee + txRegisterDomain.NetworkFee).String(),
  3912  				Index:     14,
  3913  				TxHash:    blockRegisterDomain.Hash(),
  3914  			},
  3915  			{
  3916  				Timestamp: blockDeploy4.Timestamp,
  3917  				Asset:     e.chain.UtilityTokenHash(),
  3918  				Address:   "", // burn
  3919  				Amount:    big.NewInt(txDeploy4.SystemFee + txDeploy4.NetworkFee).String(),
  3920  				Index:     11,
  3921  				TxHash:    blockDeploy4.Hash(),
  3922  			},
  3923  			{
  3924  				Timestamp: blockDeploy3.Timestamp,
  3925  				Asset:     e.chain.UtilityTokenHash(),
  3926  				Address:   "", // burn
  3927  				Amount:    big.NewInt(txDeploy3.SystemFee + txDeploy3.NetworkFee).String(),
  3928  				Index:     10,
  3929  				TxHash:    blockDeploy3.Hash(),
  3930  			},
  3931  			{
  3932  				Timestamp:   blockDepositGAS.Timestamp,
  3933  				Asset:       e.chain.UtilityTokenHash(),
  3934  				Address:     address.Uint160ToString(e.chain.GetNotaryContractScriptHash()),
  3935  				Amount:      "1000000000",
  3936  				Index:       8,
  3937  				NotifyIndex: 0,
  3938  				TxHash:      txDepositGAS.Hash(),
  3939  			},
  3940  			{
  3941  				Timestamp: blockDepositGAS.Timestamp,
  3942  				Asset:     e.chain.UtilityTokenHash(),
  3943  				Address:   "", // burn
  3944  				Amount:    big.NewInt(txDepositGAS.SystemFee + txDepositGAS.NetworkFee).String(),
  3945  				Index:     8,
  3946  				TxHash:    blockDepositGAS.Hash(),
  3947  			},
  3948  			{
  3949  				Timestamp: blockDeploy2.Timestamp,
  3950  				Asset:     e.chain.UtilityTokenHash(),
  3951  				Address:   "", // burn
  3952  				Amount:    big.NewInt(txDeploy2.SystemFee + txDeploy2.NetworkFee).String(),
  3953  				Index:     7,
  3954  				TxHash:    blockDeploy2.Hash(),
  3955  			},
  3956  			{
  3957  				Timestamp:   blockSendRubles.Timestamp,
  3958  				Asset:       rublesHash,
  3959  				Address:     testchain.PrivateKeyByID(1).Address(),
  3960  				Amount:      "123",
  3961  				Index:       6,
  3962  				NotifyIndex: 0,
  3963  				TxHash:      txSendRubles.Hash(),
  3964  			},
  3965  			{
  3966  				Timestamp: blockSendRubles.Timestamp,
  3967  				Asset:     e.chain.UtilityTokenHash(),
  3968  				Address:   "", // burn
  3969  				Amount:    big.NewInt(txSendRubles.SystemFee + txSendRubles.NetworkFee).String(),
  3970  				Index:     6,
  3971  				TxHash:    blockSendRubles.Hash(),
  3972  			},
  3973  			{
  3974  				Timestamp: blockReceiveRubles.Timestamp,
  3975  				Asset:     e.chain.UtilityTokenHash(),
  3976  				Address:   "", // burn
  3977  				Amount:    big.NewInt(txReceiveRubles.SystemFee + txReceiveRubles.NetworkFee).String(),
  3978  				Index:     5,
  3979  				TxHash:    blockReceiveRubles.Hash(),
  3980  			},
  3981  			{
  3982  				Timestamp: blockReceiveRubles.Timestamp,
  3983  				Asset:     e.chain.UtilityTokenHash(),
  3984  				Address:   "", // burn
  3985  				Amount:    big.NewInt(txInitCall.SystemFee + txInitCall.NetworkFee).String(),
  3986  				Index:     5,
  3987  				TxHash:    blockReceiveRubles.Hash(),
  3988  			},
  3989  			{
  3990  				Timestamp:   blockSendNEO.Timestamp,
  3991  				Asset:       e.chain.GoverningTokenHash(),
  3992  				Address:     testchain.PrivateKeyByID(1).Address(),
  3993  				Amount:      "1000",
  3994  				Index:       4,
  3995  				NotifyIndex: 0,
  3996  				TxHash:      txSendNEO.Hash(),
  3997  			},
  3998  			{
  3999  				Timestamp: blockSendNEO.Timestamp,
  4000  				Asset:     e.chain.UtilityTokenHash(),
  4001  				Address:   "", // burn
  4002  				Amount:    big.NewInt(txSendNEO.SystemFee + txSendNEO.NetworkFee).String(),
  4003  				Index:     4,
  4004  				TxHash:    blockSendNEO.Hash(),
  4005  			},
  4006  			{
  4007  				Timestamp: blockCtrInv1.Timestamp,
  4008  				Asset:     e.chain.UtilityTokenHash(),
  4009  				Address:   "", // burn has empty receiver
  4010  				Amount:    big.NewInt(txCtrInv1.SystemFee + txCtrInv1.NetworkFee).String(),
  4011  				Index:     3,
  4012  				TxHash:    blockCtrInv1.Hash(),
  4013  			},
  4014  			{
  4015  				Timestamp: blockCtrDeploy.Timestamp,
  4016  				Asset:     e.chain.UtilityTokenHash(),
  4017  				Address:   "", // burn has empty receiver
  4018  				Amount:    big.NewInt(txCtrDeploy.SystemFee + txCtrDeploy.NetworkFee).String(),
  4019  				Index:     2,
  4020  				TxHash:    blockCtrDeploy.Hash(),
  4021  			},
  4022  		},
  4023  		Received: []result.NEP17Transfer{
  4024  			{
  4025  				Timestamp:   blockMintNFSO.Timestamp, // GAS bounty
  4026  				Asset:       e.chain.UtilityTokenHash(),
  4027  				Address:     "",
  4028  				Amount:      "50000000",
  4029  				Index:       18,
  4030  				NotifyIndex: 0,
  4031  				TxHash:      blockMintNFSO.Hash(),
  4032  			},
  4033  			{
  4034  				Timestamp:   blockGASBounty2.Timestamp,
  4035  				Asset:       e.chain.UtilityTokenHash(),
  4036  				Address:     "",
  4037  				Amount:      "50000000",
  4038  				Index:       12,
  4039  				NotifyIndex: 0,
  4040  				TxHash:      blockGASBounty2.Hash(),
  4041  			},
  4042  			{
  4043  				Timestamp:   blockGASBounty1.Timestamp,
  4044  				Asset:       e.chain.UtilityTokenHash(),
  4045  				Address:     "",
  4046  				Amount:      "50000000",
  4047  				Index:       6,
  4048  				NotifyIndex: 0,
  4049  				TxHash:      blockGASBounty1.Hash(),
  4050  			},
  4051  			{
  4052  				Timestamp:   blockReceiveRubles.Timestamp,
  4053  				Asset:       rublesHash,
  4054  				Address:     address.Uint160ToString(rublesHash),
  4055  				Amount:      "1000",
  4056  				Index:       5,
  4057  				NotifyIndex: 0,
  4058  				TxHash:      txReceiveRubles.Hash(),
  4059  			},
  4060  			{
  4061  				Timestamp:   blockSendNEO.Timestamp,
  4062  				Asset:       e.chain.UtilityTokenHash(),
  4063  				Address:     "", // Minted GAS.
  4064  				Amount:      "149998500",
  4065  				Index:       4,
  4066  				NotifyIndex: 0,
  4067  				TxHash:      txSendNEO.Hash(),
  4068  			},
  4069  			{
  4070  				Timestamp:   blockReceiveGAS.Timestamp,
  4071  				Asset:       e.chain.UtilityTokenHash(),
  4072  				Address:     testchain.MultisigAddress(),
  4073  				Amount:      "100000000000",
  4074  				Index:       1,
  4075  				NotifyIndex: 0,
  4076  				TxHash:      txReceiveGAS.Hash(),
  4077  			},
  4078  			{
  4079  				Timestamp:   blockReceiveGAS.Timestamp,
  4080  				Asset:       e.chain.GoverningTokenHash(),
  4081  				Address:     testchain.MultisigAddress(),
  4082  				Amount:      "99999000",
  4083  				Index:       1,
  4084  				NotifyIndex: 0,
  4085  				TxHash:      txReceiveNEO.Hash(),
  4086  			},
  4087  			{
  4088  				Timestamp: blockGASBounty0.Timestamp,
  4089  				Asset:     e.chain.UtilityTokenHash(),
  4090  				Address:   "",
  4091  				Amount:    "50000000",
  4092  				Index:     0,
  4093  				TxHash:    blockGASBounty0.Hash(),
  4094  			},
  4095  		},
  4096  		Address: testchain.PrivateKeyByID(0).Address(),
  4097  	}
  4098  
  4099  	require.Equal(t, expected.Address, res.Address)
  4100  
  4101  	arr := make([]result.NEP17Transfer, 0, len(expected.Sent))
  4102  	for i := range expected.Sent {
  4103  		for _, j := range sent {
  4104  			if i == j {
  4105  				arr = append(arr, expected.Sent[i])
  4106  				break
  4107  			}
  4108  		}
  4109  	}
  4110  	require.Equal(t, arr, res.Sent)
  4111  
  4112  	arr = arr[:0]
  4113  	for i := range expected.Received {
  4114  		for _, j := range rcvd {
  4115  			if i == j {
  4116  				arr = append(arr, expected.Received[i])
  4117  				break
  4118  			}
  4119  		}
  4120  	}
  4121  	require.Equal(t, arr, res.Received)
  4122  }
  4123  
  4124  func TestEscapeForLog(t *testing.T) {
  4125  	in := "\n\tbad"
  4126  	require.Equal(t, "bad", escapeForLog(in))
  4127  }
  4128  
  4129  func BenchmarkHandleIn(b *testing.B) {
  4130  	chain, orc, cfg, logger := getUnitTestChain(b, false, false, false)
  4131  
  4132  	serverConfig, err := network.NewServerConfig(cfg)
  4133  	require.NoError(b, err)
  4134  	serverConfig.UserAgent = fmt.Sprintf(config.UserAgentFormat, "0.98.6-test")
  4135  	serverConfig.LogLevel = zapcore.FatalLevel
  4136  	server, err := network.NewServer(serverConfig, chain, chain.GetStateSyncModule(), logger)
  4137  	require.NoError(b, err)
  4138  	rpcServer := New(chain, cfg.ApplicationConfiguration.RPC, server, orc, logger, make(chan error))
  4139  
  4140  	do := func(b *testing.B, req []byte) {
  4141  		b.ReportAllocs()
  4142  		b.ResetTimer()
  4143  		for i := 0; i < b.N; i++ {
  4144  			b.StopTimer()
  4145  			in := new(params.In)
  4146  			b.StartTimer()
  4147  			err := json.Unmarshal(req, in)
  4148  			if err != nil {
  4149  				b.FailNow()
  4150  			}
  4151  
  4152  			res := rpcServer.handleIn(in, nil)
  4153  			if res.Error != nil {
  4154  				b.FailNow()
  4155  			}
  4156  		}
  4157  		b.StopTimer()
  4158  	}
  4159  
  4160  	b.Run("no extra params", func(b *testing.B) {
  4161  		do(b, []byte(`{"jsonrpc":"2.0", "method":"validateaddress","params":["Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2"]}`))
  4162  	})
  4163  
  4164  	b.Run("with extra params", func(b *testing.B) {
  4165  		do(b, []byte(`{"jsonrpc":"2.0", "method":"validateaddress","params":["Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2", 
  4166  "set", "of", "different", "parameters", "to", "see", "the", "difference", "between", "unmarshalling", "algorithms", 1234, 5678, 1234567, 765432, true, false, null,
  4167  "0x50befd26fdf6e4d957c11e078b24ebce6291456f", "someMethod", [{"type": "String", "value": "50befd26fdf6e4d957c11e078b24ebce6291456f"}, 
  4168  {"type": "Integer", "value": "42"}, {"type": "Boolean", "value": false}]]}`))
  4169  	})
  4170  }
  4171  
  4172  func TestFailedPreconditionShutdown(t *testing.T) {
  4173  	_, srv, _ := initClearServerWithCustomConfig(t, func(c *config.Config) {
  4174  		c.ApplicationConfiguration.RPC.Addresses = []string{"not an address"}
  4175  	})
  4176  
  4177  	srv.Start()
  4178  	require.Positive(t, len(srv.errChan)) // this is how Start reports internal failures
  4179  
  4180  	var stopped atomic.Bool
  4181  
  4182  	go func() {
  4183  		srv.Shutdown()
  4184  		stopped.Store(true)
  4185  	}()
  4186  
  4187  	require.Eventually(t, stopped.Load, 5*time.Second, 100*time.Millisecond, "Shutdown should return")
  4188  }
  4189  
  4190  func TestErrorResponseContentType(t *testing.T) {
  4191  	_, _, httpSrv := initClearServerWithServices(t, true, false, false)
  4192  
  4193  	const (
  4194  		expectedContentType = "application/json; charset=utf-8"
  4195  		req                 = `{"jsonrpc":"2.0", "method":"unknown","params":[]}`
  4196  	)
  4197  
  4198  	cl := http.Client{Timeout: 5 * time.Second, Transport: &http.Transport{MaxIdleConns: 50, MaxIdleConnsPerHost: 50, IdleConnTimeout: 5 * time.Second,
  4199  		DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
  4200  			dialer := net.Dialer{Timeout: 5 * time.Second}
  4201  			return dialer.DialContext(ctx, "tcp4", addr)
  4202  		},
  4203  	}}
  4204  	resp, err := cl.Post(httpSrv.URL, "application/json", strings.NewReader(req))
  4205  	require.NoErrorf(t, err, "could not make a POST request")
  4206  	resp.Body.Close()
  4207  	contentType := resp.Header.Get("Content-Type")
  4208  	require.Equal(t, expectedContentType, contentType)
  4209  }