Skip to content

Commit

Permalink
Merge pull request #618 from wpaulino/change-wallet-password
Browse files Browse the repository at this point in the history
Change wallet password
  • Loading branch information
Roasbeef committed Jun 2, 2018
2 parents 7e4abb4 + 4e2ae89 commit 2fa2644
Show file tree
Hide file tree
Showing 11 changed files with 991 additions and 575 deletions.
72 changes: 66 additions & 6 deletions cmd/lncli/commands.go
Expand Up @@ -1382,6 +1382,66 @@ func unlock(ctx *cli.Context) error {
return nil
}

var changePasswordCommand = cli.Command{
Name: "changepassword",
Category: "Startup",
Usage: "Change an encrypted wallet's password at startup.",
Description: `
The changepassword command is used to Change lnd's encrypted wallet's
password. It will automatically unlock the daemon if the password change
is successful.
If one did not specify a password for their wallet (running lnd with
--noencryptwallet), one must restart their daemon without
--noencryptwallet and use this command. The "current password" field
should be left empty.
`,
Action: actionDecorator(changePassword),
}

func changePassword(ctx *cli.Context) error {
ctxb := context.Background()
client, cleanUp := getWalletUnlockerClient(ctx)
defer cleanUp()

fmt.Printf("Input current wallet password: ")
currentPw, err := terminal.ReadPassword(int(syscall.Stdin))
if err != nil {
return err
}
fmt.Println()

fmt.Printf("Input new wallet password: ")
newPw, err := terminal.ReadPassword(int(syscall.Stdin))
if err != nil {
return err
}
fmt.Println()

fmt.Printf("Confirm new wallet password: ")
confirmPw, err := terminal.ReadPassword(int(syscall.Stdin))
if err != nil {
return err
}
fmt.Println()

if !bytes.Equal(newPw, confirmPw) {
return fmt.Errorf("passwords don't match")
}

req := &lnrpc.ChangePasswordRequest{
CurrentPassword: currentPw,
NewPassword: newPw,
}

_, err = client.ChangePassword(ctxb, req)
if err != nil {
return err
}

return nil
}

var walletBalanceCommand = cli.Command{
Name: "walletbalance",
Category: "Wallet",
Expand All @@ -1407,9 +1467,9 @@ func walletBalance(ctx *cli.Context) error {
var channelBalanceCommand = cli.Command{
Name: "channelbalance",
Category: "Channels",
Usage: "Returns the sum of the total available channel balance across " +
Usage: "Returns the sum of the total available channel balance across " +
"all open channels.",
Action: actionDecorator(channelBalance),
Action: actionDecorator(channelBalance),
}

func channelBalance(ctx *cli.Context) error {
Expand Down Expand Up @@ -2356,7 +2416,7 @@ func queryRoutes(ctx *cli.Context) error {
var getNetworkInfoCommand = cli.Command{
Name: "getnetworkinfo",
Category: "Channels",
Usage: "Get statistical information about the current " +
Usage: "Get statistical information about the current " +
"state of the network.",
Description: "Returns a set of statistics pertaining to the known " +
"channel graph",
Expand Down Expand Up @@ -2639,9 +2699,9 @@ func feeReport(ctx *cli.Context) error {
}

var updateChannelPolicyCommand = cli.Command{
Name: "updatechanpolicy",
Category: "Channels",
Usage: "Update the channel policy for all channels, or a single " +
Name: "updatechanpolicy",
Category: "Channels",
Usage: "Update the channel policy for all channels, or a single " +
"channel.",
ArgsUsage: "base_fee_msat fee_rate time_lock_delta [channel_point]",
Description: `
Expand Down
1 change: 1 addition & 0 deletions cmd/lncli/main.go
Expand Up @@ -194,6 +194,7 @@ func main() {
app.Commands = []cli.Command{
createCommand,
unlockCommand,
changePasswordCommand,
newAddressCommand,
sendManyCommand,
sendCoinsCommand,
Expand Down
70 changes: 37 additions & 33 deletions lnd.go
Expand Up @@ -195,32 +195,20 @@ func lndMain() error {
}
proxyOpts := []grpc.DialOption{grpc.WithTransportCredentials(cCreds)}

var macaroonService *macaroons.Service
if !cfg.NoMacaroons {
// Create the macaroon authentication/authorization service.
macaroonService, err = macaroons.NewService(macaroonDatabaseDir,
macaroons.IPLockChecker)
if err != nil {
srvrLog.Errorf("unable to create macaroon service: %v", err)
return err
}
defer macaroonService.Close()
}

var (
privateWalletPw = []byte("hello")
publicWalletPw = []byte("public")
privateWalletPw = lnwallet.DefaultPrivatePassphrase
publicWalletPw = lnwallet.DefaultPublicPassphrase
birthday time.Time
recoveryWindow uint32
)

// We wait until the user provides a password over RPC. In case lnd is
// started with the --noencryptwallet flag, we use the default password
// "hello" for wallet encryption.
// for wallet encryption.
if !cfg.NoEncryptWallet {
walletInitParams, err := waitForWalletPassword(
cfg.RPCListeners, cfg.RESTListeners, serverOpts,
proxyOpts, tlsConf, macaroonService,
proxyOpts, tlsConf,
)
if err != nil {
return err
Expand All @@ -238,12 +226,20 @@ func lndMain() error {
}
}

var macaroonService *macaroons.Service
if !cfg.NoMacaroons {
// Create the macaroon authentication/authorization service.
macaroonService, err = macaroons.NewService(macaroonDatabaseDir,
macaroons.IPLockChecker)
if err != nil {
srvrLog.Errorf("unable to create macaroon service: %v", err)
return err
}
defer macaroonService.Close()

// Try to unlock the macaroon store with the private password.
// Ignore ErrAlreadyUnlocked since it could be unlocked by the
// wallet unlocker.
err = macaroonService.CreateUnlock(&privateWalletPw)
if err != nil && err != macaroons.ErrAlreadyUnlocked {
if err != nil {
srvrLog.Error(err)
return err
}
Expand Down Expand Up @@ -879,23 +875,30 @@ type WalletUnlockParams struct {
// waitForWalletPassword will spin up gRPC and REST endpoints for the
// WalletUnlocker server, and block until a password is provided by
// the user to this RPC server.
func waitForWalletPassword(
grpcEndpoints, restEndpoints []string,
serverOpts []grpc.ServerOption,
proxyOpts []grpc.DialOption,
tlsConf *tls.Config,
macaroonService *macaroons.Service) (*WalletUnlockParams, error) {

// Set up a new PasswordService, which will listen
// for passwords provided over RPC.
func waitForWalletPassword(grpcEndpoints, restEndpoints []string,
serverOpts []grpc.ServerOption, proxyOpts []grpc.DialOption,
tlsConf *tls.Config) (*WalletUnlockParams, error) {

// Set up a new PasswordService, which will listen for passwords
// provided over RPC.
grpcServer := grpc.NewServer(serverOpts...)

chainConfig := cfg.Bitcoin
if registeredChains.PrimaryChain() == litecoinChain {
chainConfig = cfg.Litecoin
}
pwService := walletunlocker.New(macaroonService,
chainConfig.ChainDir, activeNetParams.Params)

// The macaroon files are passed to the wallet unlocker since they are
// also encrypted with the wallet's password. These files will be
// deleted within it and recreated when successfully changing the
// wallet's password.
macaroonFiles := []string{
filepath.Join(macaroonDatabaseDir, macaroons.DBFilename),
cfg.AdminMacPath, cfg.ReadMacPath, cfg.InvoiceMacPath,
}
pwService := walletunlocker.New(
chainConfig.ChainDir, activeNetParams.Params, macaroonFiles,
)
lnrpc.RegisterWalletUnlockerServer(grpcServer, pwService)

// Use a WaitGroup so we can be sure the instructions on how to input the
Expand Down Expand Up @@ -957,9 +960,10 @@ func waitForWalletPassword(
wg.Wait()

// Wait for user to provide the password.
ltndLog.Infof("Waiting for wallet encryption password. " +
"Use `lncli create` to create wallet, or " +
"`lncli unlock` to unlock already created wallet.")
ltndLog.Infof("Waiting for wallet encryption password. Use `lncli " +
"create` to create a wallet, `lncli unlock` to unlock an " +
"existing wallet, or `lncli changepassword` to change the " +
"password of an existing wallet and unlock it.")

// We currently don't distinguish between getting a password to be used
// for creation or unlocking, as a new wallet db will be created if
Expand Down

0 comments on commit 2fa2644

Please sign in to comment.