2018-03-02 15:24:09 +00:00
package wallet
import (
2020-03-13 14:03:38 +00:00
"encoding/hex"
2021-12-07 16:36:02 +00:00
"encoding/json"
2018-03-02 15:24:09 +00:00
"errors"
"fmt"
2020-08-28 09:11:19 +00:00
"io"
2022-08-12 12:30:55 +00:00
"math/big"
2021-12-07 16:36:02 +00:00
"os"
2018-03-02 15:24:09 +00:00
"strings"
2022-08-05 10:32:37 +00:00
"github.com/nspcc-dev/neo-go/cli/cmdargs"
2020-03-03 20:09:47 +00:00
"github.com/nspcc-dev/neo-go/cli/flags"
2020-08-31 09:22:09 +00:00
"github.com/nspcc-dev/neo-go/cli/input"
2020-06-17 21:15:13 +00:00
"github.com/nspcc-dev/neo-go/cli/options"
2022-10-06 19:59:47 +00:00
"github.com/nspcc-dev/neo-go/cli/txctx"
2022-10-06 18:52:40 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
2022-08-17 18:55:30 +00:00
"github.com/nspcc-dev/neo-go/pkg/rpcclient/neo"
2021-01-26 15:00:08 +00:00
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
2020-08-27 07:50:55 +00:00
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
2022-10-06 18:52:40 +00:00
"github.com/nspcc-dev/neo-go/pkg/util"
2021-03-05 09:23:25 +00:00
"github.com/nspcc-dev/neo-go/pkg/vm"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/wallet"
2024-07-09 18:24:39 +00:00
"github.com/urfave/cli/v2"
2018-03-02 15:24:09 +00:00
)
2022-04-29 15:22:33 +00:00
const (
// EnterPasswordPrompt is a prompt used to ask the user for a password.
EnterPasswordPrompt = "Enter password > "
// EnterNewPasswordPrompt is a prompt used to ask the user for a password on
// account creation.
EnterNewPasswordPrompt = "Enter new password > "
// EnterOldPasswordPrompt is a prompt used to ask the user for an old password.
EnterOldPasswordPrompt = "Enter old password > "
// ConfirmPasswordPrompt is a prompt used to confirm the password.
ConfirmPasswordPrompt = "Confirm password > "
)
2018-03-02 15:24:09 +00:00
var (
2022-06-23 13:50:21 +00:00
errNoPath = errors . New ( "wallet path is mandatory and should be passed using (--wallet, -w) flags or via wallet config using --wallet-config flag" )
errConflictingWalletFlags = errors . New ( "--wallet flag conflicts with --wallet-config flag, please, provide one of them to specify wallet location" )
errPhraseMismatch = errors . New ( "the entered pass-phrases do not match. Maybe you have misspelled them" )
errNoStdin = errors . New ( "can't read wallet from stdin for this command" )
2018-03-02 15:24:09 +00:00
)
2020-02-20 09:05:55 +00:00
var (
2024-07-09 18:24:39 +00:00
walletPathFlag = & cli . StringFlag {
Name : "wallet" ,
Aliases : [ ] string { "w" } ,
Usage : "Path to the wallet file ('-' to read from stdin); conflicts with --wallet-config flag." ,
2022-06-23 13:50:21 +00:00
}
2024-07-09 18:24:39 +00:00
walletConfigFlag = & cli . StringFlag {
2022-06-23 13:50:21 +00:00
Name : "wallet-config" ,
2022-12-06 17:54:01 +00:00
Usage : "Path to the wallet config file; conflicts with --wallet flag." ,
2020-02-20 09:05:55 +00:00
}
2024-07-09 18:24:39 +00:00
wifFlag = & cli . StringFlag {
2020-02-20 10:48:59 +00:00
Name : "wif" ,
Usage : "WIF to import" ,
}
2024-07-09 18:24:39 +00:00
decryptFlag = & cli . BoolFlag {
Name : "decrypt" ,
Aliases : [ ] string { "d" } ,
Usage : "Decrypt encrypted keys." ,
2020-02-20 13:14:14 +00:00
}
2024-07-09 18:24:39 +00:00
inFlag = & cli . StringFlag {
2024-07-04 22:34:40 +00:00
Name : "in" ,
Required : true ,
Usage : "File with JSON transaction" ,
Action : cmdargs . EnsureNotEmpty ( "in" ) ,
2020-03-02 12:39:02 +00:00
}
2024-07-09 18:24:39 +00:00
fromAddrFlag = & flags . AddressFlag {
2020-03-06 14:22:14 +00:00
Name : "from" ,
Usage : "Address to send an asset from" ,
}
2024-07-09 18:24:39 +00:00
toAddrFlag = & flags . AddressFlag {
2024-07-04 22:34:40 +00:00
Name : "to" ,
Usage : "Address to send an asset to" ,
Required : true ,
2020-03-06 14:22:14 +00:00
}
2020-02-20 09:05:55 +00:00
)
2019-10-19 20:58:45 +00:00
// NewCommands returns 'wallet' command.
2024-07-09 18:24:39 +00:00
func NewCommands ( ) [ ] * cli . Command {
2020-06-17 21:15:13 +00:00
claimFlags := [ ] cli . Flag {
walletPathFlag ,
2022-06-23 13:50:21 +00:00
walletConfigFlag ,
2022-10-06 19:59:47 +00:00
txctx . GasFlag ,
txctx . SysGasFlag ,
txctx . OutFlag ,
txctx . ForceFlag ,
2023-12-28 11:58:38 +00:00
txctx . AwaitFlag ,
2024-07-09 18:24:39 +00:00
& flags . AddressFlag {
2024-07-04 22:34:40 +00:00
Name : "address" ,
Aliases : [ ] string { "a" } ,
Required : true ,
Usage : "Address to claim GAS for" ,
2020-06-17 21:15:13 +00:00
} ,
}
claimFlags = append ( claimFlags , options . RPC ... )
2021-03-03 11:47:36 +00:00
signFlags := [ ] cli . Flag {
walletPathFlag ,
2022-06-23 13:50:21 +00:00
walletConfigFlag ,
2022-10-06 19:59:47 +00:00
txctx . OutFlag ,
2023-12-28 11:58:38 +00:00
txctx . AwaitFlag ,
2021-03-03 11:47:36 +00:00
inFlag ,
2024-07-09 18:24:39 +00:00
& flags . AddressFlag {
Name : "address" ,
Aliases : [ ] string { "a" } ,
Usage : "Address to use" ,
2021-03-03 11:47:36 +00:00
} ,
}
2024-07-04 22:34:40 +00:00
// By default, RPC flag is required. signtx may be called without provided rpc-endpoint.
rpcFlagOriginal , _ := options . RPC [ 0 ] . ( * cli . StringFlag )
rpcFlag := * rpcFlagOriginal
rpcFlag . Required = false
signFlags = append ( signFlags , & rpcFlag )
signFlags = append ( signFlags , options . RPC [ 1 : ] ... )
2024-07-09 18:24:39 +00:00
return [ ] * cli . Command { {
2018-03-02 15:24:09 +00:00
Name : "wallet" ,
2024-07-03 12:35:18 +00:00
Usage : "Create, open and manage a Neo wallet" ,
2024-07-09 18:24:39 +00:00
Subcommands : [ ] * cli . Command {
2020-02-27 13:09:19 +00:00
{
2022-08-05 13:23:50 +00:00
Name : "claim" ,
2024-07-03 12:35:18 +00:00
Usage : "Claim GAS" ,
2023-12-28 11:58:38 +00:00
UsageText : "neo-go wallet claim -w wallet [--wallet-config path] [-g gas] [-e sysgas] -a address -r endpoint [-s timeout] [--out file] [--force] [--await]" ,
2022-08-05 13:23:50 +00:00
Action : claimGas ,
Flags : claimFlags ,
2020-02-27 13:09:19 +00:00
} ,
2018-03-02 15:24:09 +00:00
{
2022-08-05 13:23:50 +00:00
Name : "init" ,
2024-07-03 12:35:18 +00:00
Usage : "Create a new wallet" ,
2022-08-05 13:23:50 +00:00
UsageText : "neo-go wallet init -w wallet [--wallet-config path] [-a]" ,
Action : createWallet ,
2018-03-02 15:24:09 +00:00
Flags : [ ] cli . Flag {
2020-02-20 09:05:55 +00:00
walletPathFlag ,
2022-06-23 13:50:21 +00:00
walletConfigFlag ,
2024-07-09 18:24:39 +00:00
& cli . BoolFlag {
Name : "account" ,
Aliases : [ ] string { "a" } ,
Usage : "Create a new account" ,
2018-03-02 15:24:09 +00:00
} ,
} ,
} ,
2022-01-15 00:46:31 +00:00
{
2022-08-05 13:23:50 +00:00
Name : "change-password" ,
2024-07-03 12:35:18 +00:00
Usage : "Change password for accounts" ,
2024-07-04 22:34:40 +00:00
UsageText : "neo-go wallet change-password -w wallet [-a address]" ,
2022-08-05 13:23:50 +00:00
Action : changePassword ,
2022-01-15 00:46:31 +00:00
Flags : [ ] cli . Flag {
walletPathFlag ,
2024-07-09 18:24:39 +00:00
& flags . AddressFlag {
Name : "address" ,
Aliases : [ ] string { "a" } ,
Usage : "Address to change password for" ,
2022-01-15 00:46:31 +00:00
} ,
} ,
} ,
2020-06-16 11:08:47 +00:00
{
2022-08-05 13:23:50 +00:00
Name : "convert" ,
2024-07-03 12:35:18 +00:00
Usage : "Convert addresses from existing Neo Legacy NEP6-wallet to Neo N3 format" ,
2022-08-05 13:23:50 +00:00
UsageText : "neo-go wallet convert -w legacywallet [--wallet-config path] -o n3wallet" ,
Action : convertWallet ,
2020-06-16 11:08:47 +00:00
Flags : [ ] cli . Flag {
walletPathFlag ,
2022-06-23 13:50:21 +00:00
walletConfigFlag ,
2024-07-09 18:24:39 +00:00
& cli . StringFlag {
2024-07-04 22:34:40 +00:00
Name : "out" ,
Aliases : [ ] string { "o" } ,
Required : true ,
Usage : "Where to write converted wallet" ,
Action : cmdargs . EnsureNotEmpty ( "out" ) ,
2020-06-16 11:08:47 +00:00
} ,
} ,
} ,
2020-02-20 13:39:15 +00:00
{
2022-08-05 13:23:50 +00:00
Name : "create" ,
2024-07-03 12:35:18 +00:00
Usage : "Add an account to the existing wallet" ,
2022-08-05 13:23:50 +00:00
UsageText : "neo-go wallet create -w wallet [--wallet-config path]" ,
Action : addAccount ,
2020-02-20 13:39:15 +00:00
Flags : [ ] cli . Flag {
walletPathFlag ,
2022-06-23 13:50:21 +00:00
walletConfigFlag ,
2020-02-20 13:39:15 +00:00
} ,
} ,
2018-03-02 15:24:09 +00:00
{
2022-08-05 13:23:50 +00:00
Name : "dump" ,
2024-07-03 12:35:18 +00:00
Usage : "Check and dump an existing Neo wallet" ,
2022-08-05 13:23:50 +00:00
UsageText : "neo-go wallet dump -w wallet [--wallet-config path] [-d]" ,
2022-09-01 18:03:53 +00:00
Description : ` Prints the given wallet ( via - w option or via wallet configuration file ) in JSON
format to the standard output . If - d is given , private keys are unencrypted and
displayed in clear text on the console ! Be very careful with this option and
don ' t use it unless you know what you ' re doing .
` ,
Action : dumpWallet ,
2018-03-02 15:24:09 +00:00
Flags : [ ] cli . Flag {
2020-02-20 09:05:55 +00:00
walletPathFlag ,
2022-06-23 13:50:21 +00:00
walletConfigFlag ,
2020-02-20 13:14:14 +00:00
decryptFlag ,
} ,
} ,
2021-03-05 09:23:25 +00:00
{
2022-08-05 13:23:50 +00:00
Name : "dump-keys" ,
2024-07-03 12:35:18 +00:00
Usage : "Dump public keys for account" ,
2022-08-05 13:23:50 +00:00
UsageText : "neo-go wallet dump-keys -w wallet [--wallet-config path] [-a address]" ,
Action : dumpKeys ,
2021-03-05 09:23:25 +00:00
Flags : [ ] cli . Flag {
walletPathFlag ,
2022-06-23 13:50:21 +00:00
walletConfigFlag ,
2024-07-09 18:24:39 +00:00
& flags . AddressFlag {
Name : "address" ,
Aliases : [ ] string { "a" } ,
Usage : "Address to print public keys for" ,
2021-03-05 09:23:25 +00:00
} ,
} ,
} ,
2020-02-20 13:14:14 +00:00
{
Name : "export" ,
2024-07-03 12:35:18 +00:00
Usage : "Export keys for address" ,
2022-08-05 15:27:24 +00:00
UsageText : "export -w wallet [--wallet-config path] [--decrypt] [<address>]" ,
2022-09-01 18:03:53 +00:00
Description : ` Prints the key for the given account to the standard output . It uses NEP - 2
encrypted format by default ( the way NEP - 6 wallets store it ) or WIF format if
- d option is given . In the latter case the key can be displayed in clear text
on the console , so be extremely careful with this option and don ' t use unless
you really need it and know what you ' re doing .
` ,
Action : exportKeys ,
2020-02-20 13:14:14 +00:00
Flags : [ ] cli . Flag {
walletPathFlag ,
2022-06-23 13:50:21 +00:00
walletConfigFlag ,
2020-02-20 13:14:14 +00:00
decryptFlag ,
2018-03-02 15:24:09 +00:00
} ,
} ,
2020-02-20 10:48:59 +00:00
{
2021-04-19 14:38:13 +00:00
Name : "import" ,
2024-07-03 12:35:18 +00:00
Usage : "Import WIF of a standard signature contract" ,
2022-08-05 15:27:24 +00:00
UsageText : "import -w wallet [--wallet-config path] --wif <wif> [--name <account_name>]" ,
2021-04-19 14:38:13 +00:00
Action : importWallet ,
2020-02-20 10:48:59 +00:00
Flags : [ ] cli . Flag {
walletPathFlag ,
2022-06-23 13:50:21 +00:00
walletConfigFlag ,
2020-02-20 10:48:59 +00:00
wifFlag ,
2024-07-09 18:24:39 +00:00
& cli . StringFlag {
Name : "name" ,
Aliases : [ ] string { "n" } ,
Usage : "Optional account name" ,
2020-02-20 10:48:59 +00:00
} ,
2024-07-09 18:24:39 +00:00
& cli . StringFlag {
2020-03-13 14:03:38 +00:00
Name : "contract" ,
Usage : "Verification script for custom contracts" ,
} ,
2020-02-20 10:48:59 +00:00
} ,
} ,
2020-02-20 12:50:17 +00:00
{
Name : "import-multisig" ,
2024-07-03 12:35:18 +00:00
Usage : "Import multisig contract" ,
2024-01-18 20:47:40 +00:00
UsageText : "import-multisig -w wallet [--wallet-config path] [--wif <wif>] [--name <account_name>] --min <m>" +
2020-02-20 12:50:17 +00:00
" [<pubkey1> [<pubkey2> [...]]]" ,
2024-01-18 20:47:40 +00:00
Description : ` Imports a standard multisignature contract with "m out of n" signatures required where "m" is
specified by -- min flag and "n" is the length of provided set of public keys . If
-- wif flag is provided , it ' s used to create an account with the given name ( or
without a name if -- name flag is not provided ) . Otherwise , the command tries to
find an account with one of the given public keys and convert it to multisig . If
no suitable account is found and no -- wif flag is specified , an error is returned .
` ,
2020-02-20 12:50:17 +00:00
Action : importMultisig ,
Flags : [ ] cli . Flag {
walletPathFlag ,
2022-06-23 13:50:21 +00:00
walletConfigFlag ,
2020-02-20 12:50:17 +00:00
wifFlag ,
2024-07-09 18:24:39 +00:00
& cli . StringFlag {
Name : "name" ,
Aliases : [ ] string { "n" } ,
Usage : "Optional account name" ,
2020-02-20 12:50:17 +00:00
} ,
2024-07-09 18:24:39 +00:00
& cli . IntFlag {
Name : "min" ,
Aliases : [ ] string { "m" } ,
Usage : "Minimal number of signatures" ,
2020-02-20 12:50:17 +00:00
} ,
} ,
} ,
2020-08-27 07:50:55 +00:00
{
Name : "import-deployed" ,
2024-07-03 12:35:18 +00:00
Usage : "Import deployed contract" ,
2024-07-04 22:34:40 +00:00
UsageText : "import-deployed -w wallet [--wallet-config path] --wif <wif> --contract <hash> --rpc-endpoint <endpoint> [-s <timeout>] [--name <account_name>]" ,
2020-08-27 07:50:55 +00:00
Action : importDeployed ,
Flags : append ( [ ] cli . Flag {
walletPathFlag ,
2022-06-23 13:50:21 +00:00
walletConfigFlag ,
2020-08-27 07:50:55 +00:00
wifFlag ,
2024-07-09 18:24:39 +00:00
& cli . StringFlag {
Name : "name" ,
Aliases : [ ] string { "n" } ,
Usage : "Optional account name" ,
2021-04-19 14:46:27 +00:00
} ,
2024-07-09 18:24:39 +00:00
& flags . AddressFlag {
2024-07-04 22:34:40 +00:00
Name : "contract" ,
Aliases : [ ] string { "c" } ,
Required : true ,
Usage : "Contract hash or address" ,
2020-08-27 07:50:55 +00:00
} ,
} , options . RPC ... ) ,
} ,
2020-03-13 13:24:33 +00:00
{
Name : "remove" ,
2024-07-03 12:35:18 +00:00
Usage : "Remove an account from the wallet" ,
2022-08-05 15:27:24 +00:00
UsageText : "remove -w wallet [--wallet-config path] [--force] --address <addr>" ,
2020-03-13 13:24:33 +00:00
Action : removeAccount ,
Flags : [ ] cli . Flag {
walletPathFlag ,
2022-06-23 13:50:21 +00:00
walletConfigFlag ,
2022-10-06 19:59:47 +00:00
txctx . ForceFlag ,
2024-07-09 18:24:39 +00:00
& flags . AddressFlag {
2024-07-04 22:34:40 +00:00
Name : "address" ,
Aliases : [ ] string { "a" } ,
Required : true ,
Usage : "Account address or hash in LE form to be removed" ,
2021-04-19 13:45:40 +00:00
} ,
2020-03-13 13:24:33 +00:00
} ,
} ,
2020-03-02 12:39:02 +00:00
{
2021-03-03 11:47:36 +00:00
Name : "sign" ,
2024-07-03 12:35:18 +00:00
Usage : "Cosign transaction with multisig/contract/additional account" ,
2023-12-28 11:58:38 +00:00
UsageText : "sign -w wallet [--wallet-config path] --address <address> --in <file.in> [--out <file.out>] [-r <endpoint>] [--await]" ,
2022-08-31 12:43:30 +00:00
Description : ` Signs the given ( in file . in ) context ( which must be a transaction
signing context ) for the given address using the given wallet . This command can
output the resulting JSON ( with additional signature added ) right to the console
( if no file . out and no RPC endpoint specified ) or into a file ( which can be the
same as input one ) . If an RPC endpoint is given it ' ll also try to construct a
2023-12-28 11:58:38 +00:00
complete transaction and send it via RPC ( printing its hash if everything is OK ) .
If the -- await ( with a given RPC endpoint ) flag is included , the command waits
for the transaction to be included in a block before exiting .
2022-08-31 12:43:30 +00:00
` ,
Action : signStoredTransaction ,
Flags : signFlags ,
2020-03-02 12:39:02 +00:00
} ,
2022-08-31 18:12:08 +00:00
{
Name : "strip-keys" ,
2024-07-03 12:35:18 +00:00
Usage : "Remove private keys for all accounts" ,
2022-08-31 18:12:08 +00:00
UsageText : "neo-go wallet strip-keys -w wallet [--wallet-config path] [--force]" ,
Description : ` Removes private keys for all accounts from the given wallet . Notice ,
this is a very dangerous action ( you can lose keys if you don ' t have a wallet
backup ) that should not be performed unless you know what you ' re doing . It ' s
mostly useful for creation of special wallets that can be used to create
transactions , but can ' t be used to sign them ( offline signing ) .
` ,
Action : stripKeys ,
Flags : [ ] cli . Flag {
walletPathFlag ,
walletConfigFlag ,
2022-10-06 19:59:47 +00:00
txctx . ForceFlag ,
2022-08-31 18:12:08 +00:00
} ,
} ,
2020-03-06 13:23:41 +00:00
{
2020-11-24 08:14:25 +00:00
Name : "nep17" ,
2024-07-03 12:35:18 +00:00
Usage : "Work with NEP-17 contracts" ,
2020-11-24 08:14:25 +00:00
Subcommands : newNEP17Commands ( ) ,
2020-03-06 13:23:41 +00:00
} ,
2021-04-22 15:21:18 +00:00
{
Name : "nep11" ,
2024-07-03 12:35:18 +00:00
Usage : "Work with NEP-11 contracts" ,
2021-04-22 15:21:18 +00:00
Subcommands : newNEP11Commands ( ) ,
} ,
2020-08-04 09:35:04 +00:00
{
2020-08-07 09:18:38 +00:00
Name : "candidate" ,
2024-07-03 12:35:18 +00:00
Usage : "Work with candidates" ,
2020-08-07 09:18:38 +00:00
Subcommands : newValidatorCommands ( ) ,
2020-08-04 09:35:04 +00:00
} ,
2018-03-02 15:24:09 +00:00
} ,
2019-10-19 20:58:45 +00:00
} }
2018-03-02 15:24:09 +00:00
}
2020-02-27 13:09:19 +00:00
func claimGas ( ctx * cli . Context ) error {
2022-10-06 18:52:40 +00:00
return handleNeoAction ( ctx , func ( contract * neo . Contract , shash util . Uint160 , _ * wallet . Account ) ( * transaction . Transaction , error ) {
return contract . TransferUnsigned ( shash , shash , big . NewInt ( 0 ) , nil )
} )
2020-02-27 13:09:19 +00:00
}
2022-01-15 00:46:31 +00:00
func changePassword ( ctx * cli . Context ) error {
2022-08-05 10:32:37 +00:00
if err := cmdargs . EnsureNone ( ctx ) ; err != nil {
return err
}
2022-07-04 10:27:45 +00:00
wall , _ , err := openWallet ( ctx , false )
2022-01-15 00:46:31 +00:00
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2022-01-15 00:46:31 +00:00
}
2022-09-01 18:44:49 +00:00
defer wall . Close ( )
2022-04-29 15:34:42 +00:00
if len ( wall . Accounts ) == 0 {
2024-07-09 18:24:39 +00:00
return cli . Exit ( "wallet has no accounts" , 1 )
2022-04-29 15:34:42 +00:00
}
2022-01-15 00:46:31 +00:00
addrFlag := ctx . Generic ( "address" ) . ( * flags . Address )
if addrFlag . IsSet {
// Check for account presence first before asking for password.
acc := wall . GetAccount ( addrFlag . Uint160 ( ) )
if acc == nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( "account is missing" , 1 )
2022-01-15 00:46:31 +00:00
}
}
2022-04-29 15:22:33 +00:00
oldPass , err := input . ReadPassword ( EnterOldPasswordPrompt )
2022-01-15 00:46:31 +00:00
if err != nil {
2024-07-12 09:35:10 +00:00
return cli . Exit ( fmt . Errorf ( "error reading old password: %w" , err ) , 1 )
2022-01-15 00:46:31 +00:00
}
for i := range wall . Accounts {
if addrFlag . IsSet && wall . Accounts [ i ] . Address != addrFlag . String ( ) {
continue
}
err := wall . Accounts [ i ] . Decrypt ( oldPass , wall . Scrypt )
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( fmt . Errorf ( "unable to decrypt account %s: %w" , wall . Accounts [ i ] . Address , err ) , 1 )
2022-01-15 00:46:31 +00:00
}
}
pass , err := readNewPassword ( )
if err != nil {
2024-07-12 09:35:10 +00:00
return cli . Exit ( fmt . Errorf ( "error reading new password: %w" , err ) , 1 )
2022-01-15 00:46:31 +00:00
}
for i := range wall . Accounts {
if addrFlag . IsSet && wall . Accounts [ i ] . Address != addrFlag . String ( ) {
continue
}
err := wall . Accounts [ i ] . Encrypt ( pass , wall . Scrypt )
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2022-01-15 00:46:31 +00:00
}
}
err = wall . Save ( )
if err != nil {
2024-07-12 09:35:10 +00:00
return cli . Exit ( fmt . Errorf ( "error saving the wallet: %w" , err ) , 1 )
2022-01-15 00:46:31 +00:00
}
return nil
}
2020-06-16 11:08:47 +00:00
func convertWallet ( ctx * cli . Context ) error {
2022-08-05 10:32:37 +00:00
if err := cmdargs . EnsureNone ( ctx ) ; err != nil {
return err
}
2022-06-23 13:50:21 +00:00
wall , pass , err := newWalletV2FromFile ( ctx . String ( "wallet" ) , ctx . String ( "wallet-config" ) )
2020-06-16 11:08:47 +00:00
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2020-06-16 11:08:47 +00:00
}
2022-01-31 13:26:23 +00:00
out := ctx . String ( "out" )
newWallet , err := wallet . NewWallet ( out )
2020-06-16 11:08:47 +00:00
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2020-06-16 11:08:47 +00:00
}
2021-06-04 11:27:22 +00:00
newWallet . Scrypt = wall . Scrypt
2020-06-16 11:08:47 +00:00
for _ , acc := range wall . Accounts {
2022-06-23 13:50:21 +00:00
if len ( wall . Accounts ) != 1 || pass == nil {
password , err := input . ReadPassword ( fmt . Sprintf ( "Enter password for account %s (label '%s') > " , acc . Address , acc . Label ) )
if err != nil {
2024-07-12 09:35:10 +00:00
return cli . Exit ( fmt . Errorf ( "error reading password: %w" , err ) , 1 )
2022-06-23 13:50:21 +00:00
}
pass = & password
2020-06-16 11:08:47 +00:00
}
2022-06-23 13:50:21 +00:00
newAcc , err := acc . convert ( * pass , wall . Scrypt )
2020-06-16 11:08:47 +00:00
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2020-06-16 11:08:47 +00:00
}
newWallet . AddAccount ( newAcc )
}
if err := newWallet . Save ( ) ; err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2020-06-16 11:08:47 +00:00
}
return nil
}
2020-02-20 13:39:15 +00:00
func addAccount ( ctx * cli . Context ) error {
2022-08-05 10:32:37 +00:00
if err := cmdargs . EnsureNone ( ctx ) ; err != nil {
return err
}
2022-07-04 10:27:45 +00:00
wall , pass , err := openWallet ( ctx , true )
2020-02-20 13:39:15 +00:00
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2020-02-20 13:39:15 +00:00
}
2022-09-01 18:44:49 +00:00
defer wall . Close ( )
2020-02-20 13:39:15 +00:00
2022-06-23 13:50:21 +00:00
if err := createAccount ( wall , pass ) ; err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2020-02-20 13:39:15 +00:00
}
return nil
}
2020-02-20 13:14:14 +00:00
func exportKeys ( ctx * cli . Context ) error {
2022-06-23 13:50:21 +00:00
wall , pass , err := readWallet ( ctx )
2020-02-20 13:14:14 +00:00
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2020-02-20 13:14:14 +00:00
}
2022-09-01 18:44:49 +00:00
defer wall . Close ( )
2020-02-20 13:14:14 +00:00
var addr string
decrypt := ctx . Bool ( "decrypt" )
if ctx . NArg ( ) == 0 && decrypt {
2024-07-09 18:24:39 +00:00
return cli . Exit ( errors . New ( "address must be provided if '--decrypt' flag is used" ) , 1 )
2020-02-20 13:14:14 +00:00
} else if ctx . NArg ( ) > 0 {
// check address format just to catch possible typos
addr = ctx . Args ( ) . First ( )
_ , err := address . StringToUint160 ( addr )
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( fmt . Errorf ( "can't parse address: %w" , err ) , 1 )
2020-02-20 13:14:14 +00:00
}
}
var wifs [ ] string
loop :
for _ , a := range wall . Accounts {
if addr != "" && a . Address != addr {
continue
}
for i := range wifs {
if a . EncryptedWIF == wifs [ i ] {
continue loop
}
}
wifs = append ( wifs , a . EncryptedWIF )
}
for _ , wif := range wifs {
if decrypt {
2022-06-23 13:50:21 +00:00
if pass == nil {
password , err := input . ReadPassword ( EnterPasswordPrompt )
if err != nil {
2024-07-12 09:35:10 +00:00
return cli . Exit ( fmt . Errorf ( "error reading password: %w" , err ) , 1 )
2022-06-23 13:50:21 +00:00
}
pass = & password
2020-02-20 13:14:14 +00:00
}
2022-06-23 13:50:21 +00:00
pk , err := keys . NEP2Decrypt ( wif , * pass , wall . Scrypt )
2020-02-20 13:14:14 +00:00
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2020-02-20 13:14:14 +00:00
}
wif = pk . WIF ( )
}
2020-08-28 09:11:19 +00:00
fmt . Fprintln ( ctx . App . Writer , wif )
2020-02-20 13:14:14 +00:00
}
return nil
}
2020-02-20 12:50:17 +00:00
func importMultisig ( ctx * cli . Context ) error {
2024-01-18 20:47:40 +00:00
var (
label * string
acc * wallet . Account
accPub * keys . PublicKey
)
2023-01-25 06:34:17 +00:00
wall , pass , err := openWallet ( ctx , true )
2020-02-20 12:50:17 +00:00
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2020-02-20 12:50:17 +00:00
}
2022-09-01 18:44:49 +00:00
defer wall . Close ( )
2020-02-20 12:50:17 +00:00
m := ctx . Int ( "min" )
if ctx . NArg ( ) < m {
2024-07-09 18:24:39 +00:00
return cli . Exit ( errors . New ( "insufficient number of public keys" ) , 1 )
2020-02-20 12:50:17 +00:00
}
2024-07-09 18:24:39 +00:00
args := ctx . Args ( ) . Slice ( )
2020-02-20 12:50:17 +00:00
pubs := make ( [ ] * keys . PublicKey , len ( args ) )
for i := range args {
pubs [ i ] , err = keys . NewPublicKeyFromString ( args [ i ] )
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( fmt . Errorf ( "can't decode public key %d: %w" , i , err ) , 1 )
2020-02-20 12:50:17 +00:00
}
}
2023-01-25 06:34:17 +00:00
if ctx . IsSet ( "name" ) {
l := ctx . String ( "name" )
label = & l
}
2024-01-18 20:47:40 +00:00
loop :
for _ , pub := range pubs {
for _ , wallAcc := range wall . Accounts {
if wallAcc . ScriptHash ( ) . Equals ( pub . GetScriptHash ( ) ) {
if acc != nil {
// Multiple matching accounts found, fallback to WIF-based conversion.
acc = nil
break loop
}
acc = new ( wallet . Account )
* acc = * wallAcc
accPub = pub
}
}
}
if acc != nil {
err = acc . ConvertMultisigEncrypted ( accPub , m , pubs )
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2024-01-18 20:47:40 +00:00
}
if label != nil {
acc . Label = * label
}
if err := addAccountAndSave ( wall , acc ) ; err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2024-01-18 20:47:40 +00:00
}
return nil
}
if ! ctx . IsSet ( "wif" ) {
2024-07-09 18:24:39 +00:00
return cli . Exit ( errors . New ( "none of the provided public keys correspond to an existing key in the wallet or multiple matching accounts found in the wallet, and no WIF is provided" ) , 1 )
2024-01-18 20:47:40 +00:00
}
acc , err = newAccountFromWIF ( ctx . App . Writer , ctx . String ( "wif" ) , wall . Scrypt , label , pass )
2020-02-20 12:50:17 +00:00
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2020-02-20 12:50:17 +00:00
}
if err := acc . ConvertMultisig ( m , pubs ) ; err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2020-02-20 12:50:17 +00:00
}
if err := addAccountAndSave ( wall , acc ) ; err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2020-02-20 12:50:17 +00:00
}
return nil
}
2020-08-27 07:50:55 +00:00
func importDeployed ( ctx * cli . Context ) error {
2022-08-05 10:32:37 +00:00
if err := cmdargs . EnsureNone ( ctx ) ; err != nil {
return err
}
2023-01-25 06:34:17 +00:00
wall , pass , err := openWallet ( ctx , true )
2020-02-20 10:48:59 +00:00
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2020-02-20 10:48:59 +00:00
}
2022-09-01 18:44:49 +00:00
defer wall . Close ( )
2020-02-20 10:48:59 +00:00
2021-04-16 09:23:46 +00:00
rawHash := ctx . Generic ( "contract" ) . ( * flags . Address )
2020-08-27 07:50:55 +00:00
2023-01-25 06:34:17 +00:00
var label * string
if ctx . IsSet ( "name" ) {
l := ctx . String ( "name" )
label = & l
}
acc , err := newAccountFromWIF ( ctx . App . Writer , ctx . String ( "wif" ) , wall . Scrypt , label , pass )
2020-08-27 07:50:55 +00:00
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2020-08-27 07:50:55 +00:00
}
gctx , cancel := options . GetTimeoutContext ( ctx )
defer cancel ( )
c , err := options . GetRPCClient ( gctx , ctx )
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2020-08-27 07:50:55 +00:00
}
2021-04-16 09:23:46 +00:00
cs , err := c . GetContractStateByHash ( rawHash . Uint160 ( ) )
2020-08-27 07:50:55 +00:00
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( fmt . Errorf ( "can't fetch contract info: %w" , err ) , 1 )
2020-08-27 07:50:55 +00:00
}
2021-01-26 14:37:34 +00:00
md := cs . Manifest . ABI . GetMethod ( manifest . MethodVerify , - 1 )
2021-01-26 15:00:08 +00:00
if md == nil || md . ReturnType != smartcontract . BoolType {
2024-07-09 18:24:39 +00:00
return cli . Exit ( "contract has no `verify` method with boolean return" , 1 )
2020-08-27 07:50:55 +00:00
}
2020-11-18 20:10:48 +00:00
acc . Address = address . Uint160ToString ( cs . Hash )
2024-05-31 15:45:07 +00:00
// Explicitly overwrite single signature script of the provided WIF since the contract is known to be deployed.
acc . Contract . Script = nil
2020-09-02 09:20:42 +00:00
acc . Contract . Parameters = acc . Contract . Parameters [ : 0 ]
2020-08-27 07:50:55 +00:00
for _ , p := range md . Parameters {
acc . Contract . Parameters = append ( acc . Contract . Parameters , wallet . ContractParam {
Name : p . Name ,
Type : p . Type ,
} )
}
acc . Contract . Deployed = true
if err := addAccountAndSave ( wall , acc ) ; err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2020-08-27 07:50:55 +00:00
}
return nil
}
func importWallet ( ctx * cli . Context ) error {
2022-08-05 10:32:37 +00:00
if err := cmdargs . EnsureNone ( ctx ) ; err != nil {
return err
}
2023-01-25 06:34:17 +00:00
wall , pass , err := openWallet ( ctx , true )
2020-08-27 07:50:55 +00:00
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2020-08-27 07:50:55 +00:00
}
2022-09-01 18:44:49 +00:00
defer wall . Close ( )
2020-08-27 07:50:55 +00:00
2023-01-25 06:34:17 +00:00
var label * string
if ctx . IsSet ( "name" ) {
l := ctx . String ( "name" )
label = & l
}
acc , err := newAccountFromWIF ( ctx . App . Writer , ctx . String ( "wif" ) , wall . Scrypt , label , pass )
2020-02-20 10:48:59 +00:00
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2020-02-20 10:48:59 +00:00
}
2020-03-13 14:03:38 +00:00
if ctrFlag := ctx . String ( "contract" ) ; ctrFlag != "" {
ctr , err := hex . DecodeString ( ctrFlag )
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( "invalid contract" , 1 )
2020-03-13 14:03:38 +00:00
}
acc . Contract . Script = ctr
}
2020-02-20 10:48:59 +00:00
if err := addAccountAndSave ( wall , acc ) ; err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2020-02-20 10:48:59 +00:00
}
return nil
}
2020-03-13 13:24:33 +00:00
func removeAccount ( ctx * cli . Context ) error {
2022-08-05 10:32:37 +00:00
if err := cmdargs . EnsureNone ( ctx ) ; err != nil {
return err
}
2022-07-04 10:27:45 +00:00
wall , _ , err := openWallet ( ctx , true )
2020-03-13 13:24:33 +00:00
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2020-03-13 13:24:33 +00:00
}
2022-09-01 18:44:49 +00:00
defer wall . Close ( )
2020-03-13 13:24:33 +00:00
2021-04-19 13:45:40 +00:00
addr := ctx . Generic ( "address" ) . ( * flags . Address )
acc := wall . GetAccount ( addr . Uint160 ( ) )
2020-03-13 13:24:33 +00:00
if acc == nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( "account wasn't found" , 1 )
2020-03-13 13:24:33 +00:00
}
if ! ctx . Bool ( "force" ) {
2021-04-19 13:45:40 +00:00
fmt . Fprintf ( ctx . App . Writer , "Account %s will be removed. This action is irreversible.\n" , addr . Uint160 ( ) )
2020-08-28 09:11:19 +00:00
if ok := askForConsent ( ctx . App . Writer ) ; ! ok {
2020-03-13 13:24:33 +00:00
return nil
}
}
if err := wall . RemoveAccount ( acc . Address ) ; err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( fmt . Errorf ( "error on remove: %w" , err ) , 1 )
2022-01-31 13:26:23 +00:00
}
if err := wall . Save ( ) ; err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( fmt . Errorf ( "error while saving wallet: %w" , err ) , 1 )
2020-03-13 13:24:33 +00:00
}
return nil
}
2020-08-28 09:11:19 +00:00
func askForConsent ( w io . Writer ) bool {
2021-02-10 08:53:01 +00:00
response , err := input . ReadLine ( "Are you sure? [y/N]: " )
2020-03-13 13:24:33 +00:00
if err == nil {
response = strings . ToLower ( strings . TrimSpace ( response ) )
if response == "y" || response == "yes" {
return true
}
}
2020-08-28 09:11:19 +00:00
fmt . Fprintln ( w , "Cancelled." )
2020-03-13 13:24:33 +00:00
return false
}
2020-01-09 15:39:38 +00:00
func dumpWallet ( ctx * cli . Context ) error {
2022-08-05 10:32:37 +00:00
if err := cmdargs . EnsureNone ( ctx ) ; err != nil {
return err
}
2022-06-23 13:50:21 +00:00
wall , pass , err := readWallet ( ctx )
2020-01-09 15:39:38 +00:00
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2020-01-09 15:39:38 +00:00
}
2022-09-01 18:44:49 +00:00
defer wall . Close ( )
2020-01-09 15:39:38 +00:00
if ctx . Bool ( "decrypt" ) {
2022-06-23 13:50:21 +00:00
if pass == nil {
password , err := input . ReadPassword ( EnterPasswordPrompt )
if err != nil {
2024-07-12 09:35:10 +00:00
return cli . Exit ( fmt . Errorf ( "error reading password: %w" , err ) , 1 )
2022-06-23 13:50:21 +00:00
}
pass = & password
2020-01-09 15:39:38 +00:00
}
for i := range wall . Accounts {
// Just testing the decryption here.
2022-06-23 13:50:21 +00:00
err := wall . Accounts [ i ] . Decrypt ( * pass , wall . Scrypt )
2020-01-09 15:39:38 +00:00
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2020-01-09 15:39:38 +00:00
}
}
}
2020-08-28 09:11:19 +00:00
fmtPrintWallet ( ctx . App . Writer , wall )
2018-03-02 15:24:09 +00:00
return nil
}
2021-03-05 09:23:25 +00:00
func dumpKeys ( ctx * cli . Context ) error {
2022-08-05 10:32:37 +00:00
if err := cmdargs . EnsureNone ( ctx ) ; err != nil {
return err
}
2022-06-23 13:50:21 +00:00
wall , _ , err := readWallet ( ctx )
2021-03-05 09:23:25 +00:00
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2021-03-05 09:23:25 +00:00
}
2022-09-01 18:44:49 +00:00
defer wall . Close ( )
2021-03-05 09:23:25 +00:00
accounts := wall . Accounts
2021-04-16 09:15:00 +00:00
addrFlag := ctx . Generic ( "address" ) . ( * flags . Address )
if addrFlag . IsSet {
acc := wall . GetAccount ( addrFlag . Uint160 ( ) )
2021-03-05 09:23:25 +00:00
if acc == nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( "account is missing" , 1 )
2021-03-05 09:23:25 +00:00
}
accounts = [ ] * wallet . Account { acc }
}
hasPrinted := false
for _ , acc := range accounts {
pub , ok := vm . ParseSignatureContract ( acc . Contract . Script )
if ok {
if hasPrinted {
fmt . Fprintln ( ctx . App . Writer )
}
fmt . Fprintf ( ctx . App . Writer , "%s (simple signature contract):\n" , acc . Address )
fmt . Fprintln ( ctx . App . Writer , hex . EncodeToString ( pub ) )
hasPrinted = true
continue
}
n , bs , ok := vm . ParseMultiSigContract ( acc . Contract . Script )
if ok {
if hasPrinted {
fmt . Fprintln ( ctx . App . Writer )
}
fmt . Fprintf ( ctx . App . Writer , "%s (%d out of %d multisig contract):\n" , acc . Address , n , len ( bs ) )
for i := range bs {
fmt . Fprintln ( ctx . App . Writer , hex . EncodeToString ( bs [ i ] ) )
}
hasPrinted = true
continue
}
2021-04-16 09:15:00 +00:00
if addrFlag . IsSet {
2024-07-09 18:24:39 +00:00
return cli . Exit ( fmt . Errorf ( "unknown script type for address %s" , address . Uint160ToString ( addrFlag . Uint160 ( ) ) ) , 1 )
2021-03-05 09:23:25 +00:00
}
}
return nil
}
2022-08-31 18:12:08 +00:00
func stripKeys ( ctx * cli . Context ) error {
if err := cmdargs . EnsureNone ( ctx ) ; err != nil {
return err
}
wall , _ , err := readWallet ( ctx )
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2022-08-31 18:12:08 +00:00
}
2022-09-01 18:44:49 +00:00
defer wall . Close ( )
2022-08-31 18:12:08 +00:00
if ! ctx . Bool ( "force" ) {
fmt . Fprintln ( ctx . App . Writer , "All private keys for all accounts will be removed from the wallet. This action is irreversible." )
if ok := askForConsent ( ctx . App . Writer ) ; ! ok {
return nil
}
}
for _ , a := range wall . Accounts {
a . EncryptedWIF = ""
}
if err := wall . Save ( ) ; err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( fmt . Errorf ( "error while saving wallet: %w" , err ) , 1 )
2022-08-31 18:12:08 +00:00
}
return nil
}
2018-03-02 15:24:09 +00:00
func createWallet ( ctx * cli . Context ) error {
2022-08-05 10:32:37 +00:00
if err := cmdargs . EnsureNone ( ctx ) ; err != nil {
return err
}
2020-06-25 15:46:24 +00:00
path := ctx . String ( "wallet" )
2022-06-23 13:50:21 +00:00
configPath := ctx . String ( "wallet-config" )
if len ( path ) != 0 && len ( configPath ) != 0 {
return errConflictingWalletFlags
}
if len ( path ) == 0 && len ( configPath ) == 0 {
2024-07-09 18:24:39 +00:00
return cli . Exit ( errNoPath , 1 )
2018-03-02 15:24:09 +00:00
}
2022-06-23 13:50:21 +00:00
var pass * string
if len ( configPath ) != 0 {
2023-12-04 14:02:44 +00:00
cfg , err := options . ReadWalletConfig ( configPath )
2022-06-23 13:50:21 +00:00
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2022-06-23 13:50:21 +00:00
}
path = cfg . Path
pass = & cfg . Password
}
2018-03-02 15:24:09 +00:00
wall , err := wallet . NewWallet ( path )
if err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2018-03-02 15:24:09 +00:00
}
if err := wall . Save ( ) ; err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2018-03-02 15:24:09 +00:00
}
if ctx . Bool ( "account" ) {
2022-06-23 13:50:21 +00:00
if err := createAccount ( wall , pass ) ; err != nil {
2024-07-09 18:24:39 +00:00
return cli . Exit ( err , 1 )
2018-03-02 15:24:09 +00:00
}
2022-09-01 18:44:49 +00:00
defer wall . Close ( )
2018-03-02 15:24:09 +00:00
}
2020-08-28 09:11:19 +00:00
fmtPrintWallet ( ctx . App . Writer , wall )
fmt . Fprintf ( ctx . App . Writer , "wallet successfully created, file location is %s\n" , wall . Path ( ) )
2018-03-02 15:24:09 +00:00
return nil
}
2021-04-19 14:51:11 +00:00
func readAccountInfo ( ) ( string , string , error ) {
2023-01-25 06:34:17 +00:00
name , err := readAccountName ( )
2022-01-15 00:46:31 +00:00
if err != nil {
return "" , "" , err
}
phrase , err := readNewPassword ( )
if err != nil {
return "" , "" , err
}
return name , phrase , nil
}
2023-01-25 06:34:17 +00:00
func readAccountName ( ) ( string , error ) {
return input . ReadLine ( "Enter the name of the account > " )
}
2022-01-15 00:46:31 +00:00
func readNewPassword ( ) ( string , error ) {
2022-04-29 15:22:33 +00:00
phrase , err := input . ReadPassword ( EnterNewPasswordPrompt )
2020-02-20 10:34:27 +00:00
if err != nil {
2022-01-15 00:46:31 +00:00
return "" , fmt . Errorf ( "Error reading password: %w" , err )
2020-02-20 10:34:27 +00:00
}
2022-04-29 15:22:33 +00:00
phraseCheck , err := input . ReadPassword ( ConfirmPasswordPrompt )
2020-02-20 10:34:27 +00:00
if err != nil {
2022-01-15 00:46:31 +00:00
return "" , fmt . Errorf ( "Error reading password: %w" , err )
2020-02-20 10:34:27 +00:00
}
2018-03-02 15:24:09 +00:00
if phrase != phraseCheck {
2022-01-15 00:46:31 +00:00
return "" , errPhraseMismatch
2018-03-02 15:24:09 +00:00
}
2022-01-15 00:46:31 +00:00
return phrase , nil
2020-02-20 10:48:59 +00:00
}
2022-06-23 13:50:21 +00:00
func createAccount ( wall * wallet . Wallet , pass * string ) error {
var (
name , phrase string
err error
)
if pass == nil {
name , phrase , err = readAccountInfo ( )
if err != nil {
return err
}
} else {
phrase = * pass
2020-02-20 10:48:59 +00:00
}
2018-03-02 15:24:09 +00:00
return wall . CreateAccount ( name , phrase )
}
2022-07-04 10:27:45 +00:00
func openWallet ( ctx * cli . Context , canUseWalletConfig bool ) ( * wallet . Wallet , * string , error ) {
path , pass , err := getWalletPathAndPass ( ctx , canUseWalletConfig )
if err != nil {
2024-07-09 18:24:39 +00:00
return nil , nil , cli . Exit ( fmt . Errorf ( "failed to get wallet path or password: %w" , err ) , 1 )
2020-02-21 08:28:49 +00:00
}
2021-12-07 16:36:02 +00:00
if path == "-" {
2022-06-23 13:50:21 +00:00
return nil , nil , errNoStdin
2021-12-07 16:36:02 +00:00
}
2022-07-04 10:27:45 +00:00
w , err := wallet . NewWalletFromFile ( path )
if err != nil {
2024-07-09 18:24:39 +00:00
return nil , nil , cli . Exit ( fmt . Errorf ( "failed to read wallet: %w" , err ) , 1 )
2022-07-04 10:27:45 +00:00
}
return w , pass , nil
}
func readWallet ( ctx * cli . Context ) ( * wallet . Wallet , * string , error ) {
path , pass , err := getWalletPathAndPass ( ctx , true )
if err != nil {
return nil , nil , err
}
if path == "-" {
w := & wallet . Wallet { }
if err := json . NewDecoder ( os . Stdin ) . Decode ( w ) ; err != nil {
2022-09-02 11:29:47 +00:00
return nil , nil , fmt . Errorf ( "js %w" , err )
2022-06-23 13:50:21 +00:00
}
2022-07-04 10:27:45 +00:00
return w , nil , nil
2022-06-23 13:50:21 +00:00
}
w , err := wallet . NewWalletFromFile ( path )
if err != nil {
return nil , nil , err
}
return w , pass , nil
2021-12-07 16:36:02 +00:00
}
2022-07-04 10:27:45 +00:00
// getWalletPathAndPass retrieves wallet path from context or from wallet configuration file.
// If wallet configuration file is specified, then account password is returned.
func getWalletPathAndPass ( ctx * cli . Context , canUseWalletConfig bool ) ( string , * string , error ) {
2022-06-23 13:50:21 +00:00
path , configPath := ctx . String ( "wallet" ) , ctx . String ( "wallet-config" )
2022-07-04 10:27:45 +00:00
if ! canUseWalletConfig && len ( configPath ) != 0 {
return "" , nil , errors . New ( "can't use wallet configuration file for this command" )
}
2022-06-23 13:50:21 +00:00
if len ( path ) != 0 && len ( configPath ) != 0 {
2022-07-04 10:27:45 +00:00
return "" , nil , errConflictingWalletFlags
2022-06-23 13:50:21 +00:00
}
if len ( path ) == 0 && len ( configPath ) == 0 {
2022-07-04 10:27:45 +00:00
return "" , nil , errNoPath
2022-06-23 13:50:21 +00:00
}
var pass * string
if len ( configPath ) != 0 {
2023-12-04 14:02:44 +00:00
cfg , err := options . ReadWalletConfig ( configPath )
2022-06-23 13:50:21 +00:00
if err != nil {
2022-07-04 10:27:45 +00:00
return "" , nil , err
2022-06-23 13:50:21 +00:00
}
path = cfg . Path
pass = & cfg . Password
2021-12-07 16:36:02 +00:00
}
2022-07-04 10:27:45 +00:00
return path , pass , nil
2022-06-23 13:50:21 +00:00
}
2023-01-25 06:34:17 +00:00
func newAccountFromWIF ( w io . Writer , wif string , scrypt keys . ScryptParams , label * string , pass * string ) ( * wallet . Account , error ) {
var (
phrase , name string
err error
)
if pass != nil {
phrase = * pass
}
2023-01-26 04:11:57 +00:00
if label == nil {
name , err = readAccountName ( )
if err != nil {
return nil , fmt . Errorf ( "failed to read account label: %w" , err )
}
} else {
2023-01-25 06:34:17 +00:00
name = * label
}
2020-02-20 10:48:59 +00:00
// note: NEP2 strings always have length of 58 even though
// base58 strings can have different lengths even if slice lengths are equal
if len ( wif ) == 58 {
2023-01-25 06:34:17 +00:00
if pass == nil {
phrase , err = input . ReadPassword ( EnterPasswordPrompt )
if err != nil {
return nil , fmt . Errorf ( "error reading password: %w" , err )
}
2020-02-20 10:48:59 +00:00
}
2023-01-25 06:34:17 +00:00
acc , err := wallet . NewAccountFromEncryptedWIF ( wif , phrase , scrypt )
if err != nil {
2023-01-25 06:51:54 +00:00
// If password from wallet config wasn't OK then retry with the user input,
// see the https://github.com/nspcc-dev/neo-go/issues/2883#issuecomment-1399923088.
if pass == nil {
return nil , err
}
phrase , err = input . ReadPassword ( EnterPasswordPrompt )
if err != nil {
return nil , fmt . Errorf ( "error reading password: %w" , err )
}
acc , err = wallet . NewAccountFromEncryptedWIF ( wif , phrase , scrypt )
if err != nil {
return nil , err
}
2023-01-25 06:34:17 +00:00
}
acc . Label = name
return acc , nil
2020-02-20 10:48:59 +00:00
}
acc , err := wallet . NewAccountFromWIF ( wif )
if err != nil {
return nil , err
}
2020-08-28 09:11:19 +00:00
fmt . Fprintln ( w , "Provided WIF was unencrypted. Wallet can contain only encrypted keys." )
2023-01-25 06:34:17 +00:00
if pass == nil {
phrase , err = readNewPassword ( )
if err != nil {
return nil , fmt . Errorf ( "failed to read new password: %w" , err )
}
2020-02-20 10:48:59 +00:00
}
acc . Label = name
2023-01-25 06:34:17 +00:00
if err := acc . Encrypt ( phrase , scrypt ) ; err != nil {
2020-02-20 10:48:59 +00:00
return nil , err
}
return acc , nil
}
func addAccountAndSave ( w * wallet . Wallet , acc * wallet . Account ) error {
for i := range w . Accounts {
if w . Accounts [ i ] . Address == acc . Address {
return fmt . Errorf ( "address '%s' is already in wallet" , acc . Address )
}
}
w . AddAccount ( acc )
return w . Save ( )
}
2020-08-28 09:11:19 +00:00
func fmtPrintWallet ( w io . Writer , wall * wallet . Wallet ) {
2018-03-02 15:24:09 +00:00
b , _ := wall . JSON ( )
2020-08-28 09:11:19 +00:00
fmt . Fprintln ( w , "" )
fmt . Fprintln ( w , string ( b ) )
fmt . Fprintln ( w , "" )
2018-03-02 15:24:09 +00:00
}