Modify wallet tx status if has been reorged out

Add a LockChain method to CWallet to know if we can lock or query
chain state safely.

At tx loading, we rely on chain to know if hashBlock of tx is still
in main chain. If not, we set its status to unconfirmed and reset
its hashBlock/nIndex.

If wallet loaded is the wallet-tool one, all wallet txn will
show up with a height of zero. It doesn't matter as status is not
used by wallet-tool.

We take lock prematurely in CWallet::LoadWallet and CWallet::Verify
to ensure that lock order is respected between cs_main an cs_wallet.
pull/764/head
Antoine Riard 5 years ago
parent 7e89994133
commit 40ede992d9

@ -1172,8 +1172,19 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
return true; 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 = uint256();
wtxIn.m_confirm.nIndex = 0;
}
}
uint256 hash = wtxIn.GetHash(); uint256 hash = wtxIn.GetHash();
const auto& ins = mapWallet.emplace(hash, wtxIn); const auto& ins = mapWallet.emplace(hash, wtxIn);
CWalletTx& wtx = ins.first->second; CWalletTx& wtx = ins.first->second;
@ -3330,6 +3341,11 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
DBErrors CWallet::LoadWallet(bool& fFirstRunRet) 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); LOCK(cs_wallet);
fFirstRunRet = false; fFirstRunRet = false;
@ -4231,6 +4247,11 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b
// Recover readable keypairs: // Recover readable keypairs:
CWallet dummyWallet(&chain, WalletLocation(), WalletDatabase::CreateDummy()); CWallet dummyWallet(&chain, WalletLocation(), WalletDatabase::CreateDummy());
std::string backup_filename; 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, (void *)&dummyWallet, WalletBatch::RecoverKeysOnlyFilter, backup_filename)) { if (!WalletBatch::Recover(wallet_path, (void *)&dummyWallet, WalletBatch::RecoverKeysOnlyFilter, backup_filename)) {
return false; return false;
} }

@ -946,6 +946,9 @@ public:
bool IsLocked() const; bool IsLocked() const;
bool Lock(); bool Lock();
/** Interface to assert chain access and if successful lock it */
std::unique_ptr<interfaces::Chain::Lock> LockChain() { return m_chain ? m_chain->lock() : nullptr; }
std::map<uint256, CWalletTx> mapWallet GUARDED_BY(cs_wallet); std::map<uint256, CWalletTx> mapWallet GUARDED_BY(cs_wallet);
typedef std::multimap<int64_t, CWalletTx*> TxItems; typedef std::multimap<int64_t, CWalletTx*> TxItems;
@ -1091,7 +1094,7 @@ public:
void MarkDirty(); void MarkDirty();
bool AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose=true); 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 TransactionAddedToMempool(const CTransactionRef& tx) override;
void BlockConnected(const CBlock& block, const std::vector<CTransactionRef>& vtxConflicted) override; void BlockConnected(const CBlock& block, const std::vector<CTransactionRef>& vtxConflicted) override;
void BlockDisconnected(const CBlock& block) override; void BlockDisconnected(const CBlock& block) override;

Loading…
Cancel
Save