Merge pull request #1986 from nspcc-dev/cli/getaccountstate
cli: add `wallet candidate getstate`
This commit is contained in:
commit
0677838f3e
3 changed files with 111 additions and 9 deletions
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -48,7 +49,7 @@ func TestRegisterCandidate(t *testing.T) {
|
||||||
"--wallet", validatorWallet,
|
"--wallet", validatorWallet,
|
||||||
"--address", validatorPriv.Address(),
|
"--address", validatorPriv.Address(),
|
||||||
"--candidate", hex.EncodeToString(validatorPriv.PublicKey().Bytes()))
|
"--candidate", hex.EncodeToString(validatorPriv.PublicKey().Bytes()))
|
||||||
e.checkTxPersisted(t)
|
_, index := e.checkTxPersisted(t)
|
||||||
|
|
||||||
vs, err = e.Chain.GetEnrollments()
|
vs, err = e.Chain.GetEnrollments()
|
||||||
require.Equal(t, 1, len(vs))
|
require.Equal(t, 1, len(vs))
|
||||||
|
@ -56,18 +57,36 @@ 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)
|
||||||
|
|
||||||
|
// check state
|
||||||
|
e.Run(t, "neo-go", "wallet", "candidate", "getstate",
|
||||||
|
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||||
|
"--address", validatorPriv.Address())
|
||||||
|
e.checkNextLine(t, "^\\s*Voted:\\s+"+validatorPriv.Address())
|
||||||
|
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+b.String()+"$")
|
||||||
|
e.checkNextLine(t, "^\\s*Block\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
|
||||||
|
e.checkEOF(t)
|
||||||
|
|
||||||
// unvote
|
// unvote
|
||||||
e.In.WriteString("one\r")
|
e.In.WriteString("one\r")
|
||||||
e.Run(t, "neo-go", "wallet", "candidate", "vote",
|
e.Run(t, "neo-go", "wallet", "candidate", "vote",
|
||||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||||
"--wallet", validatorWallet,
|
"--wallet", validatorWallet,
|
||||||
"--address", validatorPriv.Address())
|
"--address", validatorPriv.Address())
|
||||||
e.checkTxPersisted(t)
|
_, index = e.checkTxPersisted(t)
|
||||||
|
|
||||||
vs, err = e.Chain.GetEnrollments()
|
vs, err = e.Chain.GetEnrollments()
|
||||||
require.Equal(t, 1, len(vs))
|
require.Equal(t, 1, len(vs))
|
||||||
require.Equal(t, validatorPriv.PublicKey(), vs[0].Key)
|
require.Equal(t, validatorPriv.PublicKey(), vs[0].Key)
|
||||||
require.Equal(t, big.NewInt(0), vs[0].Votes)
|
require.Equal(t, big.NewInt(0), vs[0].Votes)
|
||||||
|
|
||||||
|
// check state
|
||||||
|
e.Run(t, "neo-go", "wallet", "candidate", "getstate",
|
||||||
|
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||||
|
"--address", validatorPriv.Address())
|
||||||
|
e.checkNextLine(t, "^\\s*Voted:\\s+"+"null") // no vote.
|
||||||
|
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+b.String()+"$")
|
||||||
|
e.checkNextLine(t, "^\\s*Block\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
|
||||||
|
e.checkEOF(t)
|
||||||
})
|
})
|
||||||
|
|
||||||
// missing address
|
// missing address
|
||||||
|
@ -84,4 +103,7 @@ 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
|
||||||
|
e.RunWithError(t, "neo-go", "wallet", "candidate", "getstate")
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,14 @@ 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"
|
||||||
|
@ -71,6 +74,18 @@ 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...),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,3 +219,54 @@ 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.NEOBalanceState)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
@ -105,7 +107,7 @@ func (s *NEOBalanceState) DecodeBinary(r *io.BinReader) {
|
||||||
if r.Err != nil {
|
if r.Err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
r.Err = s.fromStackItem(si)
|
r.Err = s.FromStackItem(si)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *NEOBalanceState) toStackItem() stackitem.Item {
|
func (s *NEOBalanceState) toStackItem() stackitem.Item {
|
||||||
|
@ -119,21 +121,33 @@ func (s *NEOBalanceState) toStackItem() stackitem.Item {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *NEOBalanceState) fromStackItem(item stackitem.Item) error {
|
// FromStackItem converts stackitem.Item to NEOBalanceState.
|
||||||
structItem := item.Value().([]stackitem.Item)
|
func (s *NEOBalanceState) FromStackItem(item stackitem.Item) error {
|
||||||
s.Balance = *structItem[0].Value().(*big.Int)
|
structItem, ok := item.Value().([]stackitem.Item)
|
||||||
s.BalanceHeight = uint32(structItem[1].Value().(*big.Int).Int64())
|
if !ok || len(structItem) < 3 {
|
||||||
|
return errors.New("invalid stackitem length")
|
||||||
|
}
|
||||||
|
balance, err := structItem[0].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid balance stackitem: %w", err)
|
||||||
|
}
|
||||||
|
s.Balance = *balance
|
||||||
|
h, err := structItem[1].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid heigh stackitem")
|
||||||
|
}
|
||||||
|
s.BalanceHeight = uint32(h.Int64())
|
||||||
if _, ok := structItem[2].(stackitem.Null); ok {
|
if _, ok := structItem[2].(stackitem.Null); ok {
|
||||||
s.VoteTo = nil
|
s.VoteTo = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
bs, err := structItem[2].TryBytes()
|
bs, err := structItem[2].TryBytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("invalid public key stackitem: %w", err)
|
||||||
}
|
}
|
||||||
pub, err := keys.NewPublicKeyFromBytes(bs, elliptic.P256())
|
pub, err := keys.NewPublicKeyFromBytes(bs, elliptic.P256())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("invalid public key bytes: %w", err)
|
||||||
}
|
}
|
||||||
s.VoteTo = pub
|
s.VoteTo = pub
|
||||||
return nil
|
return nil
|
||||||
|
|
Loading…
Reference in a new issue