mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-29 23:33:37 +00:00
Merge pull request #2090 from nspcc-dev/new-query-commands
New query commands
This commit is contained in:
commit
6e2eddbeb9
11 changed files with 330 additions and 85 deletions
|
@ -15,6 +15,8 @@ import (
|
||||||
func TestRegisterCandidate(t *testing.T) {
|
func TestRegisterCandidate(t *testing.T) {
|
||||||
e := newExecutor(t, true)
|
e := newExecutor(t, true)
|
||||||
|
|
||||||
|
validatorHex := hex.EncodeToString(validatorPriv.PublicKey().Bytes())
|
||||||
|
|
||||||
e.In.WriteString("one\r")
|
e.In.WriteString("one\r")
|
||||||
e.Run(t, "neo-go", "wallet", "nep17", "multitransfer",
|
e.Run(t, "neo-go", "wallet", "nep17", "multitransfer",
|
||||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||||
|
@ -24,6 +26,15 @@ func TestRegisterCandidate(t *testing.T) {
|
||||||
"GAS:"+validatorPriv.Address()+":10000")
|
"GAS:"+validatorPriv.Address()+":10000")
|
||||||
e.checkTxPersisted(t)
|
e.checkTxPersisted(t)
|
||||||
|
|
||||||
|
e.Run(t, "neo-go", "query", "committee",
|
||||||
|
"--rpc-endpoint", "http://"+e.RPC.Addr)
|
||||||
|
e.checkNextLine(t, "^\\s*"+validatorHex)
|
||||||
|
|
||||||
|
e.Run(t, "neo-go", "query", "candidates",
|
||||||
|
"--rpc-endpoint", "http://"+e.RPC.Addr)
|
||||||
|
e.checkNextLine(t, "^\\s*Key.+$") // Header.
|
||||||
|
e.checkEOF(t)
|
||||||
|
|
||||||
// missing address
|
// missing address
|
||||||
e.RunWithError(t, "neo-go", "wallet", "candidate", "register",
|
e.RunWithError(t, "neo-go", "wallet", "candidate", "register",
|
||||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||||
|
@ -48,7 +59,7 @@ func TestRegisterCandidate(t *testing.T) {
|
||||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||||
"--wallet", validatorWallet,
|
"--wallet", validatorWallet,
|
||||||
"--address", validatorPriv.Address(),
|
"--address", validatorPriv.Address(),
|
||||||
"--candidate", hex.EncodeToString(validatorPriv.PublicKey().Bytes()))
|
"--candidate", validatorHex)
|
||||||
_, index := e.checkTxPersisted(t)
|
_, index := e.checkTxPersisted(t)
|
||||||
|
|
||||||
vs, err = e.Chain.GetEnrollments()
|
vs, err = e.Chain.GetEnrollments()
|
||||||
|
@ -57,11 +68,21 @@ func TestRegisterCandidate(t *testing.T) {
|
||||||
b, _ := e.Chain.GetGoverningTokenBalance(validatorPriv.GetScriptHash())
|
b, _ := e.Chain.GetGoverningTokenBalance(validatorPriv.GetScriptHash())
|
||||||
require.Equal(t, b, vs[0].Votes)
|
require.Equal(t, b, vs[0].Votes)
|
||||||
|
|
||||||
|
e.Run(t, "neo-go", "query", "committee",
|
||||||
|
"--rpc-endpoint", "http://"+e.RPC.Addr)
|
||||||
|
e.checkNextLine(t, "^\\s*"+validatorHex)
|
||||||
|
|
||||||
|
e.Run(t, "neo-go", "query", "candidates",
|
||||||
|
"--rpc-endpoint", "http://"+e.RPC.Addr)
|
||||||
|
e.checkNextLine(t, "^\\s*Key.+$") // Header.
|
||||||
|
e.checkNextLine(t, "^\\s*"+validatorHex+"\\s*"+b.String()+"\\s*true\\s*true$")
|
||||||
|
e.checkEOF(t)
|
||||||
|
|
||||||
// check state
|
// check state
|
||||||
e.Run(t, "neo-go", "wallet", "candidate", "getstate",
|
e.Run(t, "neo-go", "query", "voter",
|
||||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||||
"--address", validatorPriv.Address())
|
validatorPriv.Address())
|
||||||
e.checkNextLine(t, "^\\s*Voted:\\s+"+validatorPriv.Address())
|
e.checkNextLine(t, "^\\s*Voted:\\s+"+validatorHex+"\\s+\\("+validatorPriv.Address()+"\\)$")
|
||||||
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+b.String()+"$")
|
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+b.String()+"$")
|
||||||
e.checkNextLine(t, "^\\s*Block\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
|
e.checkNextLine(t, "^\\s*Block\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
|
||||||
e.checkEOF(t)
|
e.checkEOF(t)
|
||||||
|
@ -80,9 +101,9 @@ func TestRegisterCandidate(t *testing.T) {
|
||||||
require.Equal(t, big.NewInt(0), vs[0].Votes)
|
require.Equal(t, big.NewInt(0), vs[0].Votes)
|
||||||
|
|
||||||
// check state
|
// check state
|
||||||
e.Run(t, "neo-go", "wallet", "candidate", "getstate",
|
e.Run(t, "neo-go", "query", "voter",
|
||||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||||
"--address", validatorPriv.Address())
|
validatorPriv.Address())
|
||||||
e.checkNextLine(t, "^\\s*Voted:\\s+"+"null") // no vote.
|
e.checkNextLine(t, "^\\s*Voted:\\s+"+"null") // no vote.
|
||||||
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+b.String()+"$")
|
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+b.String()+"$")
|
||||||
e.checkNextLine(t, "^\\s*Block\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
|
e.checkNextLine(t, "^\\s*Block\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
|
||||||
|
@ -104,6 +125,6 @@ func TestRegisterCandidate(t *testing.T) {
|
||||||
vs, err = e.Chain.GetEnrollments()
|
vs, err = e.Chain.GetEnrollments()
|
||||||
require.Equal(t, 0, len(vs))
|
require.Equal(t, 0, len(vs))
|
||||||
|
|
||||||
// getstate: missing address
|
// query voter: missing address
|
||||||
e.RunWithError(t, "neo-go", "wallet", "candidate", "getstate")
|
e.RunWithError(t, "neo-go", "query", "voter")
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,17 +3,24 @@ package query
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/cli/flags"
|
||||||
"github.com/nspcc-dev/neo-go/cli/options"
|
"github.com/nspcc-dev/neo-go/cli/options"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
|
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -29,12 +36,36 @@ func NewCommands() []cli.Command {
|
||||||
Name: "query",
|
Name: "query",
|
||||||
Usage: "Query data from RPC node",
|
Usage: "Query data from RPC node",
|
||||||
Subcommands: []cli.Command{
|
Subcommands: []cli.Command{
|
||||||
|
{
|
||||||
|
Name: "candidates",
|
||||||
|
Usage: "Get candidates and votes",
|
||||||
|
Action: queryCandidates,
|
||||||
|
Flags: options.RPC,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "committee",
|
||||||
|
Usage: "Get committee list",
|
||||||
|
Action: queryCommittee,
|
||||||
|
Flags: options.RPC,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "height",
|
||||||
|
Usage: "Get node height",
|
||||||
|
Action: queryHeight,
|
||||||
|
Flags: options.RPC,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "tx",
|
Name: "tx",
|
||||||
Usage: "Query transaction status",
|
Usage: "Query transaction status",
|
||||||
Action: queryTx,
|
Action: queryTx,
|
||||||
Flags: queryTxFlags,
|
Flags: queryTxFlags,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "voter",
|
||||||
|
Usage: "Print NEO holder account state",
|
||||||
|
Action: queryVoter,
|
||||||
|
Flags: options.RPC,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@ -113,3 +144,150 @@ func dumpApplicationLog(ctx *cli.Context, res *result.ApplicationLog, tx *result
|
||||||
_ = tw.Flush()
|
_ = tw.Flush()
|
||||||
fmt.Fprint(ctx.App.Writer, buf.String())
|
fmt.Fprint(ctx.App.Writer, buf.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func queryCandidates(ctx *cli.Context) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
gctx, cancel := options.GetTimeoutContext(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
c, err := options.GetRPCClient(gctx, ctx)
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
vals, err := c.GetNextBlockValidators()
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
comm, err := c.GetCommittee()
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(vals, func(i, j int) bool {
|
||||||
|
if vals[i].Active != vals[j].Active {
|
||||||
|
return vals[i].Active
|
||||||
|
}
|
||||||
|
if vals[i].Votes != vals[j].Votes {
|
||||||
|
return vals[i].Votes > vals[j].Votes
|
||||||
|
}
|
||||||
|
return vals[i].PublicKey.Cmp(&vals[j].PublicKey) == -1
|
||||||
|
})
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
tw := tabwriter.NewWriter(buf, 0, 2, 2, ' ', 0)
|
||||||
|
_, _ = tw.Write([]byte("Key\tVotes\tCommittee\tConsensus\n"))
|
||||||
|
for _, val := range vals {
|
||||||
|
_, _ = tw.Write([]byte(fmt.Sprintf("%s\t%d\t%t\t%t\n", hex.EncodeToString(val.PublicKey.Bytes()), val.Votes, comm.Contains(&val.PublicKey), val.Active)))
|
||||||
|
}
|
||||||
|
_ = tw.Flush()
|
||||||
|
fmt.Fprint(ctx.App.Writer, buf.String())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryCommittee(ctx *cli.Context) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
gctx, cancel := options.GetTimeoutContext(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
c, err := options.GetRPCClient(gctx, ctx)
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
comm, err := c.GetCommittee()
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, k := range comm {
|
||||||
|
fmt.Fprintln(ctx.App.Writer, hex.EncodeToString(k.Bytes()))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryHeight(ctx *cli.Context) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
gctx, cancel := options.GetTimeoutContext(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
c, err := options.GetRPCClient(gctx, ctx)
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
blockCount, err := c.GetBlockCount()
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
blockHeight := blockCount - 1 // GetBlockCount returns block count (including 0), not the highest block index.
|
||||||
|
|
||||||
|
fmt.Fprintf(ctx.App.Writer, "Latest block: %d\n", blockHeight)
|
||||||
|
|
||||||
|
stateHeight, err := c.GetStateHeight()
|
||||||
|
if err == nil { // We can be talking to a node without getstateheight request support.
|
||||||
|
fmt.Fprintf(ctx.App.Writer, "Validated state: %d\n", stateHeight.Validated)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryVoter(ctx *cli.Context) error {
|
||||||
|
args := ctx.Args()
|
||||||
|
if len(args) == 0 {
|
||||||
|
return cli.NewExitError("No address specified", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
addr, err := flags.ParseAddress(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(fmt.Sprintf("wrong address: %s", args[0]), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
gctx, cancel := options.GetTimeoutContext(ctx)
|
||||||
|
defer cancel()
|
||||||
|
c, exitErr := options.GetRPCClient(gctx, ctx)
|
||||||
|
if exitErr != nil {
|
||||||
|
return exitErr
|
||||||
|
}
|
||||||
|
|
||||||
|
neoHash, err := c.GetNativeContractHash(nativenames.Neo)
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(fmt.Errorf("failed to get NEO contract hash: %w", err), 1)
|
||||||
|
}
|
||||||
|
res, err := c.InvokeFunction(neoHash, "getAccountState", []smartcontract.Parameter{
|
||||||
|
{
|
||||||
|
Type: smartcontract.Hash160Type,
|
||||||
|
Value: addr,
|
||||||
|
},
|
||||||
|
}, nil)
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
if res.State != "HALT" {
|
||||||
|
return cli.NewExitError(fmt.Errorf("invocation failed: %s", res.FaultException), 1)
|
||||||
|
}
|
||||||
|
if len(res.Stack) == 0 {
|
||||||
|
return cli.NewExitError("result stack is empty", 1)
|
||||||
|
}
|
||||||
|
st := new(state.NEOBalance)
|
||||||
|
if _, ok := res.Stack[0].(stackitem.Null); !ok {
|
||||||
|
err = st.FromStackItem(res.Stack[0])
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(fmt.Errorf("failed to convert account state from stackitem: %w", err), 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dec, err := c.NEP17Decimals(neoHash)
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(fmt.Errorf("failed to get decimals: %w", err), 1)
|
||||||
|
}
|
||||||
|
voted := "null"
|
||||||
|
if st.VoteTo != nil {
|
||||||
|
voted = fmt.Sprintf("%s (%s)", hex.EncodeToString(st.VoteTo.Bytes()), address.Uint160ToString(st.VoteTo.GetScriptHash()))
|
||||||
|
}
|
||||||
|
fmt.Fprintf(ctx.App.Writer, "\tVoted: %s\n", voted)
|
||||||
|
fmt.Fprintf(ctx.App.Writer, "\tAmount : %s\n", fixedn.ToString(&st.Balance, int(dec)))
|
||||||
|
fmt.Fprintf(ctx.App.Writer, "\tBlock: %d\n", st.BalanceHeight)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -130,3 +130,12 @@ func (e *executor) compareQueryTxVerbose(t *testing.T, tx *transaction.Transacti
|
||||||
}
|
}
|
||||||
e.checkEOF(t)
|
e.checkEOF(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestQueryHeight(t *testing.T) {
|
||||||
|
e := newExecutor(t, true)
|
||||||
|
|
||||||
|
e.Run(t, "neo-go", "query", "height", "--rpc-endpoint", "http://"+e.RPC.Addr)
|
||||||
|
e.checkNextLine(t, `^Latest block: [0-9]+$`)
|
||||||
|
e.checkNextLine(t, `^Validated state: [0-9]+$`)
|
||||||
|
e.checkEOF(t)
|
||||||
|
}
|
||||||
|
|
|
@ -7,14 +7,11 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/cli/input"
|
"github.com/nspcc-dev/neo-go/cli/input"
|
||||||
"github.com/nspcc-dev/neo-go/cli/options"
|
"github.com/nspcc-dev/neo-go/cli/options"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpc/client"
|
"github.com/nspcc-dev/neo-go/pkg/rpc/client"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
|
@ -74,18 +71,6 @@ func newValidatorCommands() []cli.Command {
|
||||||
},
|
},
|
||||||
}, options.RPC...),
|
}, options.RPC...),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Name: "getstate",
|
|
||||||
Usage: "print NEO holder account state",
|
|
||||||
UsageText: "getstate -a <addr>",
|
|
||||||
Action: getAccountState,
|
|
||||||
Flags: append([]cli.Flag{
|
|
||||||
flags.AddressFlag{
|
|
||||||
Name: "address, a",
|
|
||||||
Usage: "Address to get state of",
|
|
||||||
},
|
|
||||||
}, options.RPC...),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,54 +212,3 @@ func getDecryptedAccount(ctx *cli.Context, wall *wallet.Wallet, addr util.Uint16
|
||||||
}
|
}
|
||||||
return acc, nil
|
return acc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAccountState(ctx *cli.Context) error {
|
|
||||||
addrFlag := ctx.Generic("address").(*flags.Address)
|
|
||||||
if !addrFlag.IsSet {
|
|
||||||
return cli.NewExitError("address was not provided", 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
gctx, cancel := options.GetTimeoutContext(ctx)
|
|
||||||
defer cancel()
|
|
||||||
c, exitErr := options.GetRPCClient(gctx, ctx)
|
|
||||||
if exitErr != nil {
|
|
||||||
return exitErr
|
|
||||||
}
|
|
||||||
|
|
||||||
neoHash, err := c.GetNativeContractHash(nativenames.Neo)
|
|
||||||
if err != nil {
|
|
||||||
return cli.NewExitError(fmt.Errorf("failed to get NEO contract hash: %w", err), 1)
|
|
||||||
}
|
|
||||||
res, err := c.InvokeFunction(neoHash, "getAccountState", []smartcontract.Parameter{
|
|
||||||
{
|
|
||||||
Type: smartcontract.Hash160Type,
|
|
||||||
Value: addrFlag.Uint160(),
|
|
||||||
},
|
|
||||||
}, nil)
|
|
||||||
if err != nil {
|
|
||||||
return cli.NewExitError(err, 1)
|
|
||||||
}
|
|
||||||
if res.State != "HALT" {
|
|
||||||
return cli.NewExitError(fmt.Errorf("invocation failed: %s", res.FaultException), 1)
|
|
||||||
}
|
|
||||||
if len(res.Stack) == 0 {
|
|
||||||
return cli.NewExitError("result stack is empty", 1)
|
|
||||||
}
|
|
||||||
st := new(state.NEOBalance)
|
|
||||||
err = st.FromStackItem(res.Stack[0])
|
|
||||||
if err != nil {
|
|
||||||
return cli.NewExitError(fmt.Errorf("failed to convert account state from stackitem: %w", err), 1)
|
|
||||||
}
|
|
||||||
dec, err := c.NEP17Decimals(neoHash)
|
|
||||||
if err != nil {
|
|
||||||
return cli.NewExitError(fmt.Errorf("failed to get decimals: %w", err), 1)
|
|
||||||
}
|
|
||||||
voted := "null"
|
|
||||||
if st.VoteTo != nil {
|
|
||||||
voted = address.Uint160ToString(st.VoteTo.GetScriptHash())
|
|
||||||
}
|
|
||||||
fmt.Fprintf(ctx.App.Writer, "\tVoted: %s\n", voted)
|
|
||||||
fmt.Fprintf(ctx.App.Writer, "\tAmount : %s\n", fixedn.ToString(&st.Balance, int(dec)))
|
|
||||||
fmt.Fprintf(ctx.App.Writer, "\tBlock: %d\n", st.BalanceHeight)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
78
docs/cli.md
78
docs/cli.md
|
@ -464,7 +464,17 @@ You can also vote for candidates if you own NEO:
|
||||||
./bin/neo-go wallet candidate vote -a NMe64G6j6nkPZby26JAgpaCNrn1Ee4wW6E -w wallet.json -r http://localhost:20332 -c 03cecd63d7d8120c3b194c3b2880dd4aafe1475c57e40c852872d7305615258140
|
./bin/neo-go wallet candidate vote -a NMe64G6j6nkPZby26JAgpaCNrn1Ee4wW6E -w wallet.json -r http://localhost:20332 -c 03cecd63d7d8120c3b194c3b2880dd4aafe1475c57e40c852872d7305615258140
|
||||||
```
|
```
|
||||||
|
|
||||||
### Querying transaction status
|
### Getting data from chain
|
||||||
|
|
||||||
|
#### Node height/validated height
|
||||||
|
`query height` returns the latest block and validated state height:
|
||||||
|
```
|
||||||
|
$ ./bin/neo-go query height -r http://localhost:20332
|
||||||
|
Latest block: 11926
|
||||||
|
Validated state: 11926
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Transaction status
|
||||||
`query tx` provides convenient wrapper over RPC calls to query transaction status.
|
`query tx` provides convenient wrapper over RPC calls to query transaction status.
|
||||||
```
|
```
|
||||||
./bin/neo-go query tx --rpc-endpoint http://localhost:20332 aaf87628851e0c03ee086ff88596bc24de87082e9e5c73d75bb1c740d1d68088
|
./bin/neo-go query tx --rpc-endpoint http://localhost:20332 aaf87628851e0c03ee086ff88596bc24de87082e9e5c73d75bb1c740d1d68088
|
||||||
|
@ -476,6 +486,72 @@ Success: true
|
||||||
`OnChain` is true if transaction was included in block and `Success` is true
|
`OnChain` is true if transaction was included in block and `Success` is true
|
||||||
if it was executed successfully.
|
if it was executed successfully.
|
||||||
|
|
||||||
|
#### Committee members
|
||||||
|
`query commitee` returns a list of current committee members:
|
||||||
|
```
|
||||||
|
$ ./bin/neo-go query committee -r http://localhost:20332
|
||||||
|
03009b7540e10f2562e5fd8fac9eaec25166a58b26e412348ff5a86927bfac22a2
|
||||||
|
030205e9cefaea5a1dfc580af20c8d5aa2468bb0148f1a5e4605fc622c80e604ba
|
||||||
|
0207da870cedb777fceff948641021714ec815110ca111ccc7a54c168e065bda70
|
||||||
|
02147c1b1d5728e1954958daff2f88ee2fa50a06890a8a9db3fa9e972b66ae559f
|
||||||
|
0214baf0ceea3a66f17e7e1e839ea25fd8bed6cd82e6bb6e68250189065f44ff01
|
||||||
|
03184b018d6b2bc093e535519732b3fd3f7551c8cffaf4621dd5a0b89482ca66c9
|
||||||
|
0231edee3978d46c335e851c76059166eb8878516f459e085c0dd092f0f1d51c21
|
||||||
|
023e9b32ea89b94d066e649b124fd50e396ee91369e8e2a6ae1b11c170d022256d
|
||||||
|
03408dcd416396f64783ac587ea1e1593c57d9fea880c8a6a1920e92a259477806
|
||||||
|
035056669864feea401d8c31e447fb82dd29f342a9476cfd449584ce2a6165e4d7
|
||||||
|
025831cee3708e87d78211bec0d1bfee9f4c85ae784762f042e7f31c0d40c329b8
|
||||||
|
026328aae34f149853430f526ecaa9cf9c8d78a4ea82d08bdf63dd03c4d0693be6
|
||||||
|
0370c75c54445565df62cfe2e76fbec4ba00d1298867972213530cae6d418da636
|
||||||
|
03840415b0a0fcf066bcc3dc92d8349ebd33a6ab1402ef649bae00e5d9f5840828
|
||||||
|
03957af9e77282ae3263544b7b2458903624adc3f5dee303957cb6570524a5f254
|
||||||
|
02a7834be9b32e2981d157cb5bbd3acb42cfd11ea5c3b10224d7a44e98c5910f1b
|
||||||
|
02ba2c70f5996f357a43198705859fae2cfea13e1172962800772b3d588a9d4abd
|
||||||
|
03c609bea5a4825908027e4ab217e7efc06e311f19ecad9d417089f14927a173d5
|
||||||
|
02c69a8d084ee7319cfecf5161ff257aa2d1f53e79bf6c6f164cff5d94675c38b3
|
||||||
|
02cf9dc6e85d581480d91e88e8cbeaa0c153a046e89ded08b4cefd851e1d7325b5
|
||||||
|
03d84d22b8753cf225d263a3a782a4e16ca72ef323cfde04977c74f14873ab1e4c
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Candidate/voting data
|
||||||
|
`query candidates` returns all current candidates, number of votes for them
|
||||||
|
and their committee/consensus status:
|
||||||
|
```
|
||||||
|
$ ./bin/neo-go query candidates -r http://localhost:20332
|
||||||
|
Key Votes Committee Consensus
|
||||||
|
03009b7540e10f2562e5fd8fac9eaec25166a58b26e412348ff5a86927bfac22a2 2000000 true true
|
||||||
|
030205e9cefaea5a1dfc580af20c8d5aa2468bb0148f1a5e4605fc622c80e604ba 2000000 true true
|
||||||
|
0214baf0ceea3a66f17e7e1e839ea25fd8bed6cd82e6bb6e68250189065f44ff01 2000000 true true
|
||||||
|
023e9b32ea89b94d066e649b124fd50e396ee91369e8e2a6ae1b11c170d022256d 2000000 true true
|
||||||
|
03408dcd416396f64783ac587ea1e1593c57d9fea880c8a6a1920e92a259477806 2000000 true true
|
||||||
|
02a7834be9b32e2981d157cb5bbd3acb42cfd11ea5c3b10224d7a44e98c5910f1b 2000000 true true
|
||||||
|
02ba2c70f5996f357a43198705859fae2cfea13e1172962800772b3d588a9d4abd 2000000 true true
|
||||||
|
025664cef0abcba7787ad5fb12f3af31c5cdc7a479068aa2ad8ee78804768bffe9 1000000 false false
|
||||||
|
03650a684461a64bf46bee561d9981a4c57adc6ccbd3a9512b83701480b30218ab 1000000 false false
|
||||||
|
026a10aa2b4d7639c5deafa4ff081467db10b5d00432749a2a5ee1d2bfed23e1c0 1000000 false false
|
||||||
|
02d5786a9214a8a3f1757d7596fd10f5241205e2c0d68362f4766579bac6189249 1000000 false false
|
||||||
|
033d8e35f8cd9a33852280b6d93093c7292ed5ce90d90f149fa2da50ba6168dfce 100000 false false
|
||||||
|
0349c7ef0b4aaf181f0a3e1350c527b136cc5b42498cb83ab8880c05ed95167e1c 100000 false false
|
||||||
|
035b4f9be2b853e06eb5a09c167e038b96b4804235961510423252f2ee3dbba583 100000 false false
|
||||||
|
027e459b264b6f7e325ab4b0bb0fa641081fb68517fd613ebd7a94cb79d3081e4f 100000 false false
|
||||||
|
0288cad442a877960c76b4f688f4be30f768256d9a3da2492b0180b91243918b4f 100000 false false
|
||||||
|
02a40c552798f79636095817ec88924fc6cb7094e5a3cb059a9b3bc91ea3bf0d3d 100000 false false
|
||||||
|
02db79e69c518ae9254e314b6f5f4b63e914cdd4b2574dc2f9236c01c1fc1d8973 100000 false false
|
||||||
|
02ec143f00b88524caf36a0121c2de09eef0519ddbe1c710a00f0e2663201ee4c0 100000 false false
|
||||||
|
03d8d58d2257ca6cb14522b76513d4783f7d481801695893794c2186515c6de76f 0 false false
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Voter data
|
||||||
|
`query voter` returns additional data about NEO holder: amount of NEO he has,
|
||||||
|
candidate he voted for (if any) and block number of the last transactions
|
||||||
|
involving NEO on this account:
|
||||||
|
```
|
||||||
|
$ ./bin/neo-go query voter -r http://localhost:20332 Nj91C8TxQSxW1jCE1ytFre6mg5qxTypg1Y
|
||||||
|
Voted: 0214baf0ceea3a66f17e7e1e839ea25fd8bed6cd82e6bb6e68250189065f44ff01 (Nj91C8TxQSxW1jCE1ytFre6mg5qxTypg1Y)
|
||||||
|
Amount : 2000000
|
||||||
|
Block: 3970
|
||||||
|
```
|
||||||
|
|
||||||
### NEP-17 token functions
|
### NEP-17 token functions
|
||||||
|
|
||||||
`wallet nep17` contains a set of commands to use for NEP-17 tokens.
|
`wallet nep17` contains a set of commands to use for NEP-17 tokens.
|
||||||
|
|
|
@ -92,16 +92,16 @@ use.
|
||||||
This command will create and send appropriate transaction to the network and
|
This command will create and send appropriate transaction to the network and
|
||||||
you should then wait for it to settle in a block. If all goes well it'll end
|
you should then wait for it to settle in a block. If all goes well it'll end
|
||||||
with "HALT" state and your registration will be completed. You can use
|
with "HALT" state and your registration will be completed. You can use
|
||||||
`query tx` command to see transaction status or
|
`query tx` command to see transaction status or `query candidates` to see if
|
||||||
`getnextblockvalidators` to see if your candidate was added.
|
your candidate was added.
|
||||||
|
|
||||||
### Voting
|
### Voting
|
||||||
|
|
||||||
After registration completion if you own some NEO you can also vote for your
|
After registration completion if you own some NEO you can also vote for your
|
||||||
candidate to help it become CN and receive additional voter GAS. To do that
|
candidate to help it become CN and receive additional voter GAS. To do that
|
||||||
you need to know the public key of your candidate, which can either be seen in
|
you need to know the public key of your candidate, which can either be seen in
|
||||||
`getnextblockvalidators` RPC call output or extracted from wallet `wallet
|
`query candidates` command output or extracted from wallet `wallet dump-keys`
|
||||||
dump-keys` command:
|
command:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ neo-go wallet dump-keys -w wallet.json
|
$ neo-go wallet dump-keys -w wallet.json
|
||||||
|
|
|
@ -374,6 +374,18 @@ func (c *Client) GetRawTransactionVerbose(hash util.Uint256) (*result.Transactio
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetStateHeight returns current validated and local node state height.
|
||||||
|
func (c *Client) GetStateHeight() (*result.StateHeight, error) {
|
||||||
|
var (
|
||||||
|
params = request.NewRawParams()
|
||||||
|
resp = new(result.StateHeight)
|
||||||
|
)
|
||||||
|
if err := c.performRequest("getstateheight", params, resp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetStorageByID returns the stored value, according to the contract ID and the stored key.
|
// GetStorageByID returns the stored value, according to the contract ID and the stored key.
|
||||||
func (c *Client) GetStorageByID(id int32, key []byte) ([]byte, error) {
|
func (c *Client) GetStorageByID(id int32, key []byte) ([]byte, error) {
|
||||||
return c.getStorage(request.NewRawParams(id, base64.StdEncoding.EncodeToString(key)))
|
return c.getStorage(request.NewRawParams(id, base64.StdEncoding.EncodeToString(key)))
|
||||||
|
|
|
@ -680,6 +680,21 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"getstateheight": {
|
||||||
|
{
|
||||||
|
name: "positive",
|
||||||
|
invoke: func(c *Client) (interface{}, error) {
|
||||||
|
return c.GetStateHeight()
|
||||||
|
},
|
||||||
|
serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"localrootindex":11646,"validatedrootindex":11645}}`,
|
||||||
|
result: func(c *Client) interface{} {
|
||||||
|
return &result.StateHeight{
|
||||||
|
Local: 11646,
|
||||||
|
Validated: 11645,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
"getstorage": {
|
"getstorage": {
|
||||||
{
|
{
|
||||||
name: "by hash, positive",
|
name: "by hash, positive",
|
||||||
|
|
|
@ -10,8 +10,8 @@ import (
|
||||||
|
|
||||||
// StateHeight is a result of getstateheight RPC.
|
// StateHeight is a result of getstateheight RPC.
|
||||||
type StateHeight struct {
|
type StateHeight struct {
|
||||||
BlockHeight uint32 `json:"blockHeight"`
|
Local uint32 `json:"localrootindex"`
|
||||||
StateHeight uint32 `json:"stateHeight"`
|
Validated uint32 `json:"validatedrootindex"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProofWithKey represens key-proof pair.
|
// ProofWithKey represens key-proof pair.
|
||||||
|
|
|
@ -969,8 +969,8 @@ func (s *Server) getStateHeight(_ request.Params) (interface{}, *response.Error)
|
||||||
stateHeight = height - 1
|
stateHeight = height - 1
|
||||||
}
|
}
|
||||||
return &result.StateHeight{
|
return &result.StateHeight{
|
||||||
BlockHeight: height,
|
Local: height,
|
||||||
StateHeight: stateHeight,
|
Validated: stateHeight,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -324,8 +324,8 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
sh, ok := res.(*result.StateHeight)
|
sh, ok := res.(*result.StateHeight)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
|
|
||||||
require.Equal(t, e.chain.BlockHeight(), sh.BlockHeight)
|
require.Equal(t, e.chain.BlockHeight(), sh.Local)
|
||||||
require.Equal(t, uint32(0), sh.StateHeight)
|
require.Equal(t, uint32(0), sh.Validated)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue