diff --git a/cli/options/options.go b/cli/options/options.go index de7e5e24d..1cef56915 100644 --- a/cli/options/options.go +++ b/cli/options/options.go @@ -11,18 +11,25 @@ import ( "os" "runtime" "strconv" + "strings" "time" + "github.com/nspcc-dev/neo-go/cli/flags" + "github.com/nspcc-dev/neo-go/cli/input" "github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/rpcclient" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/urfave/cli" "go.uber.org/zap" "go.uber.org/zap/zapcore" + "gopkg.in/yaml.v3" ) // DefaultTimeout is the default timeout used for RPC requests. @@ -97,6 +104,8 @@ var Debug = cli.BoolFlag{ var errNoEndpoint = errors.New("no RPC endpoint specified, use option '--" + RPCEndpointFlag + "' or '-r'") var errInvalidHistoric = errors.New("invalid 'historic' parameter, neither a block number, nor a block/state hash") +var errNoWallet = errors.New("no wallet parameter found, specify it with the '--wallet' or '-w' flag or specify wallet config file with the '--wallet-config' flag") +var errConflictingWalletFlags = errors.New("--wallet flag conflicts with --wallet-config flag, please, provide one of them to specify wallet location") // GetNetwork examines Context's flags and returns the appropriate network. It // defaults to PrivNet if no flags are given. @@ -279,3 +288,108 @@ func HandleLoggingParams(debug bool, cfg config.ApplicationConfiguration) (*zap. log, err := cc.Build() return log, &cc.Level, _winfileSinkCloser, err } + +// GetRPCWithActor returns an RPC client instance and Actor instance for the given context. +func GetRPCWithActor(gctx context.Context, ctx *cli.Context, signers []actor.SignerAccount) (*rpcclient.Client, *actor.Actor, cli.ExitCoder) { + c, err := GetRPCClient(gctx, ctx) + if err != nil { + return nil, nil, err + } + + a, actorErr := actor.New(c, signers) + if actorErr != nil { + c.Close() + return nil, nil, cli.NewExitError(fmt.Errorf("failed to create Actor: %w", actorErr), 1) + } + return c, a, nil +} + +// GetAccFromContext returns account and wallet from context. If address is not set, default address is used. +func GetAccFromContext(ctx *cli.Context) (*wallet.Account, *wallet.Wallet, error) { + var addr util.Uint160 + + wPath := ctx.String("wallet") + walletConfigPath := ctx.String("wallet-config") + if len(wPath) != 0 && len(walletConfigPath) != 0 { + return nil, nil, errConflictingWalletFlags + } + if len(wPath) == 0 && len(walletConfigPath) == 0 { + return nil, nil, errNoWallet + } + var pass *string + if len(walletConfigPath) != 0 { + cfg, err := ReadWalletConfig(walletConfigPath) + if err != nil { + return nil, nil, err + } + wPath = cfg.Path + pass = &cfg.Password + } + + wall, err := wallet.NewWalletFromFile(wPath) + if err != nil { + return nil, nil, err + } + addrFlag := ctx.Generic("address").(*flags.Address) + if addrFlag.IsSet { + addr = addrFlag.Uint160() + } else { + addr = wall.GetChangeAddress() + if addr.Equals(util.Uint160{}) { + return nil, wall, errors.New("can't get default address") + } + } + + acc, err := GetUnlockedAccount(wall, addr, pass) + return acc, wall, err +} + +// GetUnlockedAccount returns account from wallet, address and uses pass to unlock specified account if given. +// If the password is not given, then it is requested from user. +func GetUnlockedAccount(wall *wallet.Wallet, addr util.Uint160, pass *string) (*wallet.Account, error) { + acc := wall.GetAccount(addr) + if acc == nil { + return nil, fmt.Errorf("wallet contains no account for '%s'", address.Uint160ToString(addr)) + } + + if acc.CanSign() || acc.EncryptedWIF == "" { + return acc, nil + } + + if pass == nil { + rawPass, err := input.ReadPassword( + fmt.Sprintf("Enter account %s password > ", address.Uint160ToString(addr))) + if err != nil { + return nil, fmt.Errorf("Error reading password: %w", err) + } + trimmed := strings.TrimRight(string(rawPass), "\n") + pass = &trimmed + } + err := acc.Decrypt(*pass, wall.Scrypt) + if err != nil { + return nil, err + } + return acc, nil +} + +// ReadWalletConfig reads wallet config from the given path. +func ReadWalletConfig(configPath string) (*config.Wallet, error) { + file, err := os.Open(configPath) + if err != nil { + return nil, err + } + defer file.Close() + + configData, err := os.ReadFile(configPath) + if err != nil { + return nil, fmt.Errorf("unable to read wallet config: %w", err) + } + + cfg := &config.Wallet{} + + err = yaml.Unmarshal(configData, &cfg) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal wallet config YAML: %w", err) + } + return cfg, nil +} diff --git a/cli/smartcontract/manifest.go b/cli/smartcontract/manifest.go index 6c4e623a9..e940b333b 100644 --- a/cli/smartcontract/manifest.go +++ b/cli/smartcontract/manifest.go @@ -8,6 +8,7 @@ import ( "github.com/nspcc-dev/neo-go/cli/cmdargs" "github.com/nspcc-dev/neo-go/cli/flags" + "github.com/nspcc-dev/neo-go/cli/options" "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" @@ -37,7 +38,7 @@ func manifestAddGroup(ctx *cli.Context) error { h := state.CreateContractHash(sender, nf.Checksum, m.Name) - gAcc, w, err := GetAccFromContext(ctx) + gAcc, w, err := options.GetAccFromContext(ctx) if err != nil { return cli.NewExitError(fmt.Errorf("can't get account to sign group with: %w", err), 1) } diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go index dc811fb1b..ec3fe68fc 100644 --- a/cli/smartcontract/smart_contract.go +++ b/cli/smartcontract/smart_contract.go @@ -11,14 +11,11 @@ import ( "github.com/nspcc-dev/neo-go/cli/cmdargs" "github.com/nspcc-dev/neo-go/cli/flags" - "github.com/nspcc-dev/neo-go/cli/input" "github.com/nspcc-dev/neo-go/cli/options" "github.com/nspcc-dev/neo-go/cli/txctx" - cliwallet "github.com/nspcc-dev/neo-go/cli/wallet" "github.com/nspcc-dev/neo-go/pkg/compiler" "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/encoding/address" "github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" @@ -39,16 +36,14 @@ import ( const addressFlagName = "address, a" var ( - errNoInput = errors.New("no input file was found, specify an input file with the '--in or -i' flag") - errNoConfFile = errors.New("no config file was found, specify a config file with the '--config' or '-c' flag") - errNoManifestFile = errors.New("no manifest file was found, specify manifest file with '--manifest' or '-m' flag") - errNoMethod = errors.New("no method specified for function invocation command") - errNoWallet = errors.New("no wallet parameter found, specify it with the '--wallet' or '-w' flag or specify wallet config file with the '--wallet-config' flag") - errConflictingWalletFlags = errors.New("--wallet flag conflicts with --wallet-config flag, please, provide one of them to specify wallet location") - errNoScriptHash = errors.New("no smart contract hash was provided, specify one as the first argument") - errNoSmartContractName = errors.New("no name was provided, specify the '--name or -n' flag") - errFileExist = errors.New("A file with given smart-contract name already exists") - addressFlag = flags.AddressFlag{ + errNoInput = errors.New("no input file was found, specify an input file with the '--in or -i' flag") + errNoConfFile = errors.New("no config file was found, specify a config file with the '--config' or '-c' flag") + errNoManifestFile = errors.New("no manifest file was found, specify manifest file with '--manifest' or '-m' flag") + errNoMethod = errors.New("no method specified for function invocation command") + errNoScriptHash = errors.New("no smart contract hash was provided, specify one as the first argument") + errNoSmartContractName = errors.New("no name was provided, specify the '--name or -n' flag") + errFileExist = errors.New("A file with given smart-contract name already exists") + addressFlag = flags.AddressFlag{ Name: addressFlagName, Usage: "address to use as transaction signee (and gas source)", } @@ -570,7 +565,7 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error { w *wallet.Wallet ) if signAndPush { - acc, w, err = GetAccFromContext(ctx) + acc, w, err = options.GetAccFromContext(ctx) if err != nil { return cli.NewExitError(err, 1) } @@ -597,19 +592,14 @@ func invokeWithArgs(ctx *cli.Context, acc *wallet.Account, wall *wallet.Wallet, } gctx, cancel := options.GetTimeoutContext(ctx) defer cancel() - - c, err := options.GetRPCClient(gctx, ctx) - if err != nil { - return err - } if signAndPush { - act, err = actor.New(c, signersAccounts) + _, act, err = options.GetRPCWithActor(gctx, ctx, signersAccounts) if err != nil { - return cli.NewExitError(fmt.Errorf("failed to create RPC actor: %w", err), 1) + return err } inv = &act.Invoker } else { - inv, err = options.GetInvoker(c, ctx, cosigners) + _, inv, err = options.GetRPCWithInvoker(gctx, ctx, cosigners) if err != nil { return err } @@ -746,71 +736,6 @@ func inspect(ctx *cli.Context) error { return nil } -// GetAccFromContext returns account and wallet from context. If address is not set, default address is used. -func GetAccFromContext(ctx *cli.Context) (*wallet.Account, *wallet.Wallet, error) { - var addr util.Uint160 - - wPath := ctx.String("wallet") - walletConfigPath := ctx.String("wallet-config") - if len(wPath) != 0 && len(walletConfigPath) != 0 { - return nil, nil, errConflictingWalletFlags - } - if len(wPath) == 0 && len(walletConfigPath) == 0 { - return nil, nil, errNoWallet - } - var pass *string - if len(walletConfigPath) != 0 { - cfg, err := cliwallet.ReadWalletConfig(walletConfigPath) - if err != nil { - return nil, nil, err - } - wPath = cfg.Path - pass = &cfg.Password - } - - wall, err := wallet.NewWalletFromFile(wPath) - if err != nil { - return nil, nil, err - } - addrFlag := ctx.Generic("address").(*flags.Address) - if addrFlag.IsSet { - addr = addrFlag.Uint160() - } else { - addr = wall.GetChangeAddress() - } - - acc, err := GetUnlockedAccount(wall, addr, pass) - return acc, wall, err -} - -// GetUnlockedAccount returns account from wallet, address and uses pass to unlock specified account if given. -// If the password is not given, then it is requested from user. -func GetUnlockedAccount(wall *wallet.Wallet, addr util.Uint160, pass *string) (*wallet.Account, error) { - acc := wall.GetAccount(addr) - if acc == nil { - return nil, fmt.Errorf("wallet contains no account for '%s'", address.Uint160ToString(addr)) - } - - if acc.CanSign() { - return acc, nil - } - - if pass == nil { - rawPass, err := input.ReadPassword( - fmt.Sprintf("Enter account %s password > ", address.Uint160ToString(addr))) - if err != nil { - return nil, fmt.Errorf("Error reading password: %w", err) - } - trimmed := strings.TrimRight(string(rawPass), "\n") - pass = &trimmed - } - err := acc.Decrypt(*pass, wall.Scrypt) - if err != nil { - return nil, err - } - return acc, nil -} - // contractDeploy deploys contract. func contractDeploy(ctx *cli.Context) error { nefFile, f, err := readNEFFile(ctx.String("in")) @@ -836,7 +761,7 @@ func contractDeploy(ctx *cli.Context) error { appCallParams = append(appCallParams, data[0]) } - acc, w, err := GetAccFromContext(ctx) + acc, w, err := options.GetAccFromContext(ctx) if err != nil { return cli.NewExitError(fmt.Errorf("can't get sender address: %w", err), 1) } diff --git a/cli/util/cancel.go b/cli/util/cancel.go index 4dd5e3ef8..495704e3e 100644 --- a/cli/util/cancel.go +++ b/cli/util/cancel.go @@ -4,9 +4,9 @@ import ( "fmt" "strings" + "github.com/nspcc-dev/neo-go/cli/cmdargs" "github.com/nspcc-dev/neo-go/cli/flags" "github.com/nspcc-dev/neo-go/cli/options" - "github.com/nspcc-dev/neo-go/cli/smartcontract" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" @@ -31,30 +31,30 @@ func cancelTx(ctx *cli.Context) error { gctx, cancel := options.GetTimeoutContext(ctx) defer cancel() - c, err := options.GetRPCClient(gctx, ctx) + acc, w, err := options.GetAccFromContext(ctx) if err != nil { - return cli.NewExitError(fmt.Errorf("failed to create RPC client: %w", err), 1) + return cli.NewExitError(fmt.Errorf("failed to get account from context to sign the conflicting transaction: %w", err), 1) + } + defer w.Close() + + signers, err := cmdargs.GetSignersAccounts(acc, w, nil, transaction.CalledByEntry) + if err != nil { + return cli.NewExitError(fmt.Errorf("invalid signers: %w", err), 1) + } + c, a, exitErr := options.GetRPCWithActor(gctx, ctx, signers) + if exitErr != nil { + return exitErr } mainTx, _ := c.GetRawTransactionVerbose(txHash) if mainTx != nil && !mainTx.Blockhash.Equals(util.Uint256{}) { return cli.NewExitError(fmt.Errorf("transaction %s is already accepted at block %s", txHash, mainTx.Blockhash.StringLE()), 1) } - acc, w, err := smartcontract.GetAccFromContext(ctx) - if err != nil { - return cli.NewExitError(fmt.Errorf("failed to get account from context to sign the conflicting transaction: %w", err), 1) - } - defer w.Close() if mainTx != nil && !mainTx.HasSigner(acc.ScriptHash()) { return cli.NewExitError(fmt.Errorf("account %s is not a signer of the conflicting transaction", acc.Address), 1) } - a, err := actor.NewSimple(c, acc) - if err != nil { - return cli.NewExitError(fmt.Errorf("failed to create Actor: %w", err), 1) - } - resHash, _, err := a.SendTunedRun([]byte{byte(opcode.RET)}, []transaction.Attribute{{Type: transaction.ConflictsT, Value: &transaction.Conflicts{Hash: txHash}}}, func(r *result.Invoke, t *transaction.Transaction) error { err := actor.DefaultCheckerModifier(r, t) if err != nil { diff --git a/cli/wallet/legacy.go b/cli/wallet/legacy.go index c490dbf72..ec7dc4e60 100644 --- a/cli/wallet/legacy.go +++ b/cli/wallet/legacy.go @@ -8,6 +8,7 @@ import ( "errors" "os" + "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/smartcontract" @@ -46,7 +47,7 @@ func newWalletV2FromFile(path string, configPath string) (*walletV2, *string, er } var pass *string if len(configPath) != 0 { - cfg, err := ReadWalletConfig(configPath) + cfg, err := options.ReadWalletConfig(configPath) if err != nil { return nil, nil, err } diff --git a/cli/wallet/multisig.go b/cli/wallet/multisig.go index 2065af51f..337064e77 100644 --- a/cli/wallet/multisig.go +++ b/cli/wallet/multisig.go @@ -21,11 +21,6 @@ func signStoredTransaction(ctx *cli.Context) error { if err := cmdargs.EnsureNone(ctx); err != nil { return err } - wall, pass, err := readWallet(ctx) - if err != nil { - return cli.NewExitError(err, 1) - } - defer wall.Close() pc, err := paramcontext.Read(ctx.String("in")) if err != nil { @@ -35,9 +30,7 @@ func signStoredTransaction(ctx *cli.Context) error { if !addrFlag.IsSet { return cli.NewExitError("address was not provided", 1) } - - var ch = addrFlag.Uint160() - acc, err := getDecryptedAccount(wall, ch, pass) + acc, _, err := options.GetAccFromContext(ctx) if err != nil { return cli.NewExitError(err, 1) } @@ -47,20 +40,13 @@ func signStoredTransaction(ctx *cli.Context) error { return cli.NewExitError("verifiable item is not a transaction", 1) } - signerFound := false - for i := range tx.Signers { - if tx.Signers[i].Account == ch { - signerFound = true - break - } - } - if !signerFound { + if !tx.HasSigner(acc.ScriptHash()) { return cli.NewExitError("tx signers don't contain provided account", 1) } if acc.CanSign() { sign := acc.SignHashable(pc.Network, pc.Verifiable) - if err := pc.AddSignature(ch, acc.Contract, acc.PublicKey(), sign); err != nil { + if err := pc.AddSignature(acc.ScriptHash(), acc.Contract, acc.PublicKey(), sign); err != nil { return cli.NewExitError(fmt.Errorf("can't add signature: %w", err), 1) } } else if rpcNode == "" { diff --git a/cli/wallet/nep17.go b/cli/wallet/nep17.go index bb3173792..3f8337355 100644 --- a/cli/wallet/nep17.go +++ b/cli/wallet/nep17.go @@ -522,7 +522,7 @@ func multiTransferNEP17(ctx *cli.Context) error { if err != nil { return cli.NewExitError(err, 1) } - acc, err := getDecryptedAccount(wall, from, pass) + acc, err := options.GetUnlockedAccount(wall, from, pass) if err != nil { return cli.NewExitError(err, 1) } @@ -530,25 +530,36 @@ func multiTransferNEP17(ctx *cli.Context) error { gctx, cancel := options.GetTimeoutContext(ctx) defer cancel() - c, err := options.GetRPCClient(gctx, ctx) - if err != nil { - return cli.NewExitError(err, 1) - } - if ctx.NArg() == 0 { return cli.NewExitError("empty recipients list", 1) } var ( recipients []transferTarget - cosignersOffset = ctx.NArg() + cosignersSepPos = ctx.NArg() // `--` position. ) - cache := make(map[string]*wallet.Token) for i := 0; i < ctx.NArg(); i++ { arg := ctx.Args().Get(i) if arg == cmdargs.CosignersSeparator { - cosignersOffset = i + 1 + cosignersSepPos = i break } + } + cosigners, extErr := cmdargs.GetSignersFromContext(ctx, cosignersSepPos+1) + if extErr != nil { + return extErr + } + signersAccounts, err := cmdargs.GetSignersAccounts(acc, wall, cosigners, transaction.CalledByEntry) + if err != nil { + return cli.NewExitError(fmt.Errorf("invalid signers: %w", err), 1) + } + c, act, exitErr := options.GetRPCWithActor(gctx, ctx, signersAccounts) + if exitErr != nil { + return exitErr + } + + cache := make(map[string]*wallet.Token) + for i := 0; i < cosignersSepPos; i++ { + arg := ctx.Args().Get(i) ss := strings.SplitN(arg, ":", 3) if len(ss) != 3 { return cli.NewExitError("send format must be '::", 1) @@ -580,19 +591,6 @@ func multiTransferNEP17(ctx *cli.Context) error { }) } - cosigners, extErr := cmdargs.GetSignersFromContext(ctx, cosignersOffset) - if extErr != nil { - return extErr - } - signersAccounts, err := cmdargs.GetSignersAccounts(acc, wall, cosigners, transaction.CalledByEntry) - if err != nil { - return cli.NewExitError(fmt.Errorf("invalid signers: %w", err), 1) - } - act, err := actor.New(c, signersAccounts) - if err != nil { - return cli.NewExitError(fmt.Errorf("failed to create RPC actor: %w", err), 1) - } - tx, err := makeMultiTransferNEP17(act, recipients) if err != nil { return cli.NewExitError(fmt.Errorf("can't make transaction: %w", err), 1) @@ -618,7 +616,7 @@ func transferNEP(ctx *cli.Context, standard string) error { if err != nil { return cli.NewExitError(err, 1) } - acc, err := getDecryptedAccount(wall, from, pass) + acc, err := options.GetUnlockedAccount(wall, from, pass) if err != nil { return cli.NewExitError(err, 1) } @@ -626,9 +624,22 @@ func transferNEP(ctx *cli.Context, standard string) error { gctx, cancel := options.GetTimeoutContext(ctx) defer cancel() - c, err := options.GetRPCClient(gctx, ctx) + cosignersOffset, data, extErr := cmdargs.GetDataFromContext(ctx) + if extErr != nil { + return extErr + } + cosigners, extErr := cmdargs.GetSignersFromContext(ctx, cosignersOffset) + if extErr != nil { + return extErr + } + signersAccounts, err := cmdargs.GetSignersAccounts(acc, wall, cosigners, transaction.CalledByEntry) if err != nil { - return cli.NewExitError(err, 1) + return cli.NewExitError(fmt.Errorf("invalid signers: %w", err), 1) + } + + c, act, exitErr := options.GetRPCWithActor(gctx, ctx, signersAccounts) + if exitErr != nil { + return exitErr } toFlag := ctx.Generic("to").(*flags.Address) @@ -644,24 +655,6 @@ func transferNEP(ctx *cli.Context, standard string) error { } } - cosignersOffset, data, extErr := cmdargs.GetDataFromContext(ctx) - if extErr != nil { - return extErr - } - - cosigners, extErr := cmdargs.GetSignersFromContext(ctx, cosignersOffset) - if extErr != nil { - return extErr - } - signersAccounts, err := cmdargs.GetSignersAccounts(acc, wall, cosigners, transaction.CalledByEntry) - if err != nil { - return cli.NewExitError(fmt.Errorf("invalid signers: %w", err), 1) - } - act, err := actor.New(c, signersAccounts) - if err != nil { - return cli.NewExitError(fmt.Errorf("failed to create RPC actor: %w", err), 1) - } - amountArg := ctx.String("amount") amount, err := fixedn.FromString(amountArg, int(token.Decimals)) // It's OK for NEP-11 transfer to not have amount set. diff --git a/cli/wallet/validator.go b/cli/wallet/validator.go index 1194fa371..930d73cb0 100644 --- a/cli/wallet/validator.go +++ b/cli/wallet/validator.go @@ -5,13 +5,10 @@ import ( "github.com/nspcc-dev/neo-go/cli/cmdargs" "github.com/nspcc-dev/neo-go/cli/flags" - "github.com/nspcc-dev/neo-go/cli/input" "github.com/nspcc-dev/neo-go/cli/options" "github.com/nspcc-dev/neo-go/cli/txctx" "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/encoding/address" - "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" "github.com/nspcc-dev/neo-go/pkg/rpcclient/neo" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/wallet" @@ -111,7 +108,7 @@ func handleNeoAction(ctx *cli.Context, mkTx func(*neo.Contract, util.Uint160, *w return cli.NewExitError("address was not provided", 1) } addr := addrFlag.Uint160() - acc, err := getDecryptedAccount(wall, addr, pass) + acc, err := options.GetUnlockedAccount(wall, addr, pass) if err != nil { return cli.NewExitError(err, 1) } @@ -119,13 +116,13 @@ func handleNeoAction(ctx *cli.Context, mkTx func(*neo.Contract, util.Uint160, *w gctx, cancel := options.GetTimeoutContext(ctx) defer cancel() - c, err := options.GetRPCClient(gctx, ctx) + signers, err := cmdargs.GetSignersAccounts(acc, wall, nil, transaction.CalledByEntry) if err != nil { - return cli.NewExitError(err, 1) + return cli.NewExitError(fmt.Errorf("invalid signers: %w", err), 1) } - act, err := actor.NewSimple(c, acc) - if err != nil { - return cli.NewExitError(fmt.Errorf("RPC actor issue: %w", err), 1) + _, act, exitErr := options.GetRPCWithActor(gctx, ctx, signers) + if exitErr != nil { + return exitErr } contract := neo.New(act) @@ -153,32 +150,3 @@ func handleVote(ctx *cli.Context) error { return contract.VoteUnsigned(addr, pub) }) } - -// getDecryptedAccount tries to get and unlock the specified account if it has a -// key inside (otherwise it's returned as is, without an ability to sign). If -// password is nil, it will be requested via terminal. -func getDecryptedAccount(wall *wallet.Wallet, addr util.Uint160, password *string) (*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)) - } - - // No private key available, nothing to decrypt, but it's still a useful account for many purposes. - if acc.EncryptedWIF == "" { - return acc, nil - } - - if password == nil { - pass, err := input.ReadPassword(EnterPasswordPrompt) - if err != nil { - fmt.Println("Error reading password", err) - return nil, err - } - password = &pass - } - err := acc.Decrypt(*password, wall.Scrypt) - if err != nil { - return nil, err - } - return acc, nil -} diff --git a/cli/wallet/wallet.go b/cli/wallet/wallet.go index f5051efb8..4a10fc96a 100644 --- a/cli/wallet/wallet.go +++ b/cli/wallet/wallet.go @@ -15,7 +15,6 @@ import ( "github.com/nspcc-dev/neo-go/cli/input" "github.com/nspcc-dev/neo-go/cli/options" "github.com/nspcc-dev/neo-go/cli/txctx" - "github.com/nspcc-dev/neo-go/pkg/config" "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/encoding/address" @@ -26,7 +25,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/urfave/cli" - "gopkg.in/yaml.v3" ) const ( @@ -822,7 +820,7 @@ func createWallet(ctx *cli.Context) error { } var pass *string if len(configPath) != 0 { - cfg, err := ReadWalletConfig(configPath) + cfg, err := options.ReadWalletConfig(configPath) if err != nil { return cli.NewExitError(err, 1) } @@ -946,7 +944,7 @@ func getWalletPathAndPass(ctx *cli.Context, canUseWalletConfig bool) (string, *s } var pass *string if len(configPath) != 0 { - cfg, err := ReadWalletConfig(configPath) + cfg, err := options.ReadWalletConfig(configPath) if err != nil { return "", nil, err } @@ -956,27 +954,6 @@ func getWalletPathAndPass(ctx *cli.Context, canUseWalletConfig bool) (string, *s return path, pass, nil } -func ReadWalletConfig(configPath string) (*config.Wallet, error) { - file, err := os.Open(configPath) - if err != nil { - return nil, err - } - defer file.Close() - - configData, err := os.ReadFile(configPath) - if err != nil { - return nil, fmt.Errorf("unable to read wallet config: %w", err) - } - - cfg := &config.Wallet{} - - err = yaml.Unmarshal(configData, &cfg) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal wallet config YAML: %w", err) - } - return cfg, nil -} - func newAccountFromWIF(w io.Writer, wif string, scrypt keys.ScryptParams, label *string, pass *string) (*wallet.Account, error) { var ( phrase, name string