From f0e9a1f753916a2bb4fa604489009c2ec1bc8598 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sun, 9 Dec 2018 20:01:23 -0800 Subject: [PATCH] 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. --- walletunlocker/service.go | 83 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 5 deletions(-) diff --git a/walletunlocker/service.go b/walletunlocker/service.go index 747a9e54..3fe007cb 100644 --- a/walletunlocker/service.go +++ b/walletunlocker/service.go @@ -10,6 +10,7 @@ import ( "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcwallet/wallet" "github.com/lightningnetwork/lnd/aezeed" + "github.com/lightningnetwork/lnd/chanbackup" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwallet" @@ -17,6 +18,19 @@ import ( "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 // 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 @@ -36,6 +50,10 @@ type WalletInitMsg struct { // recovery should be attempted, such as after the wallet's initial // creation. 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 @@ -53,12 +71,16 @@ type WalletUnlockMsg struct { // creation, but before any addresses have been created. RecoveryWindow uint32 - // Wallet is the loaded and unlocked Wallet. This is returned - // through the channel to avoid it being unlocked twice (once to check - // if the password is correct, here in the WalletUnlocker and again - // later when lnd actually uses it). Because unlocking involves scrypt - // which is resource intensive, we want to avoid doing it twice. + // Wallet is the loaded and unlocked Wallet. This is returned through + // the channel to avoid it being unlocked twice (once to check if the + // password is correct, here in the WalletUnlocker and again later when + // lnd actually uses it). Because unlocking involves scrypt which is + // resource intensive, we want to avoid doing it twice. 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 @@ -167,6 +189,43 @@ func (u *UnlockerService) GenSeed(ctx context.Context, }, 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 // 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 @@ -233,6 +292,13 @@ func (u *UnlockerService) InitWallet(ctx context.Context, 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 return &lnrpc.InitWalletResponse{}, nil @@ -277,6 +343,13 @@ func (u *UnlockerService) UnlockWallet(ctx context.Context, 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 // provided password. We send the password over the UnlockMsgs // channel, such that it can be used by lnd to open the wallet.