From 918d9c2f882cd28123d0c052e452814c1b042e83 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Mon, 10 Dec 2018 11:57:24 +0100 Subject: [PATCH] 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. --- waddrmgr/scoped_manager.go | 53 ++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/waddrmgr/scoped_manager.go b/waddrmgr/scoped_manager.go index dfa7a8c..0f6ee3a 100644 --- a/waddrmgr/scoped_manager.go +++ b/waddrmgr/scoped_manager.go @@ -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 }