Merge pull request #5809 from str4d/5805-fix-thread-notify-wallets-init

wallet: Initialise ThreadNotifyWallets with wallets best block
This commit is contained in:
Kris Nuttycombe 2022-04-04 20:04:53 -06:00 committed by GitHub
commit 98c848fd20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 77 additions and 3 deletions

View File

@ -1707,15 +1707,33 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// Start the thread that notifies listeners of transactions that have been
// recently added to the mempool, or have been added to or removed from the
// chain. We perform this before step 10 (import blocks) so that the
// original value of chainActive.Tip(), which corresponds with the wallet's
// view of the chaintip, is passed to ThreadNotifyWallets before the chain
// tip changes again.
// original value of chainActive.Tip() can be passed to ThreadNotifyWallets
// before the chain tip changes again.
{
CBlockIndex *pindexLastTip;
{
LOCK(cs_main);
pindexLastTip = chainActive.Tip();
}
// However, if a wallet is enabled, we actually want to start notifying
// from the block which corresponds with the wallet's view of the chain
// tip. In particular, we want to handle the case where the node shuts
// down uncleanly, and on restart the chain's tip is potentially up to
// an hour of chain sync older than the wallet's tip. We assume here
// that there is only a single wallet connected to the validation
// interface, which is currently true.
#ifdef ENABLE_WALLET
if (pwalletMain)
{
LOCK2(cs_main, pwalletMain->cs_wallet);
const auto walletBestBlock = pwalletMain->GetPersistedBestBlock();
if (walletBestBlock != nullptr) {
pindexLastTip = walletBestBlock;
}
}
#endif
boost::function<void()> threadnotifywallets = boost::bind(&ThreadNotifyWallets, pindexLastTip);
threadGroup.create_thread(
boost::bind(&TraceThread<boost::function<void()>>, "txnotify", threadnotifywallets)

View File

@ -93,6 +93,31 @@ void ThreadNotifyWallets(CBlockIndex *pindexLastTip)
MilliSleep(50);
}
// We cannot progress with wallet notification until the chain tip is no
// more than 100 blocks behind pindexLastTip. This can occur if the node
// shuts down abruptly without being able to write out chainActive; the
// node writes chain data out roughly hourly, while the wallet writes it
// every 10 minutes. We need to wait for ThreadImport to catch up.
while (true) {
boost::this_thread::interruption_point();
{
LOCK(cs_main);
const CBlockIndex *pindexFork = chainActive.FindFork(pindexLastTip);
// We know we have the genesis block.
assert(pindexFork != nullptr);
if (pindexLastTip->nHeight < pindexFork->nHeight ||
pindexLastTip->nHeight - pindexFork->nHeight < 100)
{
break;
}
}
MilliSleep(50);
}
while (true) {
// Run the notifier on an integer second in the steady clock.
auto now = std::chrono::steady_clock::now().time_since_epoch();

View File

@ -1501,6 +1501,27 @@ void CWallet::SetBestChain(const CBlockLocator& loc)
SetBestChainINTERNAL(walletdb, loc);
}
CBlockIndex* CWallet::GetPersistedBestBlock()
{
AssertLockHeld(cs_main);
AssertLockHeld(cs_wallet);
CWalletDB walletdb(strWalletFile);
CBlockLocator locator;
if (walletdb.ReadBestBlock(locator)) {
if (!locator.vHave.empty()) {
BlockMap::iterator mi = mapBlockIndex.find(locator.vHave[0]);
if (mi != mapBlockIndex.end()) {
return (*mi).second;
}
}
}
// The wallet's best block is not known to the node. This can occur when a
// wallet file is transplanted between disparate nodes.
return nullptr;
}
std::set<std::pair<libzcash::SproutPaymentAddress, uint256>> CWallet::GetSproutNullifiers(
const std::set<libzcash::SproutPaymentAddress>& addresses) {
std::set<std::pair<libzcash::SproutPaymentAddress, uint256>> nullifierSet;

View File

@ -1811,6 +1811,16 @@ public:
void AddPendingSaplingMigrationTx(const CTransaction& tx);
/** Saves witness caches and best block locator to disk. */
void SetBestChain(const CBlockLocator& loc);
/**
* Returns the block index corresponding to the wallet's most recently
* persisted best block. This is the state to which the wallet will revert
* if restarted immediately, and does not necessarily match the current
* in-memory state.
*
* Returns nullptr if the wallet's best block is not known to this node
* (e.g. if the wallet was transplanted from another node).
*/
CBlockIndex* GetPersistedBestBlock();
std::set<std::pair<libzcash::SproutPaymentAddress, uint256>> GetSproutNullifiers(
const std::set<libzcash::SproutPaymentAddress>& addresses);