walletunlock: exend the Init and Unlock methods to also return optional SCB's

In this commit, we we extend the Init and Unlock methods to also parse
out and return optional SCB instances. With this change, when the user
creates their node, if they have an existing seed and also a set of SCBs
(either single or multi), they'll be able to recover both their on-chain
balance, and also any funds that were settled within their existing
channels.
This commit is contained in:
Olaoluwa Osuntokun 2018-12-09 20:01:23 -08:00
parent da3625fc02
commit f0e9a1f753
No known key found for this signature in database
GPG Key ID: CE58F7F8E20FD9A2

@ -10,6 +10,7 @@ import (
"github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcwallet/wallet" "github.com/btcsuite/btcwallet/wallet"
"github.com/lightningnetwork/lnd/aezeed" "github.com/lightningnetwork/lnd/aezeed"
"github.com/lightningnetwork/lnd/chanbackup"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
@ -17,6 +18,19 @@ import (
"golang.org/x/net/context" "golang.org/x/net/context"
) )
// ChannelsToRecover wraps any set of packed (serialized+encrypted) channel
// back ups together. These can be passed in when unlocking the wallet, or
// creating a new wallet for the first time with an existing seed.
type ChannelsToRecover struct {
// PackedMultiChanBackup is an encrypted and serialized multi-channel
// backup.
PackedMultiChanBackup chanbackup.PackedMulti
// PackedSingleChanBackups is a series of encrypted and serialized
// single-channel backup for one or more channels.
PackedSingleChanBackups chanbackup.PackedSingles
}
// WalletInitMsg is a message sent by 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 // 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 // passphrase, but is also able to provide their own source of entropy. If
@ -36,6 +50,10 @@ type WalletInitMsg struct {
// recovery should be attempted, such as after the wallet's initial // recovery should be attempted, such as after the wallet's initial
// creation. // creation.
RecoveryWindow uint32 RecoveryWindow uint32
// ChanBackups a set of static channel backups that should be received
// after the wallet has been initialized.
ChanBackups ChannelsToRecover
} }
// WalletUnlockMsg is a message sent by the UnlockerService when a user wishes // WalletUnlockMsg is a message sent by the UnlockerService when a user wishes
@ -53,12 +71,16 @@ type WalletUnlockMsg struct {
// creation, but before any addresses have been created. // creation, but before any addresses have been created.
RecoveryWindow uint32 RecoveryWindow uint32
// Wallet is the loaded and unlocked Wallet. This is returned // Wallet is the loaded and unlocked Wallet. This is returned through
// through the channel to avoid it being unlocked twice (once to check // the channel to avoid it being unlocked twice (once to check if the
// if the password is correct, here in the WalletUnlocker and again // password is correct, here in the WalletUnlocker and again later when
// later when lnd actually uses it). Because unlocking involves scrypt // lnd actually uses it). Because unlocking involves scrypt which is
// which is resource intensive, we want to avoid doing it twice. // resource intensive, we want to avoid doing it twice.
Wallet *wallet.Wallet Wallet *wallet.Wallet
// ChanBackups a set of static channel backups that should be received
// after the wallet has been unlocked.
ChanBackups ChannelsToRecover
} }
// UnlockerService implements the WalletUnlocker service used to provide lnd // UnlockerService implements the WalletUnlocker service used to provide lnd
@ -167,6 +189,43 @@ func (u *UnlockerService) GenSeed(ctx context.Context,
}, nil }, nil
} }
// extractChanBackups is a helper function that extracts the set of channel
// backups from the proto into a format that we'll pass to higher level
// sub-systems.
func extractChanBackups(chanBackups *lnrpc.ChanBackupSnapshot) *ChannelsToRecover {
// If there aren't any populated channel backups, then we can exit
// early as there's nothing to extract.
if chanBackups == nil || (chanBackups.SingleChanBackups == nil &&
chanBackups.MultiChanBackup == nil) {
return nil
}
// Now that we know there's at least a single back up populated, we'll
// extract the multi-chan backup (if it's there).
var backups ChannelsToRecover
if chanBackups.MultiChanBackup != nil {
multiBackup := chanBackups.MultiChanBackup
backups.PackedMultiChanBackup = chanbackup.PackedMulti(
multiBackup.MultiChanBackup,
)
}
if chanBackups.SingleChanBackups == nil {
return &backups
}
// Finally, we can extract all the single chan backups as well.
for _, backup := range chanBackups.SingleChanBackups.ChanBackups {
singleChanBackup := backup.ChanBackup
backups.PackedSingleChanBackups = append(
backups.PackedSingleChanBackups, singleChanBackup,
)
}
return &backups
}
// InitWallet is used when lnd is starting up for the first time to fully // InitWallet is used when lnd is starting up for the first time to fully
// initialize the daemon and its internal wallet. At the very least a wallet // initialize the daemon and its internal wallet. At the very least a wallet
// password must be provided. This will be used to encrypt sensitive material // password must be provided. This will be used to encrypt sensitive material
@ -233,6 +292,13 @@ func (u *UnlockerService) InitWallet(ctx context.Context,
RecoveryWindow: uint32(recoveryWindow), RecoveryWindow: uint32(recoveryWindow),
} }
// Before we return the unlock payload, we'll check if we can extract
// any channel backups to pass up to the higher level sub-system.
chansToRestore := extractChanBackups(in.ChannelBackups)
if chansToRestore != nil {
initMsg.ChanBackups = *chansToRestore
}
u.InitMsgs <- initMsg u.InitMsgs <- initMsg
return &lnrpc.InitWalletResponse{}, nil return &lnrpc.InitWalletResponse{}, nil
@ -277,6 +343,13 @@ func (u *UnlockerService) UnlockWallet(ctx context.Context,
Wallet: unlockedWallet, Wallet: unlockedWallet,
} }
// Before we return the unlock payload, we'll check if we can extract
// any channel backups to pass up to the higher level sub-system.
chansToRestore := extractChanBackups(in.ChannelBackups)
if chansToRestore != nil {
walletUnlockMsg.ChanBackups = *chansToRestore
}
// At this point we was able to open the existing wallet with the // At this point we was able to open the existing wallet with the
// provided password. We send the password over the UnlockMsgs // provided password. We send the password over the UnlockMsgs
// channel, such that it can be used by lnd to open the wallet. // channel, such that it can be used by lnd to open the wallet.