diff --git a/wallet/chainntfns.go b/wallet/chainntfns.go index 419a2b3..00224f4 100644 --- a/wallet/chainntfns.go +++ b/wallet/chainntfns.go @@ -33,6 +33,47 @@ func (w *Wallet) handleChainNotifications() { } } + catchUpHashes := func(w *Wallet, client chain.Interface, + height int32) error { + // TODO(aakselrod): There's a race conditon here, which + // happens when a reorg occurs between the + // rescanProgress notification and the last GetBlockHash + // call. The solution when using btcd is to make btcd + // send blockconnected notifications with each block + // the way Neutrino does, and get rid of the loop. The + // other alternative is to check the final hash and, + // if it doesn't match the original hash returned by + // the notification, to roll back and restart the + // rescan. + log.Infof("Catching up block hashes to height %d, this"+ + " might take a while", height) + err := walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error { + ns := tx.ReadWriteBucket(waddrmgrNamespaceKey) + startBlock := w.Manager.SyncedTo() + for i := startBlock.Height + 1; i <= height; i++ { + hash, err := client.GetBlockHash(int64(i)) + if err != nil { + return err + } + bs := waddrmgr.BlockStamp{ + Height: i, + Hash: *hash, + } + err = w.Manager.SetSyncedTo(ns, &bs) + if err != nil { + return err + } + } + return nil + }) + if err != nil { + log.Errorf("Failed to update address manager "+ + "sync state for height %d: %v", height, err) + } + log.Info("Done catching up block hashes") + return err + } + for n := range chainClient.Notifications() { var notificationName string var err error @@ -72,9 +113,16 @@ func (w *Wallet) handleChainNotifications() { } notificationName = "filteredblockconnected" - // The following are handled by the wallet's rescan - // goroutines, so just pass them there. - case *chain.RescanProgress, *chain.RescanFinished: + // The following require some database maintenance, but also + // need to be reported to the wallet's rescan goroutine. + case *chain.RescanProgress: + err = catchUpHashes(w, chainClient, n.Height) + notificationName = "rescanprogress" + w.rescanNotifications <- n + case *chain.RescanFinished: + err = catchUpHashes(w, chainClient, n.Height) + notificationName = "rescanprogress" + w.SetChainSynced(true) w.rescanNotifications <- n } if err != nil { diff --git a/wallet/rescan.go b/wallet/rescan.go index 2221f80..6ea66aa 100644 --- a/wallet/rescan.go +++ b/wallet/rescan.go @@ -9,7 +9,6 @@ import ( "github.com/roasbeef/btcutil" "github.com/roasbeef/btcwallet/chain" "github.com/roasbeef/btcwallet/waddrmgr" - "github.com/roasbeef/btcwallet/walletdb" "github.com/roasbeef/btcwallet/wtxmgr" ) @@ -175,51 +174,6 @@ out: log.Infof("Rescanned through block %v (height %d)", n.Hash, n.Height) - client := w.ChainClient() - // Since btcd rescans don't send blockconnected - // notifications, we need to cycle through all of the - // rescanned blocks and write the hashes to the - // database. Neutrino rescans do send the notifications, - // which means this loop won't actually cycle. - // - // TODO(aakselrod): There's a race conditon here, which - // happens when a reorg occurs between the - // rescanProgress notification and the last GetBlockHash - // call. The solution when using btcd is to make btcd - // send blockconnected notifications with each block - // the way Neutrino does, and get rid of the loop. The - // other alternative is to check the final hash and, - // if it doesn't match the original hash returned by - // the notification, to roll back and restart the - // rescan. - log.Infof("Catching up block hashes to height %d, this"+ - " might take a while", n.Height) - err := walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error { - ns := tx.ReadWriteBucket(waddrmgrNamespaceKey) - startBlock := w.Manager.SyncedTo() - for i := startBlock.Height + 1; i <= n.Height; i++ { - hash, err := client.GetBlockHash(int64(i)) - if err != nil { - return err - } - bs := waddrmgr.BlockStamp{ - Height: i, - Hash: *hash, - } - err = w.Manager.SetSyncedTo(ns, &bs) - if err != nil { - return err - } - } - return nil - }) - if err != nil { - log.Errorf("Failed to update address manager "+ - "sync state for hash %v (height %d): %v", - n.Hash, n.Height, err) - } - log.Info("Done catching up block hashes") - case msg := <-w.rescanFinished: n := msg.Notification addrs := msg.Addresses @@ -228,53 +182,6 @@ out: "%s, height %d)", len(addrs), noun, n.Hash, n.Height) - client := w.ChainClient() - // Since btcd rescans don't send blockconnected - // notifications, we need to cycle through all of the - // rescanned blocks and write the hashes to the - // database. Neutrino rescans do send the notifications, - // which means this loop won't actually cycle. - // - // TODO(aakselrod): There's a race conditon here, which - // happens when a reorg occurs between the - // rescanFinished notification and the last GetBlockHash - // call. The solution when using btcd is to make btcd - // send blockconnected notifications with each block - // the way Neutrino does, and get rid of the loop. The - // other alternative is to check the final hash and, - // if it doesn't match the original hash returned by - // the notification, to roll back and restart the - // rescan. - log.Infof("Catching up block hashes to height %d, this"+ - " might take a while", n.Height) - err := walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error { - ns := tx.ReadWriteBucket(waddrmgrNamespaceKey) - startBlock := w.Manager.SyncedTo() - for i := startBlock.Height + 1; i <= n.Height; i++ { - hash, err := client.GetBlockHash(int64(i)) - if err != nil { - return err - } - bs := waddrmgr.BlockStamp{ - Height: i, - Hash: *hash, - } - err = w.Manager.SetSyncedTo(ns, &bs) - if err != nil { - return err - } - } - return nil - }) - if err != nil { - log.Errorf("Failed to update address manager "+ - "sync state for hash %v (height %d): %v", - n.Hash, n.Height, err) - continue - } - - w.SetChainSynced(true) - log.Info("Done catching up block hashes") go w.resendUnminedTxs() case <-quit: