diff --git a/.gitignore b/.gitignore index c24c8eb..61cf93e 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ wallet myWallet*Z.json *.json log.* +/smrepl_* diff --git a/client/backend.go b/client/backend.go index 41d9ded..164200e 100644 --- a/client/backend.go +++ b/client/backend.go @@ -40,10 +40,29 @@ func friendlyTime(nastyString string) string { return t.Format("Jan 02 2006 03:04 PM") } +func (w *WalletBackend) PrintWalletMnemonic() { + mnemonic, err := w.wallet.GetMnemonic() + if err != nil { + log.Error("error reading mnemonic", err) + return + } + + fmt.Println("Mnemonic:", mnemonic) +} + func (w *WalletBackend) WalletInfo() { - fmt.Println("Wallet Name:", w.wallet.Meta.DisplayName) + fmt.Println("Name:", w.wallet.Meta.DisplayName) fmt.Println("Created:", friendlyTime(w.wallet.Meta.Created)) - fmt.Println("Wallet Path:", w.wallet.WalletPath()) + fmt.Println("File Path:", w.wallet.WalletPath()) + + addressesCount, err := w.wallet.GetNumberOfAccounts() + if err != nil { + log.Error("error reading addresses count", err) + return + } + + fmt.Println("Addresses created:", addressesCount) + } func getString(prompt string) (string, error) { @@ -147,14 +166,14 @@ func OpenWalletBackend(wallet string, grpcServer string, secureConnection bool) } func (w *WalletBackend) NewWallet() bool { - walletName := getClearString("Wallet Display Name : ") + walletName := getClearString("Wallet Display Name: ") fmt.Println() password, err := getPassword() fmt.Println() if err != nil { return false } - password2, err := getString("Repeat password : ") + password2, err := getString("Repeat password: ") fmt.Println() if err != nil { return false @@ -163,11 +182,23 @@ func (w *WalletBackend) NewWallet() bool { fmt.Println("passwords do not match") return false } - w.wallet, err = smWallet.NewWallet(walletName, password) - if err != nil { - fmt.Println(err) - return false + + mnemonicString := getClearString("Mnemonic (optional): ") + fmt.Println() + if len(mnemonicString) > 0 { + w.wallet, err = smWallet.NewWalletWithMnemonic(walletName, password, mnemonicString) + if err != nil { + fmt.Println(err) + return false + } + } else { + w.wallet, err = smWallet.NewWallet(walletName, password) + if err != nil { + fmt.Println(err) + return false + } } + err = w.wallet.SaveWalletAs(w.workingDirectory + "/my_wallet") if err != nil { fmt.Println(err) diff --git a/main.go b/main.go index 15b116f..fe3a557 100644 --- a/main.go +++ b/main.go @@ -29,6 +29,7 @@ func main() { be, err := client.OpenConnection(grpcServer, secureConnection, dataDir) if err != nil { + flag.Usage() os.Exit(1) } if walletName != "" { @@ -41,7 +42,6 @@ func main() { } } - // TODO: change this to use the health service when it is ready _, err = be.GetMeshInfo() if err != nil { log.Error("Failed to connect to mesh service at %v: %v", be.ServerInfo(), err) diff --git a/repl/account_commands.go b/repl/account_commands.go index bd32921..c9b17cd 100644 --- a/repl/account_commands.go +++ b/repl/account_commands.go @@ -11,6 +11,10 @@ import ( "github.com/spacemeshos/smrepl/log" ) +func (r* repl) printWalletMnemonic() { + r.client.PrintWalletMnemonic() +} + func (r *repl) walletInfo() { r.client.WalletInfo() } @@ -71,8 +75,10 @@ func (r *repl) chooseAccount() { account, err := r.client.CurrentAccount() if err != nil { - panic("wtf") + log.Error("error getting current account", err) + return } + fmt.Printf("%s Loaded account alias: `%s`, address: %s \n", printPrefix, account.Name, account.Address().String()) } diff --git a/repl/repl.go b/repl/repl.go index a11ed30..e55e427 100644 --- a/repl/repl.go +++ b/repl/repl.go @@ -53,6 +53,7 @@ type repl struct { // Client interface to REPL clients. type Client interface { + PrintWalletMnemonic() WalletInfo() IsOpen() bool OpenWallet() bool @@ -129,6 +130,8 @@ func (r *repl) initializeCommands() { accountCommands = []command{ // local wallet account commands {commandStateWallet, "info", commandStateLeaf, "Display wallet info", r.walletInfo}, + {commandStateWallet, "mnemonic", commandStateLeaf, "Display wallet mnemonic", r.printWalletMnemonic}, + {commandStateWallet, "close", commandStateLeaf, "Close current wallet", r.closeWallet}, {commandStateAccount, "new", commandStateLeaf, "Create a new account (key pair) and set as current", r.createAccount}, @@ -270,7 +273,6 @@ func (r *repl) commandLineParams(idx int, input string) string { func (r *repl) firstTime() { fmt.Print(printPrefix, splash) - // TODO: change this is to use the health service when it is ready _, err := r.client.GetMeshInfo() if err != nil { log.Error("Failed to connect to mesh service at %v: %v", r.client.ServerInfo(), err) diff --git a/smWallet/smWallet.go b/smWallet/smWallet.go index ddbafbc..f047489 100644 --- a/smWallet/smWallet.go +++ b/smWallet/smWallet.go @@ -23,6 +23,9 @@ const ( errorWalletNotUnlocked = "wallet has not been unlocked" // errorWalletDoesNotHaveThatAddress if attempting to access an account that has not been generated errorWalletDoesNotHaveThatAddress = "you are attempting to access an account that has not been generated" + + // entropySizeBytes is the number of bytes required for wallet entropy + entropySizeBytes = 16 ) type account struct { @@ -86,6 +89,27 @@ type Wallet struct { // NewWallet returns a brand shiny new wallet with random seed and mnemonic phrase func NewWallet(walletName, password string) (w *Wallet, err error) { + + entropy, err := bip39.NewEntropy(entropySizeBytes * 8) + if err != nil { + return nil, err + } + + mnemonic, err := bip39.NewMnemonic(entropy) + if err != nil { + return nil, err + } + + return NewWalletWithMnemonic(walletName, password, mnemonic) +} + +// NewWalletWithMnemonic creates a new wallet with the provided mnemonic +func NewWalletWithMnemonic(walletName, password string, mnemonic string) (w *Wallet, err error) { + + if len(mnemonic) == 0 { + return nil, errors.New("invalid mnemonic input") + } + wx := new(Wallet) wx.password = password wx.unlocked = true @@ -93,12 +117,8 @@ func NewWallet(walletName, password string) (w *Wallet, err error) { wx.Meta.DisplayName = walletName wx.Meta.NetID = 0 wx.Meta.Meta.Salt = spaceSalt - entropy, err := bip39.NewEntropy(128) - if err != nil { - return nil, err - } wx.Crypto.Cipher = "AES-128-CTR" - wx.Crypto.confidential.Mnemonic, err = bip39.NewMnemonic(entropy) + wx.Crypto.confidential.Mnemonic = mnemonic if err != nil { return nil, err }