wallet: Initialise ThreadNotifyWallets with wallet's best block
The previous code assumed that the last chain tip notified to the wallet was equal to the node's chain tip at startup. However, this assumption fails if the node shuts down uncleanly, or if a wallet file is moved from one node to another. We now try to start notifying from the wallet's best block, and if the node doesn't have that block we fall back to the node's chain tip like before. Closes zcash/zcash#5805.
This commit is contained in:
parent
8e15446c17
commit
3a1261efda
24
src/init.cpp
24
src/init.cpp
|
@ -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->GetBestBlock();
|
||||
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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -1501,6 +1501,27 @@ void CWallet::SetBestChain(const CBlockLocator& loc)
|
|||
SetBestChainINTERNAL(walletdb, loc);
|
||||
}
|
||||
|
||||
CBlockIndex* CWallet::GetBestBlock()
|
||||
{
|
||||
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;
|
||||
|
|
|
@ -1805,6 +1805,12 @@ 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 best block, or
|
||||
* nullptr if the wallet's best block is not known to this node (e.g. if the
|
||||
* wallet was transplanted from another node).
|
||||
*/
|
||||
CBlockIndex* GetBestBlock();
|
||||
|
||||
std::set<std::pair<libzcash::SproutPaymentAddress, uint256>> GetSproutNullifiers(
|
||||
const std::set<libzcash::SproutPaymentAddress>& addresses);
|
||||
|
|
Loading…
Reference in New Issue