diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1046,6 +1046,11 @@ bool IsLocked() const; bool Lock(); + /** Interface to assert chain access and if successful lock it */ + std::unique_ptr LockChain() { + return m_chain ? m_chain->lock() : nullptr; + } + std::map mapWallet GUARDED_BY(cs_wallet); typedef std::multimap TxItems; @@ -1260,8 +1265,7 @@ void MarkDirty(); bool AddToWallet(const CWalletTx &wtxIn, bool fFlushOnClose = true); - void LoadToWallet(const CWalletTx &wtxIn) - EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + void LoadToWallet(CWalletTx &wtxIn) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void TransactionAddedToMempool(const CTransactionRef &tx) override; void BlockConnected(const CBlock &block, diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1234,7 +1234,19 @@ return true; } -void CWallet::LoadToWallet(const CWalletTx &wtxIn) { +void CWallet::LoadToWallet(CWalletTx &wtxIn) { + // If wallet doesn't have a chain (e.g wallet-tool), lock can't be taken. + auto locked_chain = LockChain(); + // If tx hasn't been reorged out of chain while wallet being shutdown + // change tx status to UNCONFIRMED and reset hashBlock/nIndex. + if (!wtxIn.m_confirm.hashBlock.IsNull()) { + if (locked_chain && + !locked_chain->getBlockHeight(wtxIn.m_confirm.hashBlock)) { + wtxIn.setUnconfirmed(); + wtxIn.m_confirm.hashBlock = BlockHash(); + wtxIn.m_confirm.nIndex = 0; + } + } const TxId &txid = wtxIn.GetId(); const auto &ins = mapWallet.emplace(txid, wtxIn); CWalletTx &wtx = ins.first->second; @@ -3696,6 +3708,11 @@ } DBErrors CWallet::LoadWallet(bool &fFirstRunRet) { + // Even if we don't use this lock in this function, we want to preserve + // lock order in LoadToWallet if query of chain state is needed to know + // tx status. If lock can't be taken (e.g wallet-tool), tx confirmation + // status may be not reliable. + auto locked_chain = LockChain(); LOCK(cs_wallet); fFirstRunRet = false; @@ -4667,6 +4684,11 @@ CWallet dummyWallet(chainParams, &chain, WalletLocation(), WalletDatabase::CreateDummy()); std::string backup_filename; + // Even if we don't use this lock in this function, we want to preserve + // lock order in LoadToWallet if query of chain state is needed to know + // tx status. If lock can't be taken, tx confirmation status may be not + // reliable. + auto locked_chain = dummyWallet.LockChain(); if (!WalletBatch::Recover( wallet_path, static_cast(&dummyWallet), WalletBatch::RecoverKeysOnlyFilter, backup_filename)) {