2020-02-17 12:01:57 +00:00
|
|
|
package client
|
2018-03-05 08:53:09 +00:00
|
|
|
|
2018-12-21 09:32:18 +00:00
|
|
|
import (
|
|
|
|
"encoding/hex"
|
2020-08-06 14:44:08 +00:00
|
|
|
"errors"
|
2020-07-21 07:31:45 +00:00
|
|
|
"fmt"
|
2020-09-25 16:32:53 +00:00
|
|
|
"strings"
|
2018-12-21 09:32:18 +00:00
|
|
|
|
2020-10-14 15:13:20 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core"
|
2020-03-03 17:26:56 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
2020-09-28 14:56:16 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
2020-06-09 09:12:56 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
2020-09-21 12:34:04 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
2020-03-03 17:26:56 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
2020-08-24 11:00:05 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
2018-12-21 09:32:18 +00:00
|
|
|
)
|
2018-03-25 16:21:00 +00:00
|
|
|
|
2020-10-14 15:13:20 +00:00
|
|
|
var errNetworkNotInitialized = errors.New("RPC client network is not initialized")
|
|
|
|
|
2020-03-11 14:14:05 +00:00
|
|
|
// GetApplicationLog returns the contract log based on the specified txid.
|
2020-11-11 15:43:28 +00:00
|
|
|
func (c *Client) GetApplicationLog(hash util.Uint256) (*result.ApplicationLog, error) {
|
2020-03-11 14:14:05 +00:00
|
|
|
var (
|
|
|
|
params = request.NewRawParams(hash.StringLE())
|
2020-11-11 15:43:28 +00:00
|
|
|
resp = new(result.ApplicationLog)
|
2020-03-11 14:14:05 +00:00
|
|
|
)
|
|
|
|
if err := c.performRequest("getapplicationlog", params, resp); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetBestBlockHash returns the hash of the tallest block in the main chain.
|
|
|
|
func (c *Client) GetBestBlockHash() (util.Uint256, error) {
|
|
|
|
var resp = util.Uint256{}
|
|
|
|
if err := c.performRequest("getbestblockhash", request.NewRawParams(), &resp); err != nil {
|
|
|
|
return resp, err
|
|
|
|
}
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetBlockCount returns the number of blocks in the main chain.
|
|
|
|
func (c *Client) GetBlockCount() (uint32, error) {
|
|
|
|
var resp uint32
|
|
|
|
if err := c.performRequest("getblockcount", request.NewRawParams(), &resp); err != nil {
|
|
|
|
return resp, err
|
|
|
|
}
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
2020-10-14 15:13:20 +00:00
|
|
|
// GetBlockByIndex returns a block by its height. You should initialize network magic
|
|
|
|
// with Init before calling GetBlockByIndex.
|
2020-03-03 17:26:56 +00:00
|
|
|
func (c *Client) GetBlockByIndex(index uint32) (*block.Block, error) {
|
|
|
|
return c.getBlock(request.NewRawParams(index))
|
|
|
|
}
|
|
|
|
|
2020-10-14 15:13:20 +00:00
|
|
|
// GetBlockByHash returns a block by its hash. You should initialize network magic
|
|
|
|
// with Init before calling GetBlockByHash.
|
2020-03-03 17:26:56 +00:00
|
|
|
func (c *Client) GetBlockByHash(hash util.Uint256) (*block.Block, error) {
|
|
|
|
return c.getBlock(request.NewRawParams(hash.StringLE()))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) getBlock(params request.RawParams) (*block.Block, error) {
|
|
|
|
var (
|
2020-11-06 14:37:58 +00:00
|
|
|
resp []byte
|
2020-03-03 17:26:56 +00:00
|
|
|
err error
|
|
|
|
b *block.Block
|
|
|
|
)
|
2020-10-14 15:13:20 +00:00
|
|
|
if !c.initDone {
|
|
|
|
return nil, errNetworkNotInitialized
|
|
|
|
}
|
2020-03-03 17:26:56 +00:00
|
|
|
if err = c.performRequest("getblock", params, &resp); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-11-06 14:37:58 +00:00
|
|
|
r := io.NewBinReaderFromBuf(resp)
|
2020-10-14 15:13:20 +00:00
|
|
|
b = block.New(c.GetNetwork())
|
2020-03-03 17:26:56 +00:00
|
|
|
b.DecodeBinary(r)
|
|
|
|
if r.Err != nil {
|
|
|
|
return nil, r.Err
|
|
|
|
}
|
|
|
|
return b, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetBlockByIndexVerbose returns a block wrapper with additional metadata by
|
2020-10-14 15:13:20 +00:00
|
|
|
// its height. You should initialize network magic with Init before calling GetBlockByIndexVerbose.
|
2020-03-23 14:31:28 +00:00
|
|
|
// NOTE: to get transaction.ID and transaction.Size, use t.Hash() and io.GetVarSize(t) respectively.
|
2020-03-03 17:26:56 +00:00
|
|
|
func (c *Client) GetBlockByIndexVerbose(index uint32) (*result.Block, error) {
|
|
|
|
return c.getBlockVerbose(request.NewRawParams(index, 1))
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetBlockByHashVerbose returns a block wrapper with additional metadata by
|
2020-10-14 15:13:20 +00:00
|
|
|
// its hash. You should initialize network magic with Init before calling GetBlockByHashVerbose.
|
2020-03-03 17:26:56 +00:00
|
|
|
func (c *Client) GetBlockByHashVerbose(hash util.Uint256) (*result.Block, error) {
|
|
|
|
return c.getBlockVerbose(request.NewRawParams(hash.StringLE(), 1))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) getBlockVerbose(params request.RawParams) (*result.Block, error) {
|
|
|
|
var (
|
|
|
|
resp = &result.Block{}
|
|
|
|
err error
|
|
|
|
)
|
2020-10-14 15:13:20 +00:00
|
|
|
if !c.initDone {
|
|
|
|
return nil, errNetworkNotInitialized
|
|
|
|
}
|
|
|
|
resp.Network = c.GetNetwork()
|
2020-03-24 17:11:49 +00:00
|
|
|
if err = c.performRequest("getblock", params, resp); err != nil {
|
2020-03-03 17:26:56 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
2020-03-11 14:14:05 +00:00
|
|
|
// GetBlockHash returns the hash value of the corresponding block, based on the specified index.
|
|
|
|
func (c *Client) GetBlockHash(index uint32) (util.Uint256, error) {
|
|
|
|
var (
|
|
|
|
params = request.NewRawParams(index)
|
|
|
|
resp = util.Uint256{}
|
|
|
|
)
|
|
|
|
if err := c.performRequest("getblockhash", params, &resp); err != nil {
|
|
|
|
return resp, err
|
|
|
|
}
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetBlockHeader returns the corresponding block header information from serialized hex string
|
2020-10-14 15:13:20 +00:00
|
|
|
// according to the specified script hash. You should initialize network magic
|
|
|
|
// // with Init before calling GetBlockHeader.
|
2020-03-11 14:14:05 +00:00
|
|
|
func (c *Client) GetBlockHeader(hash util.Uint256) (*block.Header, error) {
|
|
|
|
var (
|
|
|
|
params = request.NewRawParams(hash.StringLE())
|
2020-11-06 14:37:58 +00:00
|
|
|
resp []byte
|
2020-03-11 14:14:05 +00:00
|
|
|
h *block.Header
|
|
|
|
)
|
2020-10-14 15:13:20 +00:00
|
|
|
if !c.initDone {
|
|
|
|
return nil, errNetworkNotInitialized
|
|
|
|
}
|
2020-03-11 14:14:05 +00:00
|
|
|
if err := c.performRequest("getblockheader", params, &resp); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-11-06 14:37:58 +00:00
|
|
|
r := io.NewBinReaderFromBuf(resp)
|
2020-03-11 14:14:05 +00:00
|
|
|
h = new(block.Header)
|
2020-10-14 15:13:20 +00:00
|
|
|
h.Network = c.GetNetwork()
|
2020-03-11 14:14:05 +00:00
|
|
|
h.DecodeBinary(r)
|
|
|
|
if r.Err != nil {
|
|
|
|
return nil, r.Err
|
|
|
|
}
|
|
|
|
return h, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetBlockHeaderVerbose returns the corresponding block header information from Json format string
|
|
|
|
// according to the specified script hash.
|
|
|
|
func (c *Client) GetBlockHeaderVerbose(hash util.Uint256) (*result.Header, error) {
|
|
|
|
var (
|
|
|
|
params = request.NewRawParams(hash.StringLE(), 1)
|
|
|
|
resp = &result.Header{}
|
|
|
|
)
|
2020-03-24 17:11:49 +00:00
|
|
|
if err := c.performRequest("getblockheader", params, resp); err != nil {
|
2020-03-11 14:14:05 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetBlockSysFee returns the system fees of the block, based on the specified index.
|
|
|
|
func (c *Client) GetBlockSysFee(index uint32) (util.Fixed8, error) {
|
|
|
|
var (
|
|
|
|
params = request.NewRawParams(index)
|
|
|
|
resp util.Fixed8
|
|
|
|
)
|
|
|
|
if err := c.performRequest("getblocksysfee", params, &resp); err != nil {
|
|
|
|
return resp, err
|
|
|
|
}
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetConnectionCount returns the current number of connections for the node.
|
|
|
|
func (c *Client) GetConnectionCount() (int, error) {
|
|
|
|
var (
|
|
|
|
params = request.NewRawParams()
|
|
|
|
resp int
|
|
|
|
)
|
|
|
|
if err := c.performRequest("getconnectioncount", params, &resp); err != nil {
|
|
|
|
return resp, err
|
|
|
|
}
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
2020-09-21 12:34:04 +00:00
|
|
|
// GetCommittee returns the current public keys of NEO nodes in committee.
|
|
|
|
func (c *Client) GetCommittee() (keys.PublicKeys, error) {
|
|
|
|
var (
|
|
|
|
params = request.NewRawParams()
|
|
|
|
resp = new(keys.PublicKeys)
|
|
|
|
)
|
|
|
|
if err := c.performRequest("getcommittee", params, resp); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return *resp, nil
|
|
|
|
}
|
|
|
|
|
2020-09-25 16:32:53 +00:00
|
|
|
// GetContractStateByHash queries contract information, according to the contract script hash.
|
|
|
|
func (c *Client) GetContractStateByHash(hash util.Uint160) (*state.Contract, error) {
|
|
|
|
return c.getContractState(hash.StringLE())
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetContractStateByAddressOrName queries contract information, according to the contract address or name.
|
|
|
|
func (c *Client) GetContractStateByAddressOrName(addressOrName string) (*state.Contract, error) {
|
|
|
|
return c.getContractState(addressOrName)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetContractStateByID queries contract information, according to the contract ID.
|
|
|
|
func (c *Client) GetContractStateByID(id int32) (*state.Contract, error) {
|
|
|
|
return c.getContractState(id)
|
|
|
|
}
|
|
|
|
|
|
|
|
// getContractState is an internal representation of GetContractStateBy* methods.
|
|
|
|
func (c *Client) getContractState(param interface{}) (*state.Contract, error) {
|
2020-03-11 14:14:05 +00:00
|
|
|
var (
|
2020-09-25 16:32:53 +00:00
|
|
|
params = request.NewRawParams(param)
|
2020-06-09 09:12:56 +00:00
|
|
|
resp = &state.Contract{}
|
2020-03-11 14:14:05 +00:00
|
|
|
)
|
|
|
|
if err := c.performRequest("getcontractstate", params, resp); err != nil {
|
|
|
|
return resp, err
|
|
|
|
}
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
2020-03-05 11:50:06 +00:00
|
|
|
// GetNEP5Balances is a wrapper for getnep5balances RPC.
|
|
|
|
func (c *Client) GetNEP5Balances(address util.Uint160) (*result.NEP5Balances, error) {
|
|
|
|
params := request.NewRawParams(address.StringLE())
|
|
|
|
resp := new(result.NEP5Balances)
|
|
|
|
if err := c.performRequest("getnep5balances", params, resp); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
2020-09-14 19:12:49 +00:00
|
|
|
// GetNEP5Transfers is a wrapper for getnep5transfers RPC. Address parameter
|
|
|
|
// is mandatory, while all the others are optional. Start and stop parameters
|
|
|
|
// are supported since neo-go 0.77.0 and limit and page since neo-go 0.78.0.
|
|
|
|
// These parameters are positional in the JSON-RPC call, you can't specify limit
|
|
|
|
// and not specify start/stop for example.
|
|
|
|
func (c *Client) GetNEP5Transfers(address string, start, stop *uint32, limit, page *int) (*result.NEP5Transfers, error) {
|
2020-03-05 12:16:03 +00:00
|
|
|
params := request.NewRawParams(address)
|
2020-09-14 19:12:49 +00:00
|
|
|
if start != nil {
|
|
|
|
params.Values = append(params.Values, *start)
|
|
|
|
if stop != nil {
|
|
|
|
params.Values = append(params.Values, *stop)
|
|
|
|
if limit != nil {
|
|
|
|
params.Values = append(params.Values, *limit)
|
|
|
|
if page != nil {
|
|
|
|
params.Values = append(params.Values, *page)
|
|
|
|
}
|
|
|
|
} else if page != nil {
|
|
|
|
return nil, errors.New("bad parameters")
|
|
|
|
}
|
|
|
|
} else if limit != nil || page != nil {
|
|
|
|
return nil, errors.New("bad parameters")
|
|
|
|
}
|
|
|
|
} else if stop != nil || limit != nil || page != nil {
|
|
|
|
return nil, errors.New("bad parameters")
|
|
|
|
}
|
2020-03-05 12:16:03 +00:00
|
|
|
resp := new(result.NEP5Transfers)
|
|
|
|
if err := c.performRequest("getnep5transfers", params, resp); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
2020-03-11 14:14:05 +00:00
|
|
|
// GetPeers returns the list of nodes that the node is currently connected/disconnected from.
|
|
|
|
func (c *Client) GetPeers() (*result.GetPeers, error) {
|
|
|
|
var (
|
|
|
|
params = request.NewRawParams()
|
|
|
|
resp = &result.GetPeers{}
|
|
|
|
)
|
|
|
|
if err := c.performRequest("getpeers", params, resp); err != nil {
|
|
|
|
return resp, err
|
|
|
|
}
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetRawMemPool returns the list of unconfirmed transactions in memory.
|
|
|
|
func (c *Client) GetRawMemPool() ([]util.Uint256, error) {
|
|
|
|
var (
|
|
|
|
params = request.NewRawParams()
|
|
|
|
resp = new([]util.Uint256)
|
|
|
|
)
|
|
|
|
if err := c.performRequest("getrawmempool", params, resp); err != nil {
|
|
|
|
return *resp, err
|
|
|
|
}
|
|
|
|
return *resp, nil
|
|
|
|
}
|
|
|
|
|
2020-10-14 15:13:20 +00:00
|
|
|
// GetRawTransaction returns a transaction by hash. You should initialize network magic
|
|
|
|
// with Init before calling GetRawTransaction.
|
2020-03-03 17:43:57 +00:00
|
|
|
func (c *Client) GetRawTransaction(hash util.Uint256) (*transaction.Transaction, error) {
|
|
|
|
var (
|
|
|
|
params = request.NewRawParams(hash.StringLE())
|
2020-11-06 14:37:58 +00:00
|
|
|
resp []byte
|
2020-03-03 17:43:57 +00:00
|
|
|
err error
|
|
|
|
)
|
2020-10-14 15:13:20 +00:00
|
|
|
if !c.initDone {
|
|
|
|
return nil, errNetworkNotInitialized
|
|
|
|
}
|
2020-03-03 17:43:57 +00:00
|
|
|
if err = c.performRequest("getrawtransaction", params, &resp); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-11-06 14:37:58 +00:00
|
|
|
tx, err := transaction.NewTransactionFromBytes(c.GetNetwork(), resp)
|
2020-04-13 09:02:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2020-03-03 17:43:57 +00:00
|
|
|
}
|
|
|
|
return tx, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetRawTransactionVerbose returns a transaction wrapper with additional
|
2020-10-14 15:13:20 +00:00
|
|
|
// metadata by transaction's hash. You should initialize network magic
|
|
|
|
// with Init before calling GetRawTransactionVerbose.
|
2020-03-23 14:31:28 +00:00
|
|
|
// NOTE: to get transaction.ID and transaction.Size, use t.Hash() and io.GetVarSize(t) respectively.
|
2020-03-03 17:43:57 +00:00
|
|
|
func (c *Client) GetRawTransactionVerbose(hash util.Uint256) (*result.TransactionOutputRaw, error) {
|
|
|
|
var (
|
|
|
|
params = request.NewRawParams(hash.StringLE(), 1)
|
|
|
|
resp = &result.TransactionOutputRaw{}
|
|
|
|
err error
|
|
|
|
)
|
2020-10-14 15:13:20 +00:00
|
|
|
if !c.initDone {
|
|
|
|
return nil, errNetworkNotInitialized
|
|
|
|
}
|
|
|
|
resp.Network = c.GetNetwork()
|
2020-03-24 17:11:49 +00:00
|
|
|
if err = c.performRequest("getrawtransaction", params, resp); err != nil {
|
2020-03-03 17:43:57 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
2020-06-23 14:27:23 +00:00
|
|
|
// GetStorageByID returns the stored value, according to the contract ID and the stored key.
|
|
|
|
func (c *Client) GetStorageByID(id int32, key []byte) ([]byte, error) {
|
|
|
|
return c.getStorage(request.NewRawParams(id, hex.EncodeToString(key)))
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetStorageByHash returns the stored value, according to the contract script hash and the stored key.
|
|
|
|
func (c *Client) GetStorageByHash(hash util.Uint160, key []byte) ([]byte, error) {
|
|
|
|
return c.getStorage(request.NewRawParams(hash.StringLE(), hex.EncodeToString(key)))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) getStorage(params request.RawParams) ([]byte, error) {
|
|
|
|
var resp string
|
2020-03-11 14:14:05 +00:00
|
|
|
if err := c.performRequest("getstorage", params, &resp); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
res, err := hex.DecodeString(resp)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetTransactionHeight returns the block index in which the transaction is found.
|
|
|
|
func (c *Client) GetTransactionHeight(hash util.Uint256) (uint32, error) {
|
|
|
|
var (
|
|
|
|
params = request.NewRawParams(hash.StringLE())
|
|
|
|
resp uint32
|
|
|
|
)
|
|
|
|
if err := c.performRequest("gettransactionheight", params, &resp); err != nil {
|
|
|
|
return resp, err
|
|
|
|
}
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
2020-06-01 20:27:03 +00:00
|
|
|
// GetUnclaimedGas returns unclaimed GAS amount for the specified address.
|
2020-07-09 14:25:26 +00:00
|
|
|
func (c *Client) GetUnclaimedGas(address string) (result.UnclaimedGas, error) {
|
2020-03-11 14:14:05 +00:00
|
|
|
var (
|
|
|
|
params = request.NewRawParams(address)
|
2020-07-09 14:25:26 +00:00
|
|
|
resp result.UnclaimedGas
|
2020-03-11 14:14:05 +00:00
|
|
|
)
|
2020-06-01 20:27:03 +00:00
|
|
|
if err := c.performRequest("getunclaimedgas", params, &resp); err != nil {
|
2020-07-09 14:25:26 +00:00
|
|
|
return resp, err
|
2020-06-01 20:27:03 +00:00
|
|
|
}
|
2020-07-09 14:25:26 +00:00
|
|
|
return resp, nil
|
2020-03-11 14:14:05 +00:00
|
|
|
}
|
|
|
|
|
2020-10-01 12:26:54 +00:00
|
|
|
// GetNextBlockValidators returns the current NEO consensus nodes information and voting status.
|
|
|
|
func (c *Client) GetNextBlockValidators() ([]result.Validator, error) {
|
2020-03-11 14:14:05 +00:00
|
|
|
var (
|
|
|
|
params = request.NewRawParams()
|
|
|
|
resp = new([]result.Validator)
|
|
|
|
)
|
2020-10-01 12:26:54 +00:00
|
|
|
if err := c.performRequest("getnextblockvalidators", params, resp); err != nil {
|
2020-03-11 14:14:05 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return *resp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetVersion returns the version information about the queried node.
|
|
|
|
func (c *Client) GetVersion() (*result.Version, error) {
|
|
|
|
var (
|
|
|
|
params = request.NewRawParams()
|
|
|
|
resp = &result.Version{}
|
|
|
|
)
|
|
|
|
if err := c.performRequest("getversion", params, resp); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
2019-02-13 18:01:10 +00:00
|
|
|
// InvokeScript returns the result of the given script after running it true the VM.
|
|
|
|
// NOTE: This is a test invoke and will not affect the blockchain.
|
2020-07-29 16:57:38 +00:00
|
|
|
func (c *Client) InvokeScript(script []byte, signers []transaction.Signer) (*result.Invoke, error) {
|
2020-10-14 13:46:06 +00:00
|
|
|
var p = request.NewRawParams(script)
|
2020-07-29 16:57:38 +00:00
|
|
|
return c.invokeSomething("invokescript", p, signers)
|
2018-03-05 08:53:09 +00:00
|
|
|
}
|
|
|
|
|
2019-10-22 14:56:03 +00:00
|
|
|
// InvokeFunction returns the results after calling the smart contract scripthash
|
2018-03-25 16:21:00 +00:00
|
|
|
// with the given operation and parameters.
|
|
|
|
// NOTE: this is test invoke and will not affect the blockchain.
|
2020-07-29 16:57:38 +00:00
|
|
|
func (c *Client) InvokeFunction(contract util.Uint160, operation string, params []smartcontract.Parameter, signers []transaction.Signer) (*result.Invoke, error) {
|
2020-07-02 13:38:33 +00:00
|
|
|
var p = request.NewRawParams(contract.StringLE(), operation, params)
|
2020-07-29 16:57:38 +00:00
|
|
|
return c.invokeSomething("invokefunction", p, signers)
|
2020-07-02 12:53:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// invokeSomething is an inner wrapper for Invoke* functions
|
2020-07-29 16:57:38 +00:00
|
|
|
func (c *Client) invokeSomething(method string, p request.RawParams, signers []transaction.Signer) (*result.Invoke, error) {
|
2020-07-02 12:53:39 +00:00
|
|
|
var resp = new(result.Invoke)
|
2020-07-29 16:57:38 +00:00
|
|
|
if signers != nil {
|
|
|
|
p.Values = append(p.Values, signers)
|
2018-03-25 16:21:00 +00:00
|
|
|
}
|
2020-07-02 12:53:39 +00:00
|
|
|
if err := c.performRequest(method, p, resp); err != nil {
|
2018-11-26 15:57:53 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
2020-02-27 07:58:46 +00:00
|
|
|
// SendRawTransaction broadcasts a transaction over the NEO network.
|
2018-03-05 08:53:09 +00:00
|
|
|
// The given hex string needs to be signed with a keypair.
|
|
|
|
// When the result of the response object is true, the TX has successfully
|
|
|
|
// been broadcasted to the network.
|
2020-07-21 07:31:45 +00:00
|
|
|
func (c *Client) SendRawTransaction(rawTX *transaction.Transaction) (util.Uint256, error) {
|
2018-03-05 08:53:09 +00:00
|
|
|
var (
|
2020-01-14 12:02:38 +00:00
|
|
|
params = request.NewRawParams(hex.EncodeToString(rawTX.Bytes()))
|
2020-07-21 07:31:45 +00:00
|
|
|
resp = new(result.RelayResult)
|
2018-03-05 08:53:09 +00:00
|
|
|
)
|
2020-07-21 07:31:45 +00:00
|
|
|
if err := c.performRequest("sendrawtransaction", params, resp); err != nil {
|
|
|
|
return util.Uint256{}, err
|
2020-02-20 18:08:22 +00:00
|
|
|
}
|
2020-07-21 07:31:45 +00:00
|
|
|
return resp.Hash, nil
|
2018-03-05 08:53:09 +00:00
|
|
|
}
|
2018-12-21 09:32:18 +00:00
|
|
|
|
2020-03-11 14:14:05 +00:00
|
|
|
// SubmitBlock broadcasts a raw block over the NEO network.
|
2020-07-21 07:41:18 +00:00
|
|
|
func (c *Client) SubmitBlock(b block.Block) (util.Uint256, error) {
|
2020-03-11 14:14:05 +00:00
|
|
|
var (
|
|
|
|
params request.RawParams
|
2020-07-21 07:41:18 +00:00
|
|
|
resp = new(result.RelayResult)
|
2020-03-11 14:14:05 +00:00
|
|
|
)
|
|
|
|
buf := io.NewBufBinWriter()
|
|
|
|
b.EncodeBinary(buf.BinWriter)
|
|
|
|
if err := buf.Err; err != nil {
|
2020-07-21 07:41:18 +00:00
|
|
|
return util.Uint256{}, err
|
2020-03-11 14:14:05 +00:00
|
|
|
}
|
|
|
|
params = request.NewRawParams(hex.EncodeToString(buf.Bytes()))
|
|
|
|
|
2020-07-21 07:41:18 +00:00
|
|
|
if err := c.performRequest("submitblock", params, resp); err != nil {
|
|
|
|
return util.Uint256{}, err
|
2020-03-11 14:14:05 +00:00
|
|
|
}
|
2020-07-21 07:41:18 +00:00
|
|
|
return resp.Hash, nil
|
2020-03-11 14:14:05 +00:00
|
|
|
}
|
|
|
|
|
2019-11-29 14:59:07 +00:00
|
|
|
// SignAndPushInvocationTx signs and pushes given script as an invocation
|
|
|
|
// transaction using given wif to sign it and spending the amount of gas
|
|
|
|
// specified. It returns a hash of the invocation transaction and an error.
|
2020-07-29 16:57:38 +00:00
|
|
|
func (c *Client) SignAndPushInvocationTx(script []byte, acc *wallet.Account, sysfee int64, netfee util.Fixed8, cosigners []transaction.Signer) (util.Uint256, error) {
|
2019-11-20 13:07:43 +00:00
|
|
|
var txHash util.Uint256
|
2019-11-29 14:59:07 +00:00
|
|
|
var err error
|
2019-11-20 13:07:43 +00:00
|
|
|
|
2020-08-17 13:49:47 +00:00
|
|
|
tx, err := c.CreateTxFromScript(script, acc, sysfee, int64(netfee), cosigners...)
|
rpc/client: handle tx creation error
Fix
neo-go contract invokefunction -w wallets/neofs1.json -a NXnzw3J9VvKXjM1BPAJK4QUpTtEQu4TpU6 -r http://main_chain.neofs.devenv:30333 af5dc5f7e6a6efc64d679098f328027591a2e518 deposit 12b97a2206ae4b10c7e0194b7b655c32cc912057 int:50 bytes: -- 12b97a2206ae4b10c7e0194b7b655c32cc912057
Enter account NXnzw3J9VvKXjM1BPAJK4QUpTtEQu4TpU6 password >
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x80 pc=0x934bbc]
goroutine 1 [running]:
github.com/nspcc-dev/neo-go/pkg/core/transaction.(*Transaction).GetSignedPart(0x0, 0x1028200, 0xc00013c000, 0xc0001a2680)
github.com/nspcc-dev/neo-go/pkg/core/transaction/transaction.go:208 +0x13c
github.com/nspcc-dev/neo-go/pkg/wallet.(*Account).SignTx(0xc0000ef880, 0x0, 0x40, 0x80)
github.com/nspcc-dev/neo-go/pkg/wallet/account.go:105 +0x61
github.com/nspcc-dev/neo-go/pkg/rpc/client.(*Client).SignAndPushInvocationTx(0xc0001a0180, 0xc00008e280, 0x40, 0x80, 0xc0000ef880, 0xc140c6, 0x0, 0xc000218b90, 0x1, 0x1, ...)
github.com/nspcc-dev/neo-go/pkg/rpc/client/rpc.go:449 +0xfe
github.com/nspcc-dev/neo-go/cli/smartcontract.invokeInternal(0xc0000acb00, 0x1, 0x0, 0x0)
github.com/nspcc-dev/neo-go/cli/smartcontract/smart_contract.go:493 +0xa4d
github.com/nspcc-dev/neo-go/cli/smartcontract.invokeFunction(0xc0000acb00, 0x10100, 0xc0000acb00)
github.com/nspcc-dev/neo-go/cli/smartcontract/smart_contract.go:418 +0x30
github.com/urfave/cli.HandleAction(0xc76cc0, 0xf175d0, 0xc0000acb00, 0xc00007ef00, 0x0)
github.com/urfave/cli@v1.20.0/app.go:490 +0xc8
github.com/urfave/cli.Command.Run(0xdbcc3c, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdd9659, 0x2a, 0xde821b, ...)
github.com/urfave/cli@v1.20.0/command.go:210 +0x9e8
github.com/urfave/cli.(*App).RunAsSubcommand(0xc00021e000, 0xc0000ac840, 0x0, 0x0)
github.com/urfave/cli@v1.20.0/app.go:379 +0x88b
github.com/urfave/cli.Command.startApp(0xdb803f, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdd7472, 0x28, 0x0, ...)
github.com/urfave/cli@v1.20.0/command.go:298 +0x81a
github.com/urfave/cli.Command.Run(0xdb803f, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdd7472, 0x28, 0x0, ...)
github.com/urfave/cli@v1.20.0/command.go:98 +0x1219
github.com/urfave/cli.(*App).Run(0xc00009bd40, 0xc000030100, 0x10, 0x10, 0x0, 0x0)
github.com/urfave/cli@v1.20.0/app.go:255 +0x741
main.main()
command-line-arguments/main.go:18 +0x4b
2020-09-28 13:53:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return txHash, fmt.Errorf("failed to create tx: %w", err)
|
|
|
|
}
|
2020-03-11 18:11:51 +00:00
|
|
|
if err = acc.SignTx(tx); err != nil {
|
2020-08-06 14:44:08 +00:00
|
|
|
return txHash, fmt.Errorf("failed to sign tx: %w", err)
|
2019-11-20 13:07:43 +00:00
|
|
|
}
|
|
|
|
txHash = tx.Hash()
|
2020-07-21 07:31:45 +00:00
|
|
|
actualHash, err := c.SendRawTransaction(tx)
|
2019-11-20 13:07:43 +00:00
|
|
|
if err != nil {
|
2020-08-06 14:44:08 +00:00
|
|
|
return txHash, fmt.Errorf("failed to send tx: %w", err)
|
2019-11-20 13:07:43 +00:00
|
|
|
}
|
2020-07-21 07:31:45 +00:00
|
|
|
if !actualHash.Equals(txHash) {
|
|
|
|
return actualHash, fmt.Errorf("sent and actual tx hashes mismatch:\n\tsent: %v\n\tactual: %v", txHash.StringLE(), actualHash.StringLE())
|
|
|
|
}
|
2019-11-20 13:07:43 +00:00
|
|
|
return txHash, nil
|
|
|
|
}
|
2020-03-11 14:14:05 +00:00
|
|
|
|
2020-07-29 16:57:38 +00:00
|
|
|
// getSigners returns an array of transaction signers from given sender and cosigners.
|
|
|
|
// If cosigners list already contains sender, the sender will be placed at the start of
|
|
|
|
// the list.
|
|
|
|
func getSigners(sender util.Uint160, cosigners []transaction.Signer) []transaction.Signer {
|
|
|
|
s := transaction.Signer{
|
|
|
|
Account: sender,
|
2020-10-01 12:26:51 +00:00
|
|
|
Scopes: transaction.None,
|
2020-07-29 16:57:38 +00:00
|
|
|
}
|
|
|
|
for i, c := range cosigners {
|
|
|
|
if c.Account == sender {
|
|
|
|
if i == 0 {
|
|
|
|
return cosigners
|
|
|
|
}
|
|
|
|
s.Scopes = c.Scopes
|
|
|
|
cosigners = append(cosigners[:i], cosigners[i+1:]...)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return append([]transaction.Signer{s}, cosigners...)
|
|
|
|
}
|
|
|
|
|
2020-03-11 14:14:05 +00:00
|
|
|
// ValidateAddress verifies that the address is a correct NEO address.
|
|
|
|
func (c *Client) ValidateAddress(address string) error {
|
|
|
|
var (
|
|
|
|
params = request.NewRawParams(address)
|
|
|
|
resp = &result.ValidateAddress{}
|
|
|
|
)
|
|
|
|
|
2020-03-24 17:11:49 +00:00
|
|
|
if err := c.performRequest("validateaddress", params, resp); err != nil {
|
2020-03-11 14:14:05 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !resp.IsValid {
|
|
|
|
return errors.New("validateaddress returned false")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2020-04-15 06:50:13 +00:00
|
|
|
|
|
|
|
// CalculateValidUntilBlock calculates ValidUntilBlock field for tx as
|
|
|
|
// current blockchain height + number of validators. Number of validators
|
2020-10-01 12:26:54 +00:00
|
|
|
// is the length of blockchain validators list got from GetNextBlockValidators()
|
2020-04-15 06:50:13 +00:00
|
|
|
// method. Validators count is being cached and updated every 100 blocks.
|
|
|
|
func (c *Client) CalculateValidUntilBlock() (uint32, error) {
|
|
|
|
var (
|
|
|
|
result uint32
|
|
|
|
validatorsCount uint32
|
|
|
|
)
|
|
|
|
blockCount, err := c.GetBlockCount()
|
|
|
|
if err != nil {
|
2020-08-06 14:44:08 +00:00
|
|
|
return result, fmt.Errorf("can't get block count: %w", err)
|
2020-04-15 06:50:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if c.cache.calculateValidUntilBlock.expiresAt > blockCount {
|
|
|
|
validatorsCount = c.cache.calculateValidUntilBlock.validatorsCount
|
|
|
|
} else {
|
2020-10-01 12:26:54 +00:00
|
|
|
validators, err := c.GetNextBlockValidators()
|
2020-04-15 06:50:13 +00:00
|
|
|
if err != nil {
|
2020-08-06 14:44:08 +00:00
|
|
|
return result, fmt.Errorf("can't get validators: %w", err)
|
2020-04-15 06:50:13 +00:00
|
|
|
}
|
|
|
|
validatorsCount = uint32(len(validators))
|
|
|
|
c.cache.calculateValidUntilBlock = calculateValidUntilBlockCache{
|
|
|
|
validatorsCount: validatorsCount,
|
|
|
|
expiresAt: blockCount + cacheTimeout,
|
|
|
|
}
|
|
|
|
}
|
2020-09-04 13:03:31 +00:00
|
|
|
return blockCount + validatorsCount + 1, nil
|
2020-04-15 06:50:13 +00:00
|
|
|
}
|
2020-05-08 17:54:24 +00:00
|
|
|
|
2020-07-06 07:49:24 +00:00
|
|
|
// AddNetworkFee adds network fee for each witness script and optional extra
|
2020-08-17 08:12:21 +00:00
|
|
|
// network fee to transaction. `accs` is an array signer's accounts.
|
|
|
|
func (c *Client) AddNetworkFee(tx *transaction.Transaction, extraFee int64, accs ...*wallet.Account) error {
|
|
|
|
if len(tx.Signers) != len(accs) {
|
|
|
|
return errors.New("number of signers must match number of scripts")
|
2020-05-08 17:54:24 +00:00
|
|
|
}
|
2020-08-17 08:12:21 +00:00
|
|
|
size := io.GetVarSize(tx)
|
|
|
|
for i, cosigner := range tx.Signers {
|
2020-08-24 11:00:05 +00:00
|
|
|
if accs[i].Contract.Deployed {
|
|
|
|
res, err := c.InvokeFunction(cosigner.Account, manifest.MethodVerify, []smartcontract.Parameter{}, tx.Signers)
|
2020-10-05 13:33:20 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to invoke verify: %w", err)
|
|
|
|
}
|
|
|
|
if res.State != "HALT" {
|
|
|
|
return fmt.Errorf("invalid VM state %s due to an error: %s", res.State, res.FaultException)
|
|
|
|
}
|
|
|
|
if l := len(res.Stack); l != 1 {
|
|
|
|
return fmt.Errorf("result stack length should be equal to 1, got %d", l)
|
|
|
|
}
|
|
|
|
r, err := topIntFromStack(res.Stack)
|
|
|
|
if err != nil || r == 0 {
|
2020-08-24 11:00:05 +00:00
|
|
|
return core.ErrVerificationFailed
|
2020-06-25 07:55:45 +00:00
|
|
|
}
|
2020-08-24 11:00:05 +00:00
|
|
|
tx.NetworkFee += res.GasConsumed
|
|
|
|
size += io.GetVarSize([]byte{}) * 2 // both scripts are empty
|
|
|
|
continue
|
2020-05-08 17:54:24 +00:00
|
|
|
}
|
2020-09-28 14:56:16 +00:00
|
|
|
netFee, sizeDelta := fee.Calculate(accs[i].Contract.Script)
|
2020-05-08 17:54:24 +00:00
|
|
|
tx.NetworkFee += netFee
|
|
|
|
size += sizeDelta
|
|
|
|
}
|
2020-06-16 12:46:28 +00:00
|
|
|
fee, err := c.GetFeePerByte()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-07-06 07:49:24 +00:00
|
|
|
tx.NetworkFee += int64(size)*fee + extraFee
|
2020-05-08 17:54:24 +00:00
|
|
|
return nil
|
|
|
|
}
|
2020-10-14 15:13:20 +00:00
|
|
|
|
|
|
|
// GetNetwork returns the network magic of the RPC node client connected to.
|
|
|
|
func (c *Client) GetNetwork() netmode.Magic {
|
|
|
|
return c.network
|
|
|
|
}
|
2020-09-25 16:32:53 +00:00
|
|
|
|
|
|
|
// GetNativeContractHash returns native contract hash by its name. It is not case-sensitive.
|
|
|
|
func (c *Client) GetNativeContractHash(name string) (util.Uint160, error) {
|
|
|
|
lowercasedName := strings.ToLower(name)
|
|
|
|
hash, ok := c.cache.nativeHashes[lowercasedName]
|
|
|
|
if ok {
|
|
|
|
return hash, nil
|
|
|
|
}
|
|
|
|
cs, err := c.GetContractStateByAddressOrName(name)
|
|
|
|
if err != nil {
|
|
|
|
return util.Uint160{}, err
|
|
|
|
}
|
|
|
|
c.cache.nativeHashes[lowercasedName] = cs.ScriptHash()
|
|
|
|
return cs.ScriptHash(), nil
|
|
|
|
}
|