From 00ee3afab87248ee89c4573fd380f0ec8a084eb4 Mon Sep 17 00:00:00 2001
From: "Johan T. Halseth" <johanth@gmail.com>
Date: Thu, 12 Oct 2017 11:42:06 +0200
Subject: [PATCH] lncli: add commands "create" and "unlock"

lncli create:
This command is used to set up a wallet encryption password for
use with lnd at first time use. It will ask fot the user to
confirm the chosen password, then do a call to the lnd RPC method
CreateWallet with the chosen password.

lncli unlock:
This command is used to unlock the wallet of a running lnd instance.
It calls the RPC method UnlockWallet with the provided password.

Both methods makes use of the terminal.ReadPassword method, to
securely read a password from user input without making it
replayable in the terminal.
---
 cmd/lncli/commands.go | 70 +++++++++++++++++++++++++++++++++++++++++++
 cmd/lncli/main.go     | 12 ++++++++
 2 files changed, 82 insertions(+)

diff --git a/cmd/lncli/commands.go b/cmd/lncli/commands.go
index fb618d2ca..f12b73e0f 100644
--- a/cmd/lncli/commands.go
+++ b/cmd/lncli/commands.go
@@ -20,6 +20,7 @@ import (
 	"github.com/roasbeef/btcd/chaincfg/chainhash"
 	"github.com/roasbeef/btcutil"
 	"github.com/urfave/cli"
+	"golang.org/x/crypto/ssh/terminal"
 	"golang.org/x/net/context"
 )
 
@@ -600,6 +601,75 @@ func listPeers(ctx *cli.Context) error {
 	return nil
 }
 
+var createCommand = cli.Command{
+	Name:   "create",
+	Usage:  "used to set the wallet password at lnd startup",
+	Action: create,
+}
+
+func create(ctx *cli.Context) error {
+	ctxb := context.Background()
+	client, cleanUp := getWalletUnlockerClient(ctx)
+	defer cleanUp()
+
+	fmt.Printf("Input wallet password: ")
+	pw1, err := terminal.ReadPassword(0)
+	if err != nil {
+		return err
+	}
+	fmt.Println()
+
+	fmt.Printf("Confirm wallet password: ")
+	pw2, err := terminal.ReadPassword(0)
+	if err != nil {
+		return err
+	}
+	fmt.Println()
+
+	if !bytes.Equal(pw1, pw2) {
+		return fmt.Errorf("passwords don't match")
+	}
+
+	req := &lnrpc.CreateWalletRequest{
+		Password: pw1,
+	}
+	_, err = client.CreateWallet(ctxb, req)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+var unlockCommand = cli.Command{
+	Name:   "unlock",
+	Usage:  "unlock encrypted wallet at lnd startup",
+	Action: unlock,
+}
+
+func unlock(ctx *cli.Context) error {
+	ctxb := context.Background()
+	client, cleanUp := getWalletUnlockerClient(ctx)
+	defer cleanUp()
+
+	fmt.Printf("Input wallet password: ")
+	pw, err := terminal.ReadPassword(0)
+	if err != nil {
+		return err
+	}
+	fmt.Println()
+
+	req := &lnrpc.UnlockWalletRequest{
+		Password: pw,
+	}
+	_, err = client.UnlockWallet(ctxb, req)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
 var walletBalanceCommand = cli.Command{
 	Name:  "walletbalance",
 	Usage: "compute and display the wallet's current balance",
diff --git a/cmd/lncli/main.go b/cmd/lncli/main.go
index dee2d44bc..8b1ebfc0c 100644
--- a/cmd/lncli/main.go
+++ b/cmd/lncli/main.go
@@ -34,6 +34,16 @@ func fatal(err error) {
 	os.Exit(1)
 }
 
+func getWalletUnlockerClient(ctx *cli.Context) (lnrpc.WalletUnlockerClient, func()) {
+	conn := getClientConn(ctx)
+
+	cleanUp := func() {
+		conn.Close()
+	}
+
+	return lnrpc.NewWalletUnlockerClient(conn), cleanUp
+}
+
 func getClient(ctx *cli.Context) (lnrpc.LightningClient, func()) {
 	conn := getClientConn(ctx)
 
@@ -146,6 +156,8 @@ func main() {
 		},
 	}
 	app.Commands = []cli.Command{
+		createCommand,
+		unlockCommand,
 		newAddressCommand,
 		sendManyCommand,
 		sendCoinsCommand,