@@ -1110,22 +1110,14 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
11101110 bool fUpdated = false ;
11111111 if (!fInsertedNew )
11121112 {
1113- // Merge
1114- if (!wtxIn.hashUnset () && wtxIn.hashBlock != wtx.hashBlock )
1115- {
1116- wtx.hashBlock = wtxIn.hashBlock ;
1117- fUpdated = true ;
1118- }
1119- // If no longer abandoned, update
1120- if (wtxIn.hashBlock .IsNull () && wtx.isAbandoned ())
1121- {
1122- wtx.hashBlock = wtxIn.hashBlock ;
1123- fUpdated = true ;
1124- }
1125- if (wtxIn.nIndex != -1 && (wtxIn.nIndex != wtx.nIndex ))
1126- {
1127- wtx.nIndex = wtxIn.nIndex ;
1113+ if (wtxIn.m_confirm .status != wtx.m_confirm .status ) {
1114+ wtx.m_confirm .status = wtxIn.m_confirm .status ;
1115+ wtx.m_confirm .nIndex = wtxIn.m_confirm .nIndex ;
1116+ wtx.m_confirm .hashBlock = wtxIn.m_confirm .hashBlock ;
11281117 fUpdated = true ;
1118+ } else {
1119+ assert (wtx.m_confirm .nIndex == wtxIn.m_confirm .nIndex );
1120+ assert (wtx.m_confirm .hashBlock == wtxIn.m_confirm .hashBlock );
11291121 }
11301122 if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe )
11311123 {
@@ -1172,8 +1164,19 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
11721164 return true ;
11731165}
11741166
1175- void CWallet::LoadToWallet (const CWalletTx& wtxIn)
1167+ void CWallet::LoadToWallet (CWalletTx& wtxIn)
11761168{
1169+ // If wallet doesn't have a chain (e.g wallet-tool), lock can't be taken.
1170+ auto locked_chain = LockChain ();
1171+ // If tx hasn't been reorged out of chain while wallet being shutdown
1172+ // change tx status to UNCONFIRMED and reset hashBlock/nIndex.
1173+ if (!wtxIn.m_confirm .hashBlock .IsNull ()) {
1174+ if (locked_chain && !locked_chain->getBlockHeight (wtxIn.m_confirm .hashBlock )) {
1175+ wtxIn.setUnconfirmed ();
1176+ wtxIn.m_confirm .hashBlock = uint256 ();
1177+ wtxIn.m_confirm .nIndex = 0 ;
1178+ }
1179+ }
11771180 uint256 hash = wtxIn.GetHash ();
11781181 const auto & ins = mapWallet.emplace (hash, wtxIn);
11791182 CWalletTx& wtx = ins.first ->second ;
@@ -1186,14 +1189,14 @@ void CWallet::LoadToWallet(const CWalletTx& wtxIn)
11861189 auto it = mapWallet.find (txin.prevout .hash );
11871190 if (it != mapWallet.end ()) {
11881191 CWalletTx& prevtx = it->second ;
1189- if (prevtx.nIndex == - 1 && !prevtx. hashUnset ()) {
1190- MarkConflicted (prevtx.hashBlock , wtx.GetHash ());
1192+ if (prevtx.isConflicted ()) {
1193+ MarkConflicted (prevtx.m_confirm . hashBlock , wtx.GetHash ());
11911194 }
11921195 }
11931196 }
11941197}
11951198
1196- bool CWallet::AddToWalletIfInvolvingMe (const CTransactionRef& ptx, const uint256& block_hash, int posInBlock, bool fUpdate )
1199+ bool CWallet::AddToWalletIfInvolvingMe (const CTransactionRef& ptx, CWalletTx::Status status, const uint256& block_hash, int posInBlock, bool fUpdate )
11971200{
11981201 const CTransaction& tx = *ptx;
11991202 {
@@ -1240,9 +1243,9 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const uint256
12401243
12411244 CWalletTx wtx (this , ptx);
12421245
1243- // Get merkle branch if transaction was found in a block
1244- if (!block_hash. IsNull ())
1245- wtx.SetMerkleBranch ( block_hash, posInBlock);
1246+ // Block disconnection override an abandoned tx as unconfirmed
1247+ // which means user may have to call abandontransaction again
1248+ wtx.SetConf (status, block_hash, posInBlock);
12461249
12471250 return AddToWallet (wtx, false );
12481251 }
@@ -1302,7 +1305,7 @@ bool CWallet::AbandonTransaction(interfaces::Chain::Lock& locked_chain, const ui
13021305 if (currentconfirm == 0 && !wtx.isAbandoned ()) {
13031306 // If the orig tx was not in block/mempool, none of its spends can be in mempool
13041307 assert (!wtx.InMempool ());
1305- wtx.nIndex = - 1 ;
1308+ wtx.m_confirm . nIndex = 0 ;
13061309 wtx.setAbandoned ();
13071310 wtx.MarkDirty ();
13081311 batch.WriteTx (wtx);
@@ -1356,8 +1359,9 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
13561359 if (conflictconfirms < currentconfirm) {
13571360 // Block is 'more conflicted' than current confirm; update.
13581361 // Mark transaction as conflicted with this block.
1359- wtx.nIndex = -1 ;
1360- wtx.hashBlock = hashBlock;
1362+ wtx.m_confirm .nIndex = 0 ;
1363+ wtx.m_confirm .hashBlock = hashBlock;
1364+ wtx.setConflicted ();
13611365 wtx.MarkDirty ();
13621366 batch.WriteTx (wtx);
13631367 // Iterate over all its outputs, and mark transactions in the wallet that spend them conflicted too
@@ -1375,8 +1379,9 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
13751379 }
13761380}
13771381
1378- void CWallet::SyncTransaction (const CTransactionRef& ptx, const uint256& block_hash, int posInBlock, bool update_tx) {
1379- if (!AddToWalletIfInvolvingMe (ptx, block_hash, posInBlock, update_tx))
1382+ void CWallet::SyncTransaction (const CTransactionRef& ptx, CWalletTx::Status status, const uint256& block_hash, int posInBlock, bool update_tx)
1383+ {
1384+ if (!AddToWalletIfInvolvingMe (ptx, status, block_hash, posInBlock, update_tx))
13801385 return ; // Not one of ours
13811386
13821387 // If a transaction changes 'conflicted' state, that changes the balance
@@ -1388,7 +1393,7 @@ void CWallet::SyncTransaction(const CTransactionRef& ptx, const uint256& block_h
13881393void CWallet::TransactionAddedToMempool (const CTransactionRef& ptx) {
13891394 auto locked_chain = chain ().lock ();
13901395 LOCK (cs_wallet);
1391- SyncTransaction (ptx, {} /* block hash */ , 0 /* position in block */ );
1396+ SyncTransaction (ptx, CWalletTx::Status::UNCONFIRMED, {} /* block hash */ , 0 /* position in block */ );
13921397
13931398 auto it = mapWallet.find (ptx->GetHash ());
13941399 if (it != mapWallet.end ()) {
@@ -1408,22 +1413,14 @@ void CWallet::BlockConnected(const CBlock& block, const std::vector<CTransaction
14081413 const uint256& block_hash = block.GetHash ();
14091414 auto locked_chain = chain ().lock ();
14101415 LOCK (cs_wallet);
1411- // TODO: Temporarily ensure that mempool removals are notified before
1412- // connected transactions. This shouldn't matter, but the abandoned
1413- // state of transactions in our wallet is currently cleared when we
1414- // receive another notification and there is a race condition where
1415- // notification of a connected conflict might cause an outside process
1416- // to abandon a transaction and then have it inadvertently cleared by
1417- // the notification that the conflicted transaction was evicted.
14181416
1419- for (const CTransactionRef& ptx : vtxConflicted) {
1420- SyncTransaction (ptx, {} /* block hash */ , 0 /* position in block */ );
1421- TransactionRemovedFromMempool (ptx);
1422- }
14231417 for (size_t i = 0 ; i < block.vtx .size (); i++) {
1424- SyncTransaction (block.vtx [i], block_hash, i);
1418+ SyncTransaction (block.vtx [i], CWalletTx::Status::CONFIRMED, block_hash, i);
14251419 TransactionRemovedFromMempool (block.vtx [i]);
14261420 }
1421+ for (const CTransactionRef& ptx : vtxConflicted) {
1422+ TransactionRemovedFromMempool (ptx);
1423+ }
14271424
14281425 m_last_block_processed = block_hash;
14291426}
@@ -1432,8 +1429,12 @@ void CWallet::BlockDisconnected(const CBlock& block) {
14321429 auto locked_chain = chain ().lock ();
14331430 LOCK (cs_wallet);
14341431
1432+ // At block disconnection, this will change an abandoned transaction to
1433+ // be unconfirmed, whether or not the transaction is added back to the mempool.
1434+ // User may have to call abandontransaction again. It may be addressed in the
1435+ // future with a stickier abandoned state or even removing abandontransaction call.
14351436 for (const CTransactionRef& ptx : block.vtx ) {
1436- SyncTransaction (ptx, {} /* block hash */ , 0 /* position in block */ );
1437+ SyncTransaction (ptx, CWalletTx::Status::UNCONFIRMED, {} /* block hash */ , 0 /* position in block */ );
14371438 }
14381439}
14391440
@@ -2070,7 +2071,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
20702071 break ;
20712072 }
20722073 for (size_t posInBlock = 0 ; posInBlock < block.vtx .size (); ++posInBlock) {
2073- SyncTransaction (block.vtx [posInBlock], block_hash, posInBlock, fUpdate );
2074+ SyncTransaction (block.vtx [posInBlock], CWalletTx::Status::CONFIRMED, block_hash, posInBlock, fUpdate );
20742075 }
20752076 // scan succeeded, record block as most recent successfully scanned
20762077 result.last_scanned_block = block_hash;
@@ -3332,6 +3333,11 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
33323333
33333334DBErrors CWallet::LoadWallet (bool & fFirstRunRet )
33343335{
3336+ // Even if we don't use this lock in this function, we want to preserve
3337+ // lock order in LoadToWallet if query of chain state is needed to know
3338+ // tx status. If lock can't be taken (e.g wallet-tool), tx confirmation
3339+ // status may be not reliable.
3340+ auto locked_chain = LockChain ();
33353341 LOCK (cs_wallet);
33363342
33373343 fFirstRunRet = false ;
@@ -4042,7 +4048,7 @@ void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<C
40424048 for (const auto & entry : mapWallet) {
40434049 // iterate over all wallet transactions...
40444050 const CWalletTx &wtx = entry.second ;
4045- if (Optional<int > height = locked_chain.getBlockHeight (wtx.hashBlock )) {
4051+ if (Optional<int > height = locked_chain.getBlockHeight (wtx.m_confirm . hashBlock )) {
40464052 // ... which are already in a block
40474053 for (const CTxOut &txout : wtx.tx ->vout ) {
40484054 // iterate over all their outputs
@@ -4085,9 +4091,9 @@ void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<C
40854091unsigned int CWallet::ComputeTimeSmart (const CWalletTx& wtx) const
40864092{
40874093 unsigned int nTimeSmart = wtx.nTimeReceived ;
4088- if (!wtx.hashUnset ()) {
4094+ if (!wtx.isUnconfirmed () && !wtx. isAbandoned ()) {
40894095 int64_t blocktime;
4090- if (chain ().findBlock (wtx.hashBlock , nullptr /* block */ , &blocktime)) {
4096+ if (chain ().findBlock (wtx.m_confirm . hashBlock , nullptr /* block */ , &blocktime)) {
40914097 int64_t latestNow = wtx.nTimeReceived ;
40924098 int64_t latestEntry = 0 ;
40934099
@@ -4115,7 +4121,7 @@ unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx) const
41154121
41164122 nTimeSmart = std::max (latestEntry, std::min (blocktime, latestNow));
41174123 } else {
4118- WalletLogPrintf (" %s: found %s in block %s not in index\n " , __func__, wtx.GetHash ().ToString (), wtx.hashBlock .ToString ());
4124+ WalletLogPrintf (" %s: found %s in block %s not in index\n " , __func__, wtx.GetHash ().ToString (), wtx.m_confirm . hashBlock .ToString ());
41194125 }
41204126 }
41214127 return nTimeSmart;
@@ -4233,6 +4239,11 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b
42334239 // Recover readable keypairs:
42344240 CWallet dummyWallet (&chain, WalletLocation (), WalletDatabase::CreateDummy ());
42354241 std::string backup_filename;
4242+ // Even if we don't use this lock in this function, we want to preserve
4243+ // lock order in LoadToWallet if query of chain state is needed to know
4244+ // tx status. If lock can't be taken, tx confirmation status may be not
4245+ // reliable.
4246+ auto locked_chain = dummyWallet.LockChain ();
42364247 if (!WalletBatch::Recover (wallet_path, (void *)&dummyWallet, WalletBatch::RecoverKeysOnlyFilter, backup_filename)) {
42374248 return false ;
42384249 }
@@ -4627,21 +4638,23 @@ CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn)
46274638 m_pre_split = false ;
46284639}
46294640
4630- void CWalletTx::SetMerkleBranch ( const uint256& block_hash, int posInBlock)
4641+ void CWalletTx::SetConf (Status status, const uint256& block_hash, int posInBlock)
46314642{
4643+ // Update tx status
4644+ m_confirm.status = status;
4645+
46324646 // Update the tx's hashBlock
4633- hashBlock = block_hash;
4647+ m_confirm. hashBlock = block_hash;
46344648
46354649 // set the position of the transaction in the block
4636- nIndex = posInBlock;
4650+ m_confirm. nIndex = posInBlock;
46374651}
46384652
46394653int CWalletTx::GetDepthInMainChain (interfaces::Chain::Lock& locked_chain) const
46404654{
4641- if (hashUnset ())
4642- return 0 ;
4655+ if (isUnconfirmed () || isAbandoned ()) return 0 ;
46434656
4644- return locked_chain.getBlockDepth (hashBlock) * (nIndex == - 1 ? -1 : 1 );
4657+ return locked_chain.getBlockDepth (m_confirm. hashBlock ) * (isConflicted () ? -1 : 1 );
46454658}
46464659
46474660int CWalletTx::GetBlocksToMaturity (interfaces::Chain::Lock& locked_chain) const
0 commit comments