github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/rpcclient/rpc.go (about)

     1  package rpcclient
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/hex"
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  
    10  	"github.com/google/uuid"
    11  	"github.com/nspcc-dev/neo-go/pkg/config"
    12  	"github.com/nspcc-dev/neo-go/pkg/core/block"
    13  	"github.com/nspcc-dev/neo-go/pkg/core/state"
    14  	"github.com/nspcc-dev/neo-go/pkg/core/transaction"
    15  	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
    16  	"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
    17  	"github.com/nspcc-dev/neo-go/pkg/io"
    18  	"github.com/nspcc-dev/neo-go/pkg/neorpc"
    19  	"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
    20  	"github.com/nspcc-dev/neo-go/pkg/network/payload"
    21  	"github.com/nspcc-dev/neo-go/pkg/smartcontract"
    22  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
    23  	"github.com/nspcc-dev/neo-go/pkg/util"
    24  	"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
    25  )
    26  
    27  var errNetworkNotInitialized = errors.New("RPC client network is not initialized")
    28  
    29  // CalculateNetworkFee calculates network fee for the transaction. The transaction may
    30  // have empty witnesses for contract signers and may have only verification scripts
    31  // filled for standard sig/multisig signers.
    32  func (c *Client) CalculateNetworkFee(tx *transaction.Transaction) (int64, error) {
    33  	var (
    34  		params = []any{tx.Bytes()}
    35  		resp   = new(result.NetworkFee)
    36  	)
    37  	if err := c.performRequest("calculatenetworkfee", params, resp); err != nil {
    38  		return 0, err
    39  	}
    40  	return resp.Value, nil
    41  }
    42  
    43  // GetApplicationLog returns a contract log based on the specified txid.
    44  func (c *Client) GetApplicationLog(hash util.Uint256, trig *trigger.Type) (*result.ApplicationLog, error) {
    45  	var (
    46  		params = []any{hash.StringLE()}
    47  		resp   = new(result.ApplicationLog)
    48  	)
    49  	if trig != nil {
    50  		params = append(params, trig.String())
    51  	}
    52  	if err := c.performRequest("getapplicationlog", params, resp); err != nil {
    53  		return nil, err
    54  	}
    55  	return resp, nil
    56  }
    57  
    58  // GetBestBlockHash returns the hash of the tallest block in the blockchain.
    59  func (c *Client) GetBestBlockHash() (util.Uint256, error) {
    60  	var resp = util.Uint256{}
    61  	if err := c.performRequest("getbestblockhash", nil, &resp); err != nil {
    62  		return resp, err
    63  	}
    64  	return resp, nil
    65  }
    66  
    67  // GetBlockCount returns the number of blocks in the blockchain.
    68  func (c *Client) GetBlockCount() (uint32, error) {
    69  	var resp uint32
    70  	if err := c.performRequest("getblockcount", nil, &resp); err != nil {
    71  		return resp, err
    72  	}
    73  	return resp, nil
    74  }
    75  
    76  // GetBlockByIndex returns a block by its height. In-header stateroot option
    77  // must be initialized with Init before calling this method.
    78  func (c *Client) GetBlockByIndex(index uint32) (*block.Block, error) {
    79  	return c.getBlock(index)
    80  }
    81  
    82  // GetBlockByHash returns a block by its hash. In-header stateroot option
    83  // must be initialized with Init before calling this method.
    84  func (c *Client) GetBlockByHash(hash util.Uint256) (*block.Block, error) {
    85  	return c.getBlock(hash.StringLE())
    86  }
    87  
    88  func (c *Client) getBlock(param any) (*block.Block, error) {
    89  	var (
    90  		resp []byte
    91  		err  error
    92  		b    *block.Block
    93  	)
    94  	if err = c.performRequest("getblock", []any{param}, &resp); err != nil {
    95  		return nil, err
    96  	}
    97  	r := io.NewBinReaderFromBuf(resp)
    98  	sr, err := c.stateRootInHeader()
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  	b = block.New(sr)
   103  	b.DecodeBinary(r)
   104  	if r.Err != nil {
   105  		return nil, r.Err
   106  	}
   107  	return b, nil
   108  }
   109  
   110  // GetBlockByIndexVerbose returns a block wrapper with additional metadata by
   111  // its height. In-header stateroot option must be initialized with Init before
   112  // calling this method.
   113  // NOTE: to get transaction.ID and transaction.Size, use t.Hash() and io.GetVarSize(t) respectively.
   114  func (c *Client) GetBlockByIndexVerbose(index uint32) (*result.Block, error) {
   115  	return c.getBlockVerbose(index)
   116  }
   117  
   118  // GetBlockByHashVerbose returns a block wrapper with additional metadata by
   119  // its hash. In-header stateroot option must be initialized with Init before
   120  // calling this method.
   121  func (c *Client) GetBlockByHashVerbose(hash util.Uint256) (*result.Block, error) {
   122  	return c.getBlockVerbose(hash.StringLE())
   123  }
   124  
   125  func (c *Client) getBlockVerbose(param any) (*result.Block, error) {
   126  	var (
   127  		params = []any{param, 1} // 1 for verbose.
   128  		resp   = &result.Block{}
   129  		err    error
   130  	)
   131  	sr, err := c.stateRootInHeader()
   132  	if err != nil {
   133  		return nil, err
   134  	}
   135  	resp.Header.StateRootEnabled = sr
   136  	if err = c.performRequest("getblock", params, resp); err != nil {
   137  		return nil, err
   138  	}
   139  	return resp, nil
   140  }
   141  
   142  // GetBlockHash returns the hash value of the corresponding block based on the specified index.
   143  func (c *Client) GetBlockHash(index uint32) (util.Uint256, error) {
   144  	var (
   145  		params = []any{index}
   146  		resp   = util.Uint256{}
   147  	)
   148  	if err := c.performRequest("getblockhash", params, &resp); err != nil {
   149  		return resp, err
   150  	}
   151  	return resp, nil
   152  }
   153  
   154  // GetBlockHeader returns the corresponding block header information from a serialized hex string
   155  // according to the specified script hash. In-header stateroot option must be
   156  // initialized with Init before calling this method.
   157  func (c *Client) GetBlockHeader(hash util.Uint256) (*block.Header, error) {
   158  	var (
   159  		params = []any{hash.StringLE()}
   160  		resp   []byte
   161  		h      *block.Header
   162  	)
   163  	if err := c.performRequest("getblockheader", params, &resp); err != nil {
   164  		return nil, err
   165  	}
   166  	sr, err := c.stateRootInHeader()
   167  	if err != nil {
   168  		return nil, err
   169  	}
   170  	r := io.NewBinReaderFromBuf(resp)
   171  	h = new(block.Header)
   172  	h.StateRootEnabled = sr
   173  	h.DecodeBinary(r)
   174  	if r.Err != nil {
   175  		return nil, r.Err
   176  	}
   177  	return h, nil
   178  }
   179  
   180  // GetBlockHeaderCount returns the number of headers in the main chain.
   181  func (c *Client) GetBlockHeaderCount() (uint32, error) {
   182  	var resp uint32
   183  	if err := c.performRequest("getblockheadercount", nil, &resp); err != nil {
   184  		return resp, err
   185  	}
   186  	return resp, nil
   187  }
   188  
   189  // GetBlockHeaderVerbose returns the corresponding block header information from a Json format string
   190  // according to the specified script hash. In-header stateroot option must be
   191  // initialized with Init before calling this method.
   192  func (c *Client) GetBlockHeaderVerbose(hash util.Uint256) (*result.Header, error) {
   193  	var (
   194  		params = []any{hash.StringLE(), 1}
   195  		resp   = &result.Header{}
   196  	)
   197  	if err := c.performRequest("getblockheader", params, resp); err != nil {
   198  		return nil, err
   199  	}
   200  	return resp, nil
   201  }
   202  
   203  // GetBlockSysFee returns the system fees of the block based on the specified index.
   204  // This method is only supported by NeoGo servers.
   205  func (c *Client) GetBlockSysFee(index uint32) (fixedn.Fixed8, error) {
   206  	var (
   207  		params = []any{index}
   208  		resp   fixedn.Fixed8
   209  	)
   210  	if err := c.performRequest("getblocksysfee", params, &resp); err != nil {
   211  		return resp, err
   212  	}
   213  	return resp, nil
   214  }
   215  
   216  // GetConnectionCount returns the current number of the connections for the node.
   217  func (c *Client) GetConnectionCount() (int, error) {
   218  	var resp int
   219  
   220  	if err := c.performRequest("getconnectioncount", nil, &resp); err != nil {
   221  		return resp, err
   222  	}
   223  	return resp, nil
   224  }
   225  
   226  // GetCommittee returns the current public keys of NEO nodes in the committee.
   227  func (c *Client) GetCommittee() (keys.PublicKeys, error) {
   228  	var resp = new(keys.PublicKeys)
   229  
   230  	if err := c.performRequest("getcommittee", nil, resp); err != nil {
   231  		return nil, err
   232  	}
   233  	return *resp, nil
   234  }
   235  
   236  // GetContractStateByHash queries contract information according to the contract script hash.
   237  func (c *Client) GetContractStateByHash(hash util.Uint160) (*state.Contract, error) {
   238  	return c.getContractState(hash.StringLE())
   239  }
   240  
   241  // GetContractStateByAddressOrName queries contract information using the contract
   242  // address or name. Notice that name-based queries work only for native contracts,
   243  // non-native ones can't be requested this way.
   244  func (c *Client) GetContractStateByAddressOrName(addressOrName string) (*state.Contract, error) {
   245  	return c.getContractState(addressOrName)
   246  }
   247  
   248  // GetContractStateByID queries contract information according to the contract ID.
   249  // Notice that this is supported by all servers only for native contracts,
   250  // non-native ones can be requested only from NeoGo servers.
   251  func (c *Client) GetContractStateByID(id int32) (*state.Contract, error) {
   252  	return c.getContractState(id)
   253  }
   254  
   255  // getContractState is an internal representation of GetContractStateBy* methods.
   256  func (c *Client) getContractState(param any) (*state.Contract, error) {
   257  	var (
   258  		params = []any{param}
   259  		resp   = &state.Contract{}
   260  	)
   261  	if err := c.performRequest("getcontractstate", params, resp); err != nil {
   262  		return resp, err
   263  	}
   264  	return resp, nil
   265  }
   266  
   267  // GetNativeContracts queries information about native contracts.
   268  func (c *Client) GetNativeContracts() ([]state.Contract, error) {
   269  	var resp []state.Contract
   270  	if err := c.performRequest("getnativecontracts", nil, &resp); err != nil {
   271  		return resp, err
   272  	}
   273  
   274  	// Update native contract hashes.
   275  	c.cacheLock.Lock()
   276  	for _, cs := range resp {
   277  		c.cache.nativeHashes[cs.Manifest.Name] = cs.Hash
   278  	}
   279  	c.cacheLock.Unlock()
   280  
   281  	return resp, nil
   282  }
   283  
   284  // GetNEP11Balances is a wrapper for getnep11balances RPC.
   285  func (c *Client) GetNEP11Balances(address util.Uint160) (*result.NEP11Balances, error) {
   286  	params := []any{address.StringLE()}
   287  	resp := new(result.NEP11Balances)
   288  	if err := c.performRequest("getnep11balances", params, resp); err != nil {
   289  		return nil, err
   290  	}
   291  	return resp, nil
   292  }
   293  
   294  // GetNEP17Balances is a wrapper for getnep17balances RPC.
   295  func (c *Client) GetNEP17Balances(address util.Uint160) (*result.NEP17Balances, error) {
   296  	params := []any{address.StringLE()}
   297  	resp := new(result.NEP17Balances)
   298  	if err := c.performRequest("getnep17balances", params, resp); err != nil {
   299  		return nil, err
   300  	}
   301  	return resp, nil
   302  }
   303  
   304  // GetNEP11Properties is a wrapper for getnep11properties RPC. We recommend using
   305  // nep11 package and Properties method there to receive proper VM types and work with them.
   306  // This method is provided mostly for the sake of completeness. For well-known
   307  // attributes like "description", "image", "name" and "tokenURI" it returns strings,
   308  // while for all others []byte (which can be nil).
   309  func (c *Client) GetNEP11Properties(asset util.Uint160, token []byte) (map[string]any, error) {
   310  	params := []any{asset.StringLE(), hex.EncodeToString(token)}
   311  	resp := make(map[string]any)
   312  	if err := c.performRequest("getnep11properties", params, &resp); err != nil {
   313  		return nil, err
   314  	}
   315  	for k, v := range resp {
   316  		if v == nil {
   317  			continue
   318  		}
   319  		str, ok := v.(string)
   320  		if !ok {
   321  			return nil, errors.New("value is not a string")
   322  		}
   323  		if result.KnownNEP11Properties[k] {
   324  			continue
   325  		}
   326  		val, err := base64.StdEncoding.DecodeString(str)
   327  		if err != nil {
   328  			return nil, err
   329  		}
   330  		resp[k] = val
   331  	}
   332  	return resp, nil
   333  }
   334  
   335  // GetNEP11Transfers is a wrapper for getnep11transfers RPC. Address parameter
   336  // is mandatory, while all others are optional. Limit and page parameters are
   337  // only supported by NeoGo servers and can only be specified with start and stop.
   338  func (c *Client) GetNEP11Transfers(address util.Uint160, start, stop *uint64, limit, page *int) (*result.NEP11Transfers, error) {
   339  	params, err := packTransfersParams(address, start, stop, limit, page)
   340  	if err != nil {
   341  		return nil, err
   342  	}
   343  	resp := new(result.NEP11Transfers)
   344  	if err := c.performRequest("getnep11transfers", params, resp); err != nil {
   345  		return nil, err
   346  	}
   347  	return resp, nil
   348  }
   349  
   350  func packTransfersParams(address util.Uint160, start, stop *uint64, limit, page *int) ([]any, error) {
   351  	params := []any{address.StringLE()}
   352  	if start != nil {
   353  		params = append(params, *start)
   354  		if stop != nil {
   355  			params = append(params, *stop)
   356  			if limit != nil {
   357  				params = append(params, *limit)
   358  				if page != nil {
   359  					params = append(params, *page)
   360  				}
   361  			} else if page != nil {
   362  				return nil, errors.New("bad parameters")
   363  			}
   364  		} else if limit != nil || page != nil {
   365  			return nil, errors.New("bad parameters")
   366  		}
   367  	} else if stop != nil || limit != nil || page != nil {
   368  		return nil, errors.New("bad parameters")
   369  	}
   370  	return params, nil
   371  }
   372  
   373  // GetNEP17Transfers is a wrapper for getnep17transfers RPC. Address parameter
   374  // is mandatory while all the others are optional. Start and stop parameters
   375  // are supported since neo-go 0.77.0 and limit and page since neo-go 0.78.0.
   376  // These parameters are positional in the JSON-RPC call. For example, you can't specify the limit
   377  // without specifying start/stop first.
   378  func (c *Client) GetNEP17Transfers(address util.Uint160, start, stop *uint64, limit, page *int) (*result.NEP17Transfers, error) {
   379  	params, err := packTransfersParams(address, start, stop, limit, page)
   380  	if err != nil {
   381  		return nil, err
   382  	}
   383  	resp := new(result.NEP17Transfers)
   384  	if err := c.performRequest("getnep17transfers", params, resp); err != nil {
   385  		return nil, err
   386  	}
   387  	return resp, nil
   388  }
   389  
   390  // GetPeers returns a list of the nodes that the node is currently connected to/disconnected from.
   391  func (c *Client) GetPeers() (*result.GetPeers, error) {
   392  	var resp = &result.GetPeers{}
   393  
   394  	if err := c.performRequest("getpeers", nil, resp); err != nil {
   395  		return resp, err
   396  	}
   397  	return resp, nil
   398  }
   399  
   400  // GetRawMemPool returns a list of unconfirmed transactions in the memory.
   401  func (c *Client) GetRawMemPool() ([]util.Uint256, error) {
   402  	var resp = new([]util.Uint256)
   403  
   404  	if err := c.performRequest("getrawmempool", nil, resp); err != nil {
   405  		return *resp, err
   406  	}
   407  	return *resp, nil
   408  }
   409  
   410  // GetRawTransaction returns a transaction by hash.
   411  func (c *Client) GetRawTransaction(hash util.Uint256) (*transaction.Transaction, error) {
   412  	var (
   413  		params = []any{hash.StringLE()}
   414  		resp   []byte
   415  		err    error
   416  	)
   417  	if err = c.performRequest("getrawtransaction", params, &resp); err != nil {
   418  		return nil, err
   419  	}
   420  	tx, err := transaction.NewTransactionFromBytes(resp)
   421  	if err != nil {
   422  		return nil, err
   423  	}
   424  	return tx, nil
   425  }
   426  
   427  // GetRawTransactionVerbose returns a transaction wrapper with additional
   428  // metadata by transaction's hash.
   429  // NOTE: to get transaction.ID and transaction.Size, use t.Hash() and io.GetVarSize(t) respectively.
   430  func (c *Client) GetRawTransactionVerbose(hash util.Uint256) (*result.TransactionOutputRaw, error) {
   431  	var (
   432  		params = []any{hash.StringLE(), 1} // 1 for verbose.
   433  		resp   = &result.TransactionOutputRaw{}
   434  		err    error
   435  	)
   436  	if err = c.performRequest("getrawtransaction", params, resp); err != nil {
   437  		return nil, err
   438  	}
   439  	return resp, nil
   440  }
   441  
   442  // GetProof returns existence proof of storage item state by the given stateroot
   443  // historical contract hash and historical item key.
   444  func (c *Client) GetProof(stateroot util.Uint256, historicalContractHash util.Uint160, historicalKey []byte) (*result.ProofWithKey, error) {
   445  	var (
   446  		params = []any{stateroot.StringLE(), historicalContractHash.StringLE(), historicalKey}
   447  		resp   = &result.ProofWithKey{}
   448  	)
   449  	if err := c.performRequest("getproof", params, resp); err != nil {
   450  		return nil, err
   451  	}
   452  	return resp, nil
   453  }
   454  
   455  // VerifyProof returns value by the given stateroot and proof.
   456  func (c *Client) VerifyProof(stateroot util.Uint256, proof *result.ProofWithKey) ([]byte, error) {
   457  	var (
   458  		params = []any{stateroot.StringLE(), proof.String()}
   459  		resp   []byte
   460  	)
   461  	if err := c.performRequest("verifyproof", params, &resp); err != nil {
   462  		return nil, err
   463  	}
   464  	return resp, nil
   465  }
   466  
   467  // GetState returns historical contract storage item state by the given stateroot,
   468  // historical contract hash and historical item key.
   469  func (c *Client) GetState(stateroot util.Uint256, historicalContractHash util.Uint160, historicalKey []byte) ([]byte, error) {
   470  	var (
   471  		params = []any{stateroot.StringLE(), historicalContractHash.StringLE(), historicalKey}
   472  		resp   []byte
   473  	)
   474  	if err := c.performRequest("getstate", params, &resp); err != nil {
   475  		return nil, err
   476  	}
   477  	return resp, nil
   478  }
   479  
   480  // FindStates returns historical contract storage item states by the given stateroot,
   481  // historical contract hash and historical prefix. If `start` path is specified, items
   482  // starting from `start` path are being returned (excluding item located at the start path).
   483  // If `maxCount` specified, the maximum number of items to be returned equals to `maxCount`.
   484  func (c *Client) FindStates(stateroot util.Uint256, historicalContractHash util.Uint160, historicalPrefix []byte,
   485  	start []byte, maxCount *int) (result.FindStates, error) {
   486  	if historicalPrefix == nil {
   487  		historicalPrefix = []byte{}
   488  	}
   489  	var (
   490  		params = []any{stateroot.StringLE(), historicalContractHash.StringLE(), historicalPrefix}
   491  		resp   result.FindStates
   492  	)
   493  	if start == nil && maxCount != nil {
   494  		start = []byte{}
   495  	}
   496  	if start != nil {
   497  		params = append(params, start)
   498  	}
   499  	if maxCount != nil {
   500  		params = append(params, *maxCount)
   501  	}
   502  	if err := c.performRequest("findstates", params, &resp); err != nil {
   503  		return resp, err
   504  	}
   505  	return resp, nil
   506  }
   507  
   508  // GetStateRootByHeight returns the state root for the specified height.
   509  func (c *Client) GetStateRootByHeight(height uint32) (*state.MPTRoot, error) {
   510  	return c.getStateRoot(height)
   511  }
   512  
   513  // GetStateRootByBlockHash returns the state root for the block with the specified hash.
   514  func (c *Client) GetStateRootByBlockHash(hash util.Uint256) (*state.MPTRoot, error) {
   515  	return c.getStateRoot(hash)
   516  }
   517  
   518  func (c *Client) getStateRoot(param any) (*state.MPTRoot, error) {
   519  	var resp = new(state.MPTRoot)
   520  	if err := c.performRequest("getstateroot", []any{param}, resp); err != nil {
   521  		return nil, err
   522  	}
   523  	return resp, nil
   524  }
   525  
   526  // GetStateHeight returns the current validated and local node state height.
   527  func (c *Client) GetStateHeight() (*result.StateHeight, error) {
   528  	var resp = new(result.StateHeight)
   529  
   530  	if err := c.performRequest("getstateheight", nil, resp); err != nil {
   531  		return nil, err
   532  	}
   533  	return resp, nil
   534  }
   535  
   536  // GetStorageByID returns the stored value according to the contract ID and the stored key.
   537  func (c *Client) GetStorageByID(id int32, key []byte) ([]byte, error) {
   538  	return c.getStorage([]any{id, key})
   539  }
   540  
   541  // GetStorageByHash returns the stored value according to the contract script hash and the stored key.
   542  func (c *Client) GetStorageByHash(hash util.Uint160, key []byte) ([]byte, error) {
   543  	return c.getStorage([]any{hash.StringLE(), key})
   544  }
   545  
   546  func (c *Client) getStorage(params []any) ([]byte, error) {
   547  	var resp []byte
   548  	if err := c.performRequest("getstorage", params, &resp); err != nil {
   549  		return nil, err
   550  	}
   551  	return resp, nil
   552  }
   553  
   554  // GetStorageByIDHistoric returns the historical stored value according to the
   555  // contract ID and, stored key and specified stateroot.
   556  func (c *Client) GetStorageByIDHistoric(root util.Uint256, id int32, key []byte) ([]byte, error) {
   557  	return c.getStorageHistoric([]any{root.StringLE(), id, key})
   558  }
   559  
   560  // GetStorageByHashHistoric returns the historical stored value according to the
   561  // contract script hash, the stored key and specified stateroot.
   562  func (c *Client) GetStorageByHashHistoric(root util.Uint256, hash util.Uint160, key []byte) ([]byte, error) {
   563  	return c.getStorageHistoric([]any{root.StringLE(), hash.StringLE(), key})
   564  }
   565  
   566  func (c *Client) getStorageHistoric(params []any) ([]byte, error) {
   567  	var resp []byte
   568  	if err := c.performRequest("getstoragehistoric", params, &resp); err != nil {
   569  		return nil, err
   570  	}
   571  	return resp, nil
   572  }
   573  
   574  // FindStorageByHash returns contract storage items by the given contract hash and prefix.
   575  // If `start` index is specified, items starting from `start` index are being returned
   576  // (including item located at the start index).
   577  func (c *Client) FindStorageByHash(contractHash util.Uint160, prefix []byte, start *int) (result.FindStorage, error) {
   578  	var params = []any{contractHash.StringLE(), prefix}
   579  	if start != nil {
   580  		params = append(params, *start)
   581  	} else {
   582  		// C# node expects `start` parameter in any case.
   583  		params = append(params, 0)
   584  	}
   585  	return c.findStorage(params)
   586  }
   587  
   588  // FindStorageByID returns contract storage items by the given contract ID and prefix.
   589  // If `start` index is specified, items starting from `start` index are being returned
   590  // (including item located at the start index).
   591  func (c *Client) FindStorageByID(contractID int32, prefix []byte, start *int) (result.FindStorage, error) {
   592  	var params = []any{contractID, prefix}
   593  	if start != nil {
   594  		params = append(params, *start)
   595  	} else {
   596  		// C# node expects `start` parameter in any case.
   597  		params = append(params, 0)
   598  	}
   599  	return c.findStorage(params)
   600  }
   601  
   602  func (c *Client) findStorage(params []any) (result.FindStorage, error) {
   603  	var resp result.FindStorage
   604  	if err := c.performRequest("findstorage", params, &resp); err != nil {
   605  		return resp, err
   606  	}
   607  	return resp, nil
   608  }
   609  
   610  // FindStorageByHashHistoric returns historical contract storage items by the given stateroot,
   611  // historical contract hash and historical prefix. If `start` index is specified, then items
   612  // starting from `start` index are being returned (including item located at the start index).
   613  func (c *Client) FindStorageByHashHistoric(stateroot util.Uint256, historicalContractHash util.Uint160, historicalPrefix []byte,
   614  	start *int) (result.FindStorage, error) {
   615  	if historicalPrefix == nil {
   616  		historicalPrefix = []byte{}
   617  	}
   618  	var params = []any{stateroot.StringLE(), historicalContractHash.StringLE(), historicalPrefix}
   619  	if start != nil {
   620  		params = append(params, start)
   621  	}
   622  	return c.findStorageHistoric(params)
   623  }
   624  
   625  // FindStorageByIDHistoric returns historical contract storage items by the given stateroot,
   626  // historical contract ID and historical prefix. If `start` index is specified, then items
   627  // starting from `start` index are being returned (including item located at the start index).
   628  func (c *Client) FindStorageByIDHistoric(stateroot util.Uint256, historicalContractID int32, historicalPrefix []byte,
   629  	start *int) (result.FindStorage, error) {
   630  	if historicalPrefix == nil {
   631  		historicalPrefix = []byte{}
   632  	}
   633  	var params = []any{stateroot.StringLE(), historicalContractID, historicalPrefix}
   634  	if start != nil {
   635  		params = append(params, start)
   636  	}
   637  	return c.findStorageHistoric(params)
   638  }
   639  
   640  func (c *Client) findStorageHistoric(params []any) (result.FindStorage, error) {
   641  	var resp result.FindStorage
   642  	if err := c.performRequest("findstoragehistoric", params, &resp); err != nil {
   643  		return resp, err
   644  	}
   645  	return resp, nil
   646  }
   647  
   648  // GetTransactionHeight returns the block index where the transaction is found.
   649  func (c *Client) GetTransactionHeight(hash util.Uint256) (uint32, error) {
   650  	var (
   651  		params = []any{hash.StringLE()}
   652  		resp   uint32
   653  	)
   654  	if err := c.performRequest("gettransactionheight", params, &resp); err != nil {
   655  		return resp, err
   656  	}
   657  	return resp, nil
   658  }
   659  
   660  // GetUnclaimedGas returns the unclaimed GAS amount for the specified address.
   661  func (c *Client) GetUnclaimedGas(address string) (result.UnclaimedGas, error) {
   662  	var (
   663  		params = []any{address}
   664  		resp   result.UnclaimedGas
   665  	)
   666  	if err := c.performRequest("getunclaimedgas", params, &resp); err != nil {
   667  		return resp, err
   668  	}
   669  	return resp, nil
   670  }
   671  
   672  // GetCandidates returns the current list of NEO candidate node with voting data and
   673  // validator status.
   674  func (c *Client) GetCandidates() ([]result.Candidate, error) {
   675  	var resp = new([]result.Candidate)
   676  
   677  	if err := c.performRequest("getcandidates", nil, resp); err != nil {
   678  		return nil, err
   679  	}
   680  	return *resp, nil
   681  }
   682  
   683  // GetNextBlockValidators returns the current NEO consensus nodes information and voting data.
   684  func (c *Client) GetNextBlockValidators() ([]result.Validator, error) {
   685  	var resp = new([]result.Validator)
   686  
   687  	if err := c.performRequest("getnextblockvalidators", nil, resp); err != nil {
   688  		return nil, err
   689  	}
   690  	return *resp, nil
   691  }
   692  
   693  // GetVersion returns the version information about the queried node.
   694  func (c *Client) GetVersion() (*result.Version, error) {
   695  	var resp = &result.Version{}
   696  
   697  	if err := c.performRequest("getversion", nil, resp); err != nil {
   698  		return nil, err
   699  	}
   700  	return resp, nil
   701  }
   702  
   703  // InvokeScript returns the result of the given script after running it true the VM.
   704  // NOTE: This is a test invoke and will not affect the blockchain.
   705  func (c *Client) InvokeScript(script []byte, signers []transaction.Signer) (*result.Invoke, error) {
   706  	var p = []any{script}
   707  	return c.invokeSomething("invokescript", p, signers)
   708  }
   709  
   710  // InvokeScriptAtHeight returns the result of the given script after running it
   711  // true the VM using the provided chain state retrieved from the specified chain
   712  // height.
   713  // NOTE: This is a test invoke and will not affect the blockchain.
   714  func (c *Client) InvokeScriptAtHeight(height uint32, script []byte, signers []transaction.Signer) (*result.Invoke, error) {
   715  	var p = []any{height, script}
   716  	return c.invokeSomething("invokescripthistoric", p, signers)
   717  }
   718  
   719  // InvokeScriptWithState returns the result of the given script after running it
   720  // true the VM using the provided chain state retrieved from the specified
   721  // state root or block hash.
   722  // NOTE: This is a test invoke and will not affect the blockchain.
   723  func (c *Client) InvokeScriptWithState(stateOrBlock util.Uint256, script []byte, signers []transaction.Signer) (*result.Invoke, error) {
   724  	var p = []any{stateOrBlock.StringLE(), script}
   725  	return c.invokeSomething("invokescripthistoric", p, signers)
   726  }
   727  
   728  // InvokeFunction returns the results after calling the smart contract scripthash
   729  // with the given operation and parameters.
   730  // NOTE: this is test invoke and will not affect the blockchain.
   731  func (c *Client) InvokeFunction(contract util.Uint160, operation string, params []smartcontract.Parameter, signers []transaction.Signer) (*result.Invoke, error) {
   732  	var p = []any{contract.StringLE(), operation, params}
   733  	return c.invokeSomething("invokefunction", p, signers)
   734  }
   735  
   736  // InvokeFunctionAtHeight returns the results after calling the smart contract
   737  // with the given operation and parameters at the given blockchain state
   738  // specified by the blockchain height.
   739  // NOTE: this is test invoke and will not affect the blockchain.
   740  func (c *Client) InvokeFunctionAtHeight(height uint32, contract util.Uint160, operation string, params []smartcontract.Parameter, signers []transaction.Signer) (*result.Invoke, error) {
   741  	var p = []any{height, contract.StringLE(), operation, params}
   742  	return c.invokeSomething("invokefunctionhistoric", p, signers)
   743  }
   744  
   745  // InvokeFunctionWithState returns the results after calling the smart contract
   746  // with the given operation and parameters at the given blockchain state defined
   747  // by the specified state root or block hash.
   748  // NOTE: this is test invoke and will not affect the blockchain.
   749  func (c *Client) InvokeFunctionWithState(stateOrBlock util.Uint256, contract util.Uint160, operation string, params []smartcontract.Parameter, signers []transaction.Signer) (*result.Invoke, error) {
   750  	var p = []any{stateOrBlock.StringLE(), contract.StringLE(), operation, params}
   751  	return c.invokeSomething("invokefunctionhistoric", p, signers)
   752  }
   753  
   754  // InvokeContractVerify returns the results after calling `verify` method of the smart contract
   755  // with the given parameters under verification trigger type.
   756  // NOTE: this is test invoke and will not affect the blockchain.
   757  func (c *Client) InvokeContractVerify(contract util.Uint160, params []smartcontract.Parameter, signers []transaction.Signer, witnesses ...transaction.Witness) (*result.Invoke, error) {
   758  	var p = []any{contract.StringLE(), params}
   759  	return c.invokeSomething("invokecontractverify", p, signers, witnesses...)
   760  }
   761  
   762  // InvokeContractVerifyAtHeight returns the results after calling `verify` method
   763  // of the smart contract with the given parameters under verification trigger type
   764  // at the blockchain state specified by the blockchain height.
   765  // NOTE: this is test invoke and will not affect the blockchain.
   766  func (c *Client) InvokeContractVerifyAtHeight(height uint32, contract util.Uint160, params []smartcontract.Parameter, signers []transaction.Signer, witnesses ...transaction.Witness) (*result.Invoke, error) {
   767  	var p = []any{height, contract.StringLE(), params}
   768  	return c.invokeSomething("invokecontractverifyhistoric", p, signers, witnesses...)
   769  }
   770  
   771  // InvokeContractVerifyWithState returns the results after calling `verify` method
   772  // of the smart contract with the given parameters under verification trigger type
   773  // at the blockchain state specified by the state root or block hash.
   774  // NOTE: this is test invoke and will not affect the blockchain.
   775  func (c *Client) InvokeContractVerifyWithState(stateOrBlock util.Uint256, contract util.Uint160, params []smartcontract.Parameter, signers []transaction.Signer, witnesses ...transaction.Witness) (*result.Invoke, error) {
   776  	var p = []any{stateOrBlock.StringLE(), contract.StringLE(), params}
   777  	return c.invokeSomething("invokecontractverifyhistoric", p, signers, witnesses...)
   778  }
   779  
   780  // invokeSomething is an inner wrapper for Invoke* functions.
   781  func (c *Client) invokeSomething(method string, p []any, signers []transaction.Signer, witnesses ...transaction.Witness) (*result.Invoke, error) {
   782  	var resp = new(result.Invoke)
   783  	if signers != nil {
   784  		if witnesses == nil {
   785  			p = append(p, signers)
   786  		} else {
   787  			if len(witnesses) != len(signers) {
   788  				return nil, fmt.Errorf("number of witnesses should match number of signers, got %d vs %d", len(witnesses), len(signers))
   789  			}
   790  			signersWithWitnesses := make([]neorpc.SignerWithWitness, len(signers))
   791  			for i := range signersWithWitnesses {
   792  				signersWithWitnesses[i] = neorpc.SignerWithWitness{
   793  					Signer:  signers[i],
   794  					Witness: witnesses[i],
   795  				}
   796  			}
   797  			p = append(p, signersWithWitnesses)
   798  		}
   799  	}
   800  	if err := c.performRequest(method, p, resp); err != nil {
   801  		return nil, err
   802  	}
   803  	return resp, nil
   804  }
   805  
   806  // SendRawTransaction broadcasts the given transaction to the Neo network.
   807  // It always returns transaction hash, when successful (no error) this is the
   808  // hash returned from server, when not it's a locally calculated rawTX hash.
   809  func (c *Client) SendRawTransaction(rawTX *transaction.Transaction) (util.Uint256, error) {
   810  	var (
   811  		params = []any{rawTX.Bytes()}
   812  		resp   = new(result.RelayResult)
   813  	)
   814  	if err := c.performRequest("sendrawtransaction", params, resp); err != nil {
   815  		return rawTX.Hash(), err
   816  	}
   817  	return resp.Hash, nil
   818  }
   819  
   820  // SubmitBlock broadcasts a raw block over the NEO network.
   821  func (c *Client) SubmitBlock(b block.Block) (util.Uint256, error) {
   822  	var (
   823  		params []any
   824  		resp   = new(result.RelayResult)
   825  	)
   826  	buf := io.NewBufBinWriter()
   827  	b.EncodeBinary(buf.BinWriter)
   828  	if err := buf.Err; err != nil {
   829  		return util.Uint256{}, err
   830  	}
   831  	params = []any{buf.Bytes()}
   832  
   833  	if err := c.performRequest("submitblock", params, resp); err != nil {
   834  		return util.Uint256{}, err
   835  	}
   836  	return resp.Hash, nil
   837  }
   838  
   839  // SubmitRawOracleResponse submits a raw oracle response to the oracle node.
   840  // Raw params are used to avoid excessive marshalling.
   841  func (c *Client) SubmitRawOracleResponse(ps []any) error {
   842  	return c.performRequest("submitoracleresponse", ps, new(result.RelayResult))
   843  }
   844  
   845  // SubmitP2PNotaryRequest submits given P2PNotaryRequest payload to the RPC node.
   846  // It returns fallback transaction hash.
   847  func (c *Client) SubmitP2PNotaryRequest(req *payload.P2PNotaryRequest) (util.Uint256, error) {
   848  	var resp = new(result.RelayResult)
   849  	bytes, err := req.Bytes()
   850  	if err != nil {
   851  		return util.Uint256{}, fmt.Errorf("failed to encode request: %w", err)
   852  	}
   853  	params := []any{bytes}
   854  	if err := c.performRequest("submitnotaryrequest", params, resp); err != nil {
   855  		return util.Uint256{}, err
   856  	}
   857  	return resp.Hash, nil
   858  }
   859  
   860  // ValidateAddress verifies that the address is a correct NEO address.
   861  // Consider using [address] package instead to do it locally.
   862  func (c *Client) ValidateAddress(address string) error {
   863  	var (
   864  		params = []any{address}
   865  		resp   = &result.ValidateAddress{}
   866  	)
   867  
   868  	if err := c.performRequest("validateaddress", params, resp); err != nil {
   869  		return err
   870  	}
   871  	if !resp.IsValid {
   872  		return errors.New("validateaddress returned false")
   873  	}
   874  	return nil
   875  }
   876  
   877  // stateRootInHeader returns true if the state root is contained in the block header.
   878  // Requires Init() before use.
   879  func (c *Client) stateRootInHeader() (bool, error) {
   880  	c.cacheLock.RLock()
   881  	defer c.cacheLock.RUnlock()
   882  
   883  	if !c.cache.initDone {
   884  		return false, errNetworkNotInitialized
   885  	}
   886  	return c.cache.stateRootInHeader, nil
   887  }
   888  
   889  // TraverseIterator returns a set of iterator values (maxItemsCount at max) for
   890  // the specified iterator and session. If result contains no elements, then either
   891  // Iterator has no elements or session was expired and terminated by the server.
   892  // If maxItemsCount is non-positive, then config.DefaultMaxIteratorResultItems
   893  // iterator values will be returned using single `traverseiterator` call.
   894  // Note that iterator session lifetime is restricted by the RPC-server
   895  // configuration and is being reset each time iterator is accessed. If session
   896  // won't be accessed within session expiration time, then it will be terminated
   897  // by the RPC-server automatically.
   898  func (c *Client) TraverseIterator(sessionID, iteratorID uuid.UUID, maxItemsCount int) ([]stackitem.Item, error) {
   899  	if maxItemsCount <= 0 {
   900  		maxItemsCount = config.DefaultMaxIteratorResultItems
   901  	}
   902  	var (
   903  		params = []any{sessionID.String(), iteratorID.String(), maxItemsCount}
   904  		resp   []json.RawMessage
   905  	)
   906  	if err := c.performRequest("traverseiterator", params, &resp); err != nil {
   907  		return nil, err
   908  	}
   909  	result := make([]stackitem.Item, len(resp))
   910  	for i, iBytes := range resp {
   911  		itm, err := stackitem.FromJSONWithTypes(iBytes)
   912  		if err != nil {
   913  			return nil, fmt.Errorf("failed to unmarshal %d-th iterator value: %w", i, err)
   914  		}
   915  		result[i] = itm
   916  	}
   917  
   918  	return result, nil
   919  }
   920  
   921  // TerminateSession tries to terminate the specified session and returns `true` iff
   922  // the specified session was found on server.
   923  func (c *Client) TerminateSession(sessionID uuid.UUID) (bool, error) {
   924  	var resp bool
   925  	params := []any{sessionID.String()}
   926  	if err := c.performRequest("terminatesession", params, &resp); err != nil {
   927  		return false, err
   928  	}
   929  
   930  	return resp, nil
   931  }
   932  
   933  // GetRawNotaryTransaction  returns main or fallback transaction from the
   934  // RPC node's notary request pool.
   935  func (c *Client) GetRawNotaryTransaction(hash util.Uint256) (*transaction.Transaction, error) {
   936  	var (
   937  		params = []any{hash.StringLE()}
   938  		resp   []byte
   939  		err    error
   940  	)
   941  	if err = c.performRequest("getrawnotarytransaction", params, &resp); err != nil {
   942  		return nil, err
   943  	}
   944  	return transaction.NewTransactionFromBytes(resp)
   945  }
   946  
   947  // GetRawNotaryTransactionVerbose returns main or fallback transaction from the
   948  // RPC node's notary request pool.
   949  // NOTE: to get transaction.ID and transaction.Size, use t.Hash() and
   950  // io.GetVarSize(t) respectively.
   951  func (c *Client) GetRawNotaryTransactionVerbose(hash util.Uint256) (*transaction.Transaction, error) {
   952  	var (
   953  		params = []any{hash.StringLE(), 1} // 1 for verbose.
   954  		resp   = &transaction.Transaction{}
   955  		err    error
   956  	)
   957  	if err = c.performRequest("getrawnotarytransaction", params, resp); err != nil {
   958  		return nil, err
   959  	}
   960  	return resp, nil
   961  }
   962  
   963  // GetRawNotaryPool returns hashes of main P2PNotaryRequest transactions that
   964  // are currently in the RPC node's notary request pool with the corresponding
   965  // hashes of fallback transactions.
   966  func (c *Client) GetRawNotaryPool() (*result.RawNotaryPool, error) {
   967  	resp := &result.RawNotaryPool{}
   968  	if err := c.performRequest("getrawnotarypool", nil, resp); err != nil {
   969  		return nil, err
   970  	}
   971  	return resp, nil
   972  }