diff --git a/cli/wallet/wallet.go b/cli/wallet/wallet.go index 91ddb6847..725c4d218 100644 --- a/cli/wallet/wallet.go +++ b/cli/wallet/wallet.go @@ -13,7 +13,11 @@ import ( "github.com/nspcc-dev/neo-go/cli/options" "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/io" + "github.com/nspcc-dev/neo-go/pkg/rpc/client" "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/opcode" "github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/urfave/cli" "golang.org/x/crypto/ssh/terminal" @@ -186,6 +190,24 @@ func NewCommands() []cli.Command { Usage: "work with NEP5 contracts", Subcommands: newNEP5Commands(), }, + { + Name: "vote", + Usage: "vote for a validator", + UsageText: "vote -w -r [-t ] [-g gas] -a -k ", + Action: handleVote, + Flags: append([]cli.Flag{ + walletPathFlag, + gasFlag, + flags.AddressFlag{ + Name: "addr, a", + Usage: "Address to vote from", + }, + cli.StringFlag{ + Name: "key, k", + Usage: "Public key of candidate to vote for", + }, + }, options.RPC...), + }, }, }} } @@ -513,6 +535,69 @@ func createWallet(ctx *cli.Context) error { return nil } +func handleVote(ctx *cli.Context) error { + wall, err := openWallet(ctx.String("wallet")) + if err != nil { + return cli.NewExitError(err, 1) + } + + addrFlag := ctx.Generic("addr").(*flags.Address) + addr := addrFlag.Uint160() + acc := wall.GetAccount(addr) + if acc == nil { + return cli.NewExitError(fmt.Errorf("can't find account for the address: %s", addrFlag), 1) + } + + var pub *keys.PublicKey + pubStr := ctx.String("key") + if pubStr != "" { + pub, err = keys.NewPublicKeyFromString(pubStr) + if err != nil { + return cli.NewExitError(fmt.Errorf("invalid public key: '%s'", pubStr), 1) + } + } + + gctx, cancel := options.GetTimeoutContext(ctx) + defer cancel() + + c, err := options.GetRPCClient(gctx, ctx) + if err != nil { + return err + } + + var pubArg interface{} + if pub != nil { + pubArg = pub.Bytes() + } + + gas := flags.Fixed8FromContext(ctx, "gas") + w := io.NewBufBinWriter() + emit.AppCallWithOperationAndArgs(w.BinWriter, client.NeoContractHash, "vote", addr.BytesBE(), pubArg) + emit.Opcode(w.BinWriter, opcode.ASSERT) + + tx, err := c.CreateTxFromScript(w.Bytes(), acc, int64(gas)) + if err != nil { + return cli.NewExitError(err, 1) + } + + if pass, err := readPassword("Password > "); err != nil { + return cli.NewExitError(err, 1) + } else if err := acc.Decrypt(pass); err != nil { + return cli.NewExitError(err, 1) + } + + if err = acc.SignTx(tx); err != nil { + return cli.NewExitError(fmt.Errorf("can't sign tx: %v", err), 1) + } + + res, err := c.SendRawTransaction(tx) + if err != nil { + return cli.NewExitError(err, 1) + } + fmt.Println(res.StringLE()) + return nil +} + func readAccountInfo() (string, string, error) { buf := bufio.NewReader(os.Stdin) fmt.Print("Enter the name of the account > ") diff --git a/pkg/rpc/client/nep5.go b/pkg/rpc/client/nep5.go index 5c3ea9304..5e84c0204 100644 --- a/pkg/rpc/client/nep5.go +++ b/pkg/rpc/client/nep5.go @@ -131,8 +131,15 @@ func (c *Client) CreateNEP5MultiTransferTx(acc *wallet.Account, gas int64, recep recepients[i].Address, recepients[i].Amount) emit.Opcode(w.BinWriter, opcode.ASSERT) } + return c.CreateTxFromScript(w.Bytes(), acc, gas) +} - script := w.Bytes() +// CreateTxFromScript creates transaction and properly sets cosigners and NetworkFee. +func (c *Client) CreateTxFromScript(script []byte, acc *wallet.Account, gas int64) (*transaction.Transaction, error) { + from, err := address.StringToUint160(acc.Address) + if err != nil { + return nil, fmt.Errorf("bad account address: %v", err) + } result, err := c.InvokeScript(script, []transaction.Signer{ { Account: from,