walletunlocker: accept recovery window from InitWallet

This commit is contained in:
Conner Fromknecht 2018-03-26 14:07:22 -07:00
parent c824af11a1
commit f8c0357770
No known key found for this signature in database
GPG Key ID: 39DE78FBE6ACB0EF
2 changed files with 63 additions and 23 deletions

@ -14,7 +14,7 @@ import (
"golang.org/x/net/context"
)
// WalletInitMsg is a message sent to the UnlockerService when a user wishes to
// WalletInitMsg is a message sent by the UnlockerService when a user wishes to
// set up the internal wallet for the first time. The user MUST provide a
// passphrase, but is also able to provide their own source of entropy. If
// provided, then this source of entropy will be used to generate the wallet's
@ -27,6 +27,28 @@ type WalletInitMsg struct {
// WalletSeed is the deciphered cipher seed that the wallet should use
// to initialize itself.
WalletSeed *aezeed.CipherSeed
// RecoveryWindow is the address look-ahead used when restoring a seed
// with existing funds. A recovery window zero indicates that no
// recovery should be attempted, such as after the wallet's initial
// creation.
RecoveryWindow uint32
}
// WalletUnlockMsg is a message sent by the UnlockerService when a user wishes
// to unlock the internal wallet after initial setup. The user can optionally
// specify a recovery window, which will resume an interrupted rescan for used
// addresses.
type WalletUnlockMsg struct {
// Passphrase is the passphrase that will be used to encrypt the wallet
// itself. This MUST be at least 8 characters.
Passphrase []byte
// RecoveryWindow is the address look-ahead used when restoring a seed
// with existing funds. A recovery window zero indicates that no
// recovery should be attempted, such as after the wallet's initial
// creation, but before any addresses have been created.
RecoveryWindow uint32
}
// UnlockerService implements the WalletUnlocker service used to provide lnd
@ -37,10 +59,10 @@ type UnlockerService struct {
// InitMsgs is a channel that carries all wallet init messages.
InitMsgs chan *WalletInitMsg
// UnlockPasswords is a channel where passwords provided by the rpc
// UnlockMsgs is a channel where unlock parameters provided by the rpc
// client to be used to unlock and decrypt an existing wallet will be
// sent.
UnlockPasswords chan []byte
UnlockMsgs chan *WalletUnlockMsg
chainDir string
netParams *chaincfg.Params
@ -52,10 +74,10 @@ func New(authSvc *macaroons.Service, chainDir string,
params *chaincfg.Params) *UnlockerService {
return &UnlockerService{
InitMsgs: make(chan *WalletInitMsg, 1),
UnlockPasswords: make(chan []byte, 1),
chainDir: chainDir,
netParams: params,
InitMsgs: make(chan *WalletInitMsg, 1),
UnlockMsgs: make(chan *WalletUnlockMsg, 1),
chainDir: chainDir,
netParams: params,
}
}
@ -73,7 +95,7 @@ func (u *UnlockerService) GenSeed(ctx context.Context,
// Before we start, we'll ensure that the wallet hasn't already created
// so we don't show a *new* seed to the user if one already exists.
netDir := btcwallet.NetworkDir(u.chainDir, u.netParams)
loader := wallet.NewLoader(u.netParams, netDir)
loader := wallet.NewLoader(u.netParams, netDir, 0)
walletExists, err := loader.WalletExists()
if err != nil {
return nil, err
@ -156,10 +178,17 @@ func (u *UnlockerService) InitWallet(ctx context.Context,
"at least 8 characters")
}
// Require that the recovery window be non-negative.
recoveryWindow := in.RecoveryWindow
if recoveryWindow < 0 {
return nil, fmt.Errorf("recovery window %d must be "+
"non-negative", recoveryWindow)
}
// We'll then open up the directory that will be used to store the
// wallet's files so we can check if the wallet already exists.
netDir := btcwallet.NetworkDir(u.chainDir, u.netParams)
loader := wallet.NewLoader(u.netParams, netDir)
loader := wallet.NewLoader(u.netParams, netDir, uint32(recoveryWindow))
walletExists, err := loader.WalletExists()
if err != nil {
@ -198,8 +227,9 @@ func (u *UnlockerService) InitWallet(ctx context.Context,
// now send over the wallet password and the seed. This will allow the
// daemon to initialize itself and startup.
initMsg := &WalletInitMsg{
Passphrase: password,
WalletSeed: cipherSeed,
Passphrase: password,
WalletSeed: cipherSeed,
RecoveryWindow: uint32(recoveryWindow),
}
u.InitMsgs <- initMsg
@ -208,13 +238,16 @@ func (u *UnlockerService) InitWallet(ctx context.Context,
}
// UnlockWallet sends the password provided by the incoming UnlockWalletRequest
// over the UnlockPasswords channel in case it successfully decrypts an
// existing wallet found in the chain's wallet database directory.
// over the UnlockMsgs channel in case it successfully decrypts an existing
// wallet found in the chain's wallet database directory.
func (u *UnlockerService) UnlockWallet(ctx context.Context,
in *lnrpc.UnlockWalletRequest) (*lnrpc.UnlockWalletResponse, error) {
password := in.WalletPassword
recoveryWindow := uint32(in.RecoveryWindow)
netDir := btcwallet.NetworkDir(u.chainDir, u.netParams)
loader := wallet.NewLoader(u.netParams, netDir)
loader := wallet.NewLoader(u.netParams, netDir, recoveryWindow)
// Check if wallet already exists.
walletExists, err := loader.WalletExists()
@ -228,7 +261,7 @@ func (u *UnlockerService) UnlockWallet(ctx context.Context,
}
// Try opening the existing wallet with the provided password.
_, err = loader.OpenExistingWallet(in.WalletPassword, false)
_, err = loader.OpenExistingWallet(password, false)
if err != nil {
// Could not open wallet, most likely this means that provided
// password was incorrect.
@ -244,17 +277,22 @@ func (u *UnlockerService) UnlockWallet(ctx context.Context,
// Attempt to create a password for the macaroon service.
if u.authSvc != nil {
err = u.authSvc.CreateUnlock(&in.WalletPassword)
err = u.authSvc.CreateUnlock(&password)
if err != nil {
return nil, fmt.Errorf("unable to create/unlock "+
"macaroon store: %v", err)
}
}
walletUnlockMsg := &WalletUnlockMsg{
Passphrase: password,
RecoveryWindow: recoveryWindow,
}
// At this point we was able to open the existing wallet with the
// provided password. We send the password over the UnlockPasswords
// provided password. We send the password over the UnlockMsgs
// channel, such that it can be used by lnd to open the wallet.
u.UnlockPasswords <- in.WalletPassword
u.UnlockMsgs <- walletUnlockMsg
return &lnrpc.UnlockWalletResponse{}, nil
}

@ -37,8 +37,10 @@ var (
func createTestWallet(t *testing.T, dir string, netParams *chaincfg.Params) {
netDir := btcwallet.NetworkDir(dir, netParams)
loader := wallet.NewLoader(netParams, netDir)
_, err := loader.CreateNewWallet(testPassword, testPassword, testSeed)
loader := wallet.NewLoader(netParams, netDir, 0)
_, err := loader.CreateNewWallet(
testPassword, testPassword, testSeed, time.Time{},
)
if err != nil {
t.Fatalf("failed creating wallet: %v", err)
}
@ -340,10 +342,10 @@ func TestUnlockWallet(t *testing.T) {
// Password should be sent over the channel.
select {
case pw := <-service.UnlockPasswords:
if !bytes.Equal(pw, testPassword) {
case unlockMsg := <-service.UnlockMsgs:
if !bytes.Equal(unlockMsg.Passphrase, testPassword) {
t.Fatalf("expected to receive password %x, got %x",
testPassword, pw)
testPassword, unlockMsg.Passphrase)
}
case <-time.After(3 * time.Second):
t.Fatalf("password not received")