mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-26 19:42:23 +00:00
Merge pull request #1286 from nspcc-dev/feature/register
Support (un)registering candidate in CLI
This commit is contained in:
commit
1487365ca8
4 changed files with 200 additions and 113 deletions
|
@ -50,9 +50,9 @@ func signMultisig(ctx *cli.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(fmt.Errorf("invalid address: %w", err), 1)
|
return cli.NewExitError(fmt.Errorf("invalid address: %w", err), 1)
|
||||||
}
|
}
|
||||||
acc := wall.GetAccount(sh)
|
acc, err := getDecryptedAccount(wall, sh)
|
||||||
if acc == nil {
|
if err != nil {
|
||||||
return cli.NewExitError(fmt.Errorf("can't find account for the address: %s", addr), 1)
|
return cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
tx, ok := c.Verifiable.(*transaction.Transaction)
|
tx, ok := c.Verifiable.(*transaction.Transaction)
|
||||||
|
@ -60,13 +60,6 @@ func signMultisig(ctx *cli.Context) error {
|
||||||
return cli.NewExitError("verifiable item is not a transaction", 1)
|
return cli.NewExitError("verifiable item is not a transaction", 1)
|
||||||
}
|
}
|
||||||
printTxInfo(tx)
|
printTxInfo(tx)
|
||||||
fmt.Println("Enter password to unlock wallet and sign the transaction")
|
|
||||||
pass, err := readPassword("Password > ")
|
|
||||||
if err != nil {
|
|
||||||
return cli.NewExitError(err, 1)
|
|
||||||
} else if err := acc.Decrypt(pass); err != nil {
|
|
||||||
return cli.NewExitError(fmt.Errorf("can't unlock an account: %w", err), 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
priv := acc.PrivateKey()
|
priv := acc.PrivateKey()
|
||||||
sign := priv.Sign(tx.GetSignedPart())
|
sign := priv.Sign(tx.GetSignedPart())
|
||||||
|
|
|
@ -343,9 +343,9 @@ func multiTransferNEP5(ctx *cli.Context) error {
|
||||||
|
|
||||||
fromFlag := ctx.Generic("from").(*flags.Address)
|
fromFlag := ctx.Generic("from").(*flags.Address)
|
||||||
from := fromFlag.Uint160()
|
from := fromFlag.Uint160()
|
||||||
acc := wall.GetAccount(from)
|
acc, err := getDecryptedAccount(wall, from)
|
||||||
if acc == nil {
|
if err != nil {
|
||||||
return cli.NewExitError(fmt.Errorf("can't find account for the address: %s", fromFlag), 1)
|
return cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
gctx, cancel := options.GetTimeoutContext(ctx)
|
gctx, cancel := options.GetTimeoutContext(ctx)
|
||||||
|
@ -406,9 +406,9 @@ func transferNEP5(ctx *cli.Context) error {
|
||||||
|
|
||||||
fromFlag := ctx.Generic("from").(*flags.Address)
|
fromFlag := ctx.Generic("from").(*flags.Address)
|
||||||
from := fromFlag.Uint160()
|
from := fromFlag.Uint160()
|
||||||
acc := wall.GetAccount(from)
|
acc, err := getDecryptedAccount(wall, from)
|
||||||
if acc == nil {
|
if err != nil {
|
||||||
return cli.NewExitError(fmt.Errorf("can't find account for the address: %s", fromFlag), 1)
|
return cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
gctx, cancel := options.GetTimeoutContext(ctx)
|
gctx, cancel := options.GetTimeoutContext(ctx)
|
||||||
|
@ -445,12 +445,6 @@ func transferNEP5(ctx *cli.Context) error {
|
||||||
func signAndSendTransfer(ctx *cli.Context, c *client.Client, acc *wallet.Account, recepients []client.TransferTarget) error {
|
func signAndSendTransfer(ctx *cli.Context, c *client.Client, acc *wallet.Account, recepients []client.TransferTarget) error {
|
||||||
gas := flags.Fixed8FromContext(ctx, "gas")
|
gas := flags.Fixed8FromContext(ctx, "gas")
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
tx, err := c.CreateNEP5MultiTransferTx(acc, int64(gas), recepients...)
|
tx, err := c.CreateNEP5MultiTransferTx(acc, int64(gas), recepients...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(err, 1)
|
return cli.NewExitError(err, 1)
|
||||||
|
|
187
cli/wallet/validator.go
Normal file
187
cli/wallet/validator.go
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
package wallet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/cli/flags"
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newValidatorCommands() []cli.Command {
|
||||||
|
return []cli.Command{
|
||||||
|
{
|
||||||
|
Name: "register",
|
||||||
|
Usage: "register as a new candidate",
|
||||||
|
UsageText: "register -w <path> -r <rpc> -a <addr>",
|
||||||
|
Action: handleRegister,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
walletPathFlag,
|
||||||
|
gasFlag,
|
||||||
|
flags.AddressFlag{
|
||||||
|
Name: "address, a",
|
||||||
|
Usage: "Address to register",
|
||||||
|
},
|
||||||
|
}, options.RPC...),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "unregister",
|
||||||
|
Usage: "unregister self as a candidate",
|
||||||
|
UsageText: "unregister -w <path> -r <rpc> -a <addr>",
|
||||||
|
Action: handleUnregister,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
walletPathFlag,
|
||||||
|
gasFlag,
|
||||||
|
flags.AddressFlag{
|
||||||
|
Name: "address, a",
|
||||||
|
Usage: "Address to unregister",
|
||||||
|
},
|
||||||
|
}, options.RPC...),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "vote",
|
||||||
|
Usage: "vote for a validator",
|
||||||
|
UsageText: "vote -w <path> -r <rpc> [-s <timeout>] [-g gas] -a <addr> -c <public key>",
|
||||||
|
Action: handleVote,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
walletPathFlag,
|
||||||
|
gasFlag,
|
||||||
|
flags.AddressFlag{
|
||||||
|
Name: "address, a",
|
||||||
|
Usage: "Address to vote from",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "candidate, c",
|
||||||
|
Usage: "Public key of candidate to vote for",
|
||||||
|
},
|
||||||
|
}, options.RPC...),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleRegister(ctx *cli.Context) error {
|
||||||
|
return handleCandidate(ctx, "registerCandidate")
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleUnregister(ctx *cli.Context) error {
|
||||||
|
return handleCandidate(ctx, "unregisterCandidate")
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleCandidate(ctx *cli.Context, method string) error {
|
||||||
|
wall, err := openWallet(ctx.String("wallet"))
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
addrFlag := ctx.Generic("address").(*flags.Address)
|
||||||
|
addr := addrFlag.Uint160()
|
||||||
|
acc, err := getDecryptedAccount(wall, addr)
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
gctx, cancel := options.GetTimeoutContext(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
c, err := options.GetRPCClient(gctx, ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
gas := flags.Fixed8FromContext(ctx, "gas")
|
||||||
|
w := io.NewBufBinWriter()
|
||||||
|
emit.AppCallWithOperationAndArgs(w.BinWriter, client.NeoContractHash, method, acc.PrivateKey().PublicKey().Bytes())
|
||||||
|
emit.Opcode(w.BinWriter, opcode.ASSERT)
|
||||||
|
tx, err := c.CreateTxFromScript(w.Bytes(), acc, int64(gas))
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
} else 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 handleVote(ctx *cli.Context) error {
|
||||||
|
wall, err := openWallet(ctx.String("wallet"))
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
addrFlag := ctx.Generic("address").(*flags.Address)
|
||||||
|
addr := addrFlag.Uint160()
|
||||||
|
acc, err := getDecryptedAccount(wall, addr)
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
var pub *keys.PublicKey
|
||||||
|
pubStr := ctx.String("candidate")
|
||||||
|
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 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 getDecryptedAccount(wall *wallet.Wallet, addr util.Uint160) (*wallet.Account, error) {
|
||||||
|
acc := wall.GetAccount(addr)
|
||||||
|
if acc == nil {
|
||||||
|
return nil, fmt.Errorf("can't find account for the address: %s", address.Uint160ToString(addr))
|
||||||
|
}
|
||||||
|
|
||||||
|
if pass, err := readPassword("Password > "); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if err := acc.Decrypt(pass); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return acc, nil
|
||||||
|
}
|
|
@ -13,11 +13,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/cli/options"
|
"github.com/nspcc-dev/neo-go/cli/options"
|
||||||
"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/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/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/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
|
@ -191,22 +187,9 @@ func NewCommands() []cli.Command {
|
||||||
Subcommands: newNEP5Commands(),
|
Subcommands: newNEP5Commands(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "vote",
|
Name: "candidate",
|
||||||
Usage: "vote for a validator",
|
Usage: "work with candidates",
|
||||||
UsageText: "vote -w <path> -r <rpc> [-t <timeout>] [-g gas] -a <addr> -k <public key>",
|
Subcommands: newValidatorCommands(),
|
||||||
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...),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
@ -224,16 +207,9 @@ func claimGas(ctx *cli.Context) error {
|
||||||
return cli.NewExitError("address was not provided", 1)
|
return cli.NewExitError("address was not provided", 1)
|
||||||
}
|
}
|
||||||
scriptHash := addrFlag.Uint160()
|
scriptHash := addrFlag.Uint160()
|
||||||
acc := wall.GetAccount(scriptHash)
|
acc, err := getDecryptedAccount(wall, scriptHash)
|
||||||
if acc == nil {
|
|
||||||
return cli.NewExitError(fmt.Errorf("wallet contains no account for '%s'", addrFlag), 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
pass, err := readPassword("Enter password > ")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(err, 1)
|
return cli.NewExitError(err, 1)
|
||||||
} else if err := acc.Decrypt(pass); err != nil {
|
|
||||||
return cli.NewExitError(err, 1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gctx, cancel := options.GetTimeoutContext(ctx)
|
gctx, cancel := options.GetTimeoutContext(ctx)
|
||||||
|
@ -535,69 +511,6 @@ func createWallet(ctx *cli.Context) error {
|
||||||
return nil
|
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) {
|
func readAccountInfo() (string, string, error) {
|
||||||
buf := bufio.NewReader(os.Stdin)
|
buf := bufio.NewReader(os.Stdin)
|
||||||
fmt.Print("Enter the name of the account > ")
|
fmt.Print("Enter the name of the account > ")
|
||||||
|
|
Loading…
Reference in a new issue