wallet/wallet: consolidate rollback logic

In this commit, we consolidate the existing rollback logic to carry out
its duties under one database transaction.

Co-authored-by: Roei Erez <roeierez@gmail.com>
This commit is contained in:
Wilmer Paulino 2019-01-07 18:44:28 -08:00
parent db837f1ba3
commit c853ceaa60
No known key found for this signature in database
GPG key ID: 6DF57B9F9514972F

View file

@ -322,11 +322,6 @@ func (w *Wallet) activeData(dbtx walletdb.ReadTx) ([]btcutil.Address, []wtxmgr.C
// finished. The birthday block can be passed in, if set, to ensure we can // finished. The birthday block can be passed in, if set, to ensure we can
// properly detect if it gets rolled back. // properly detect if it gets rolled back.
func (w *Wallet) syncWithChain(birthdayStamp *waddrmgr.BlockStamp) error { func (w *Wallet) syncWithChain(birthdayStamp *waddrmgr.BlockStamp) error {
chainClient, err := w.requireChainClient()
if err != nil {
return err
}
// To start, if we've yet to find our birthday stamp, we'll do so now. // To start, if we've yet to find our birthday stamp, we'll do so now.
if birthdayStamp == nil { if birthdayStamp == nil {
var err error var err error
@ -352,14 +347,20 @@ func (w *Wallet) syncWithChain(birthdayStamp *waddrmgr.BlockStamp) error {
} }
} }
// Compare previously-seen blocks against the chain server. If any of // Compare previously-seen blocks against the current chain. If any of
// these blocks no longer exist, rollback all of the missing blocks // these blocks no longer exist, rollback all of the missing blocks
// before catching up with the rescan. // before catching up with the rescan.
rollback := false rollback := false
rollbackStamp := w.Manager.SyncedTo() rollbackStamp := w.Manager.SyncedTo()
chainClient, err := w.requireChainClient()
if err != nil {
return err
}
err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error { err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey) addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
txmgrNs := tx.ReadWriteBucket(wtxmgrNamespaceKey) txmgrNs := tx.ReadWriteBucket(wtxmgrNamespaceKey)
for height := rollbackStamp.Height; true; height-- { for height := rollbackStamp.Height; true; height-- {
hash, err := w.Manager.BlockHash(addrmgrNs, height) hash, err := w.Manager.BlockHash(addrmgrNs, height)
if err != nil { if err != nil {
@ -384,46 +385,39 @@ func (w *Wallet) syncWithChain(birthdayStamp *waddrmgr.BlockStamp) error {
rollback = true rollback = true
} }
if rollback { // If a rollback did not happen, we can proceed safely.
if !rollback {
return nil
}
// Otherwise, we'll mark this as our new synced height.
err := w.Manager.SetSyncedTo(addrmgrNs, &rollbackStamp) err := w.Manager.SetSyncedTo(addrmgrNs, &rollbackStamp)
if err != nil { if err != nil {
return err return err
} }
// Rollback unconfirms transactions at and beyond the
// passed height, so add one to the new synced-to height
// to prevent unconfirming txs from the synced-to block.
err = w.TxStore.Rollback(txmgrNs, rollbackStamp.Height+1)
if err != nil {
return err
}
}
return nil
})
if err != nil {
return err
}
// If a birthday stamp was found during the initial sync and the // If the rollback happened to go beyond our birthday stamp,
// rollback causes us to revert it, update the birthday stamp so that it // we'll need to find a new one by syncing with the chain again
// points at the new tip. // until finding one.
birthdayRollback := false if rollbackStamp.Height <= birthdayStamp.Height &&
if birthdayStamp != nil && rollbackStamp.Height <= birthdayStamp.Height { rollbackStamp.Hash != birthdayStamp.Hash {
birthdayStamp = &rollbackStamp
birthdayRollback = true
log.Debugf("Found new birthday block after rollback: "+ err := w.Manager.SetBirthdayBlock(
"height=%d, hash=%v", birthdayStamp.Height, addrmgrNs, rollbackStamp, true,
birthdayStamp.Hash)
err := walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
return w.Manager.SetBirthdayBlock(
ns, *birthdayStamp, true,
) )
if err != nil {
return err
}
}
// Finally, we'll roll back our transaction store to reflect the
// stale state. `Rollback` unconfirms transactions at and beyond
// the passed height, so add one to the new synced-to height to
// prevent unconfirming transactions in the synced-to block.
return w.TxStore.Rollback(txmgrNs, rollbackStamp.Height+1)
}) })
if err != nil { if err != nil {
return nil return err
}
} }
// Request notifications for connected and disconnected blocks. // Request notifications for connected and disconnected blocks.