package wallet import ( "fmt" "math/big" "github.com/tutus-one/tutus-chain/cli/cmdargs" "github.com/tutus-one/tutus-chain/cli/flags" "github.com/tutus-one/tutus-chain/cli/options" "github.com/tutus-one/tutus-chain/cli/txctx" "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" "github.com/tutus-one/tutus-chain/pkg/core/transaction" "github.com/tutus-one/tutus-chain/pkg/crypto/keys" "github.com/tutus-one/tutus-chain/pkg/rpcclient/gas" "github.com/tutus-one/tutus-chain/pkg/rpcclient/neo" "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep17" "github.com/tutus-one/tutus-chain/pkg/util" "github.com/tutus-one/tutus-chain/pkg/wallet" "github.com/urfave/cli/v2" ) func newValidatorCommands() []*cli.Command { return []*cli.Command{ { Name: "register", Usage: "Register as a new candidate", UsageText: "register -w -r [-s timeout] -a [-g gas] [-e sysgas] [--out file] [--force] [--await]", Action: handleRegister, Flags: append([]cli.Flag{ walletPathFlag, walletConfigFlag, txctx.GasFlag, txctx.SysGasFlag, txctx.OutFlag, txctx.ForceFlag, txctx.AwaitFlag, &flags.AddressFlag{ Name: "address", Aliases: []string{"a"}, Required: true, Usage: "Address to register", }, &cli.BoolFlag{ Name: "useRegisterCall", Usage: "Use a call to registerCandidate method instead of NEP-17 GAS transfer", }, }, options.RPC...), }, { Name: "unregister", Usage: "Unregister self as a candidate", UsageText: "unregister -w -r [-s timeout] -a [-g gas] [-e sysgas] [--out file] [--force] [--await]", Action: handleUnregister, Flags: append([]cli.Flag{ walletPathFlag, walletConfigFlag, txctx.GasFlag, txctx.SysGasFlag, txctx.OutFlag, txctx.ForceFlag, txctx.AwaitFlag, &flags.AddressFlag{ Name: "address", Required: true, Aliases: []string{"a"}, Usage: "Address to unregister", }, }, options.RPC...), }, { Name: "vote", Usage: "Vote for a validator", UsageText: "vote -w -r [-s ] [-g gas] [-e sysgas] -a [-c ] [--out file] [--force] [--await]", Description: `Votes for a validator by calling "vote" method of a NEO native contract. Do not provide candidate argument to perform unvoting. If --await flag is included, the command waits for the transaction to be included in a block before exiting. `, Action: handleVote, Flags: append([]cli.Flag{ walletPathFlag, walletConfigFlag, txctx.GasFlag, txctx.SysGasFlag, txctx.OutFlag, txctx.ForceFlag, txctx.AwaitFlag, &flags.AddressFlag{ Name: "address", Required: true, Aliases: []string{"a"}, Usage: "Address to vote from", }, &cli.StringFlag{ Name: "candidate", Aliases: []string{"c"}, Usage: "Public key of candidate to vote for", }, }, options.RPC...), }, } } func handleRegister(ctx *cli.Context) error { if ctx.Bool("useRegisterCall") { return handleNeoAction(ctx, func(contract *neo.Contract, _ util.Uint160, acc *wallet.Account) (*transaction.Transaction, error) { return contract.RegisterCandidateUnsigned(acc.PublicKey()) }) } return handleGasAction(ctx, func(nc *neo.Contract, gasT *nep17.Token, _ util.Uint160, acc *wallet.Account) (*transaction.Transaction, error) { regPrice, err := nc.GetRegisterPrice() if err != nil { return nil, err } return gasT.TransferUnsigned( acc.ScriptHash(), nativehashes.NeoToken, big.NewInt(regPrice), acc.PublicKey().Bytes(), ) }) } func handleUnregister(ctx *cli.Context) error { return handleNeoAction(ctx, func(contract *neo.Contract, _ util.Uint160, acc *wallet.Account) (*transaction.Transaction, error) { return contract.UnregisterCandidateUnsigned(acc.PublicKey()) }) } func handleNeoAction(ctx *cli.Context, mkTx func(*neo.Contract, util.Uint160, *wallet.Account) (*transaction.Transaction, error)) error { return handleTokenAction(ctx, func(nc *neo.Contract, _ *nep17.Token, addr util.Uint160, acc *wallet.Account) (*transaction.Transaction, error) { return mkTx(nc, addr, acc) }, transaction.CalledByEntry) } func handleGasAction(ctx *cli.Context, mkTx func(*neo.Contract, *nep17.Token, util.Uint160, *wallet.Account) (*transaction.Transaction, error)) error { return handleTokenAction(ctx, mkTx, transaction.Global) } func handleTokenAction( ctx *cli.Context, mkTx func(nc *neo.Contract, gasT *nep17.Token, addr util.Uint160, acc *wallet.Account) (*transaction.Transaction, error), scope transaction.WitnessScope, ) error { if err := cmdargs.EnsureNone(ctx); err != nil { return err } wall, pass, err := readWallet(ctx) if err != nil { return cli.Exit(err, 1) } defer wall.Close() addrFlag := ctx.Generic("address").(*flags.Address) addr := addrFlag.Uint160() acc, err := options.GetUnlockedAccount(wall, addr, pass) if err != nil { return cli.Exit(err, 1) } gctx, cancel := options.GetTimeoutContext(ctx) defer cancel() signers, err := cmdargs.GetSignersAccounts(acc, wall, nil, scope) if err != nil { return cli.Exit(fmt.Errorf("invalid signers: %w", err), 1) } _, act, exitErr := options.GetRPCWithActor(gctx, ctx, signers) if exitErr != nil { return exitErr } neoT := neo.New(act) gasT := gas.New(act) tx, err := mkTx(neoT, gasT, addr, acc) if err != nil { return cli.Exit(err, 1) } return txctx.SignAndSend(ctx, act, acc, tx) } func handleVote(ctx *cli.Context) error { return handleNeoAction(ctx, func(contract *neo.Contract, addr util.Uint160, acc *wallet.Account) (*transaction.Transaction, error) { var ( err error pub *keys.PublicKey ) pubStr := ctx.String("candidate") if pubStr != "" { pub, err = keys.NewPublicKeyFromString(pubStr) if err != nil { return nil, fmt.Errorf("invalid public key: '%s'", pubStr) } } return contract.VoteUnsigned(addr, pub) }) }