From 03a21367d39b3c6b8cf995d8d9353dc6e3ecd096 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 15 Dec 2020 21:19:32 +0100 Subject: [PATCH] lnd+walletunlocker: move history drop to unlocker Apparently dropping the wallet transaction history only fully takes effect after opening the wallet from scratch again. To do this cleanly, we need to do it in the unlocker instead of lnd. --- lnd.go | 14 ++-------- walletunlocker/service.go | 51 +++++++++++++++++++++++++++++----- walletunlocker/service_test.go | 18 +++++++++--- 3 files changed, 60 insertions(+), 23 deletions(-) diff --git a/lnd.go b/lnd.go index 4b91d825..60e3d962 100644 --- a/lnd.go +++ b/lnd.go @@ -1146,6 +1146,7 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr, pwService := walletunlocker.New( chainConfig.ChainDir, cfg.ActiveNetParams.Params, !cfg.SyncFreelist, macaroonFiles, cfg.DB.Bolt.DBTimeout, + cfg.ResetWalletTransactions, ) // Set up a new PasswordService, which will listen for passwords @@ -1313,21 +1314,10 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr, // remind the user to turn off the setting again after // successful completion. if cfg.ResetWalletTransactions { - ltndLog.Warnf("Dropping all transaction history from " + + ltndLog.Warnf("Dropped all transaction history from " + "on-chain wallet. Remember to disable " + "reset-wallet-transactions flag for next " + "start of lnd") - - err := wallet.DropTransactionHistory( - unlockMsg.Wallet.Database(), true, - ) - if err != nil { - if err := unlockMsg.UnloadWallet(); err != nil { - ltndLog.Errorf("Could not unload "+ - "wallet: %v", err) - } - return nil, shutdown, err - } } return &WalletUnlockParams{ diff --git a/walletunlocker/service.go b/walletunlocker/service.go index 539e130b..9855f22a 100644 --- a/walletunlocker/service.go +++ b/walletunlocker/service.go @@ -133,11 +133,16 @@ type UnlockerService struct { // dbTimeout specifies the timeout value to use when opening the wallet // database. dbTimeout time.Duration + + // resetWalletTransactions indicates that the wallet state should be + // reset on unlock to force a full chain rescan. + resetWalletTransactions bool } // New creates and returns a new UnlockerService. func New(chainDir string, params *chaincfg.Params, noFreelistSync bool, - macaroonFiles []string, dbTimeout time.Duration) *UnlockerService { + macaroonFiles []string, dbTimeout time.Duration, + resetWalletTransactions bool) *UnlockerService { return &UnlockerService{ InitMsgs: make(chan *WalletInitMsg, 1), @@ -145,12 +150,13 @@ func New(chainDir string, params *chaincfg.Params, noFreelistSync bool, // Make sure we buffer the channel is buffered so the main lnd // goroutine isn't blocking on writing to it. - MacResponseChan: make(chan []byte, 1), - chainDir: chainDir, - netParams: params, - macaroonFiles: macaroonFiles, - dbTimeout: dbTimeout, - noFreelistSync: noFreelistSync, + MacResponseChan: make(chan []byte, 1), + chainDir: chainDir, + netParams: params, + macaroonFiles: macaroonFiles, + dbTimeout: dbTimeout, + noFreelistSync: noFreelistSync, + resetWalletTransactions: resetWalletTransactions, } } @@ -400,6 +406,37 @@ func (u *UnlockerService) UnlockWallet(ctx context.Context, return nil, err } + // The user requested to drop their whole wallet transaction state to + // force a full chain rescan for wallet addresses. Dropping the state + // only properly takes effect after opening the wallet. That's why we + // start, drop, stop and start again. + if u.resetWalletTransactions { + dropErr := wallet.DropTransactionHistory( + unlockedWallet.Database(), true, + ) + + // Even if dropping the history fails, we'll want to unload the + // wallet. If unloading fails, that error is probably more + // important to be returned to the user anyway. + if err := loader.UnloadWallet(); err != nil { + return nil, fmt.Errorf("could not unload "+ + "wallet (tx history drop err: %v): %v", dropErr, + err) + } + + // If dropping failed but unloading didn't, we'll still abort + // and inform the user. + if dropErr != nil { + return nil, dropErr + } + + // All looks good, let's now open the wallet again. + unlockedWallet, err = loader.OpenExistingWallet(password, false) + if err != nil { + return nil, err + } + } + // We successfully opened the wallet and pass the instance back to // avoid it needing to be unlocked again. walletUnlockMsg := &WalletUnlockMsg{ diff --git a/walletunlocker/service_test.go b/walletunlocker/service_test.go index f84df857..62fcc28d 100644 --- a/walletunlocker/service_test.go +++ b/walletunlocker/service_test.go @@ -148,6 +148,7 @@ func TestGenSeed(t *testing.T) { service := walletunlocker.New( testDir, testNetParams, true, nil, kvdb.DefaultDBTimeout, + false, ) // Now that the service has been created, we'll ask it to generate a @@ -185,6 +186,7 @@ func TestGenSeedGenerateEntropy(t *testing.T) { }() service := walletunlocker.New( testDir, testNetParams, true, nil, kvdb.DefaultDBTimeout, + false, ) // Now that the service has been created, we'll ask it to generate a @@ -221,6 +223,7 @@ func TestGenSeedInvalidEntropy(t *testing.T) { }() service := walletunlocker.New( testDir, testNetParams, true, nil, kvdb.DefaultDBTimeout, + false, ) // Now that the service has been created, we'll ask it to generate a @@ -254,6 +257,7 @@ func TestInitWallet(t *testing.T) { // Create new UnlockerService. service := walletunlocker.New( testDir, testNetParams, true, nil, kvdb.DefaultDBTimeout, + false, ) // Once we have the unlocker service created, we'll now instantiate a @@ -342,6 +346,7 @@ func TestCreateWalletInvalidEntropy(t *testing.T) { // Create new UnlockerService. service := walletunlocker.New( testDir, testNetParams, true, nil, kvdb.DefaultDBTimeout, + false, ) // We'll attempt to init the wallet with an invalid cipher seed and @@ -370,9 +375,11 @@ func TestUnlockWallet(t *testing.T) { _ = os.RemoveAll(testDir) }() - // Create new UnlockerService. + // Create new UnlockerService that'll also drop the wallet's history on + // unlock. service := walletunlocker.New( testDir, testNetParams, true, nil, kvdb.DefaultDBTimeout, + true, ) ctx := context.Background() @@ -464,6 +471,7 @@ func TestChangeWalletPasswordNewRootkey(t *testing.T) { // Create a new UnlockerService with our temp files. service := walletunlocker.New( testDir, testNetParams, true, tempFiles, kvdb.DefaultDBTimeout, + false, ) ctx := context.Background() @@ -572,9 +580,11 @@ func TestChangeWalletPasswordStateless(t *testing.T) { nonExistingFile := path.Join(testDir, "does-not-exist") // Create a new UnlockerService with our temp files. - service := walletunlocker.New(testDir, testNetParams, true, []string{ - tempMacFile, nonExistingFile, - }, kvdb.DefaultDBTimeout) + service := walletunlocker.New( + testDir, testNetParams, true, []string{ + tempMacFile, nonExistingFile, + }, kvdb.DefaultDBTimeout, false, + ) // Create a wallet we can try to unlock. We use the default password // so we can check that the unlocker service defaults to this when