waddrmgr/scoped_manager: add nextAddresses cache update to db tx's OnCommit()

This commit makes nextAddresses add a function to the transactions
OnCommit handler used to update the cache on successful database
transaction commit. Before this we would risk the cache and database of
get out of sync if the database transaction failed or was aborted after
the cache was updated.
This commit is contained in:
Johan T. Halseth 2018-12-10 11:57:24 +01:00
parent 3378be750b
commit 918d9c2f88
No known key found for this signature in database
GPG key ID: 15BAADA29DA20D26

View file

@ -818,33 +818,46 @@ func (s *ScopedKeyManager) nextAddresses(ns walletdb.ReadWriteBucket,
}
}
// Finally update the next address tracking and add the addresses to
// the cache after the newly generated addresses have been successfully
// added to the db.
managedAddresses := make([]ManagedAddress, 0, len(addressInfo))
for _, info := range addressInfo {
ma := info.managedAddr
s.addrs[addrKey(ma.Address().ScriptAddress())] = ma
// Add the new managed address to the list of addresses that
// need their private keys derived when the address manager is
// next unlocked.
if s.rootManager.IsLocked() && !s.rootManager.WatchOnly() {
s.deriveOnUnlock = append(s.deriveOnUnlock, info)
}
managedAddresses = append(managedAddresses, ma)
}
// Set the last address and next address for tracking.
ma := addressInfo[len(addressInfo)-1].managedAddr
if internal {
acctInfo.nextInternalIndex = nextIndex
acctInfo.lastInternalAddr = ma
} else {
acctInfo.nextExternalIndex = nextIndex
acctInfo.lastExternalAddr = ma
// Finally, create a closure that will update the next address tracking
// and add the addresses to the cache after the newly generated
// addresses have been successfully committed to the db.
onCommit := func() {
// Since this closure will be called when the DB transaction
// gets committed, we won't longer be holding the manager's
// mutex at that point. We must therefore re-acquire it before
// continuing.
s.mtx.Lock()
defer s.mtx.Unlock()
for _, info := range addressInfo {
ma := info.managedAddr
s.addrs[addrKey(ma.Address().ScriptAddress())] = ma
// Add the new managed address to the list of addresses
// that need their private keys derived when the
// address manager is next unlocked.
if s.rootManager.IsLocked() && !s.rootManager.WatchOnly() {
s.deriveOnUnlock = append(s.deriveOnUnlock, info)
}
}
// Set the last address and next address for tracking.
ma := addressInfo[len(addressInfo)-1].managedAddr
if internal {
acctInfo.nextInternalIndex = nextIndex
acctInfo.lastInternalAddr = ma
} else {
acctInfo.nextExternalIndex = nextIndex
acctInfo.lastExternalAddr = ma
}
}
ns.Tx().OnCommit(onCommit)
return managedAddresses, nil
}