@@ -1110,22 +1110,14 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
1110
1110
bool fUpdated = false ;
1111
1111
if (!fInsertedNew )
1112
1112
{
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 ;
1128
1117
fUpdated = true ;
1118
+ } else {
1119
+ assert (wtx.m_confirm .nIndex == wtxIn.m_confirm .nIndex );
1120
+ assert (wtx.m_confirm .hashBlock == wtxIn.m_confirm .hashBlock );
1129
1121
}
1130
1122
if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe )
1131
1123
{
@@ -1172,8 +1164,19 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
1172
1164
return true ;
1173
1165
}
1174
1166
1175
- void CWallet::LoadToWallet (const CWalletTx& wtxIn)
1167
+ void CWallet::LoadToWallet (CWalletTx& wtxIn)
1176
1168
{
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
+ }
1177
1180
uint256 hash = wtxIn.GetHash ();
1178
1181
const auto & ins = mapWallet.emplace (hash, wtxIn);
1179
1182
CWalletTx& wtx = ins.first ->second ;
@@ -1186,14 +1189,14 @@ void CWallet::LoadToWallet(const CWalletTx& wtxIn)
1186
1189
auto it = mapWallet.find (txin.prevout .hash );
1187
1190
if (it != mapWallet.end ()) {
1188
1191
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 ());
1191
1194
}
1192
1195
}
1193
1196
}
1194
1197
}
1195
1198
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 )
1197
1200
{
1198
1201
const CTransaction& tx = *ptx;
1199
1202
{
@@ -1240,9 +1243,9 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const uint256
1240
1243
1241
1244
CWalletTx wtx (this , ptx);
1242
1245
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);
1246
1249
1247
1250
return AddToWallet (wtx, false );
1248
1251
}
@@ -1302,7 +1305,7 @@ bool CWallet::AbandonTransaction(interfaces::Chain::Lock& locked_chain, const ui
1302
1305
if (currentconfirm == 0 && !wtx.isAbandoned ()) {
1303
1306
// If the orig tx was not in block/mempool, none of its spends can be in mempool
1304
1307
assert (!wtx.InMempool ());
1305
- wtx.nIndex = - 1 ;
1308
+ wtx.m_confirm . nIndex = 0 ;
1306
1309
wtx.setAbandoned ();
1307
1310
wtx.MarkDirty ();
1308
1311
batch.WriteTx (wtx);
@@ -1356,8 +1359,9 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
1356
1359
if (conflictconfirms < currentconfirm) {
1357
1360
// Block is 'more conflicted' than current confirm; update.
1358
1361
// 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 ();
1361
1365
wtx.MarkDirty ();
1362
1366
batch.WriteTx (wtx);
1363
1367
// 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)
1375
1379
}
1376
1380
}
1377
1381
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))
1380
1385
return ; // Not one of ours
1381
1386
1382
1387
// If a transaction changes 'conflicted' state, that changes the balance
@@ -1388,7 +1393,7 @@ void CWallet::SyncTransaction(const CTransactionRef& ptx, const uint256& block_h
1388
1393
void CWallet::TransactionAddedToMempool (const CTransactionRef& ptx) {
1389
1394
auto locked_chain = chain ().lock ();
1390
1395
LOCK (cs_wallet);
1391
- SyncTransaction (ptx, {} /* block hash */ , 0 /* position in block */ );
1396
+ SyncTransaction (ptx, CWalletTx::Status::UNCONFIRMED, {} /* block hash */ , 0 /* position in block */ );
1392
1397
1393
1398
auto it = mapWallet.find (ptx->GetHash ());
1394
1399
if (it != mapWallet.end ()) {
@@ -1408,22 +1413,14 @@ void CWallet::BlockConnected(const CBlock& block, const std::vector<CTransaction
1408
1413
const uint256& block_hash = block.GetHash ();
1409
1414
auto locked_chain = chain ().lock ();
1410
1415
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.
1418
1416
1419
- for (const CTransactionRef& ptx : vtxConflicted) {
1420
- SyncTransaction (ptx, {} /* block hash */ , 0 /* position in block */ );
1421
- TransactionRemovedFromMempool (ptx);
1422
- }
1423
1417
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);
1425
1419
TransactionRemovedFromMempool (block.vtx [i]);
1426
1420
}
1421
+ for (const CTransactionRef& ptx : vtxConflicted) {
1422
+ TransactionRemovedFromMempool (ptx);
1423
+ }
1427
1424
1428
1425
m_last_block_processed = block_hash;
1429
1426
}
@@ -1432,8 +1429,12 @@ void CWallet::BlockDisconnected(const CBlock& block) {
1432
1429
auto locked_chain = chain ().lock ();
1433
1430
LOCK (cs_wallet);
1434
1431
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.
1435
1436
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 */ );
1437
1438
}
1438
1439
}
1439
1440
@@ -2070,7 +2071,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
2070
2071
break ;
2071
2072
}
2072
2073
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 );
2074
2075
}
2075
2076
// scan succeeded, record block as most recent successfully scanned
2076
2077
result.last_scanned_block = block_hash;
@@ -3332,6 +3333,11 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
3332
3333
3333
3334
DBErrors CWallet::LoadWallet (bool & fFirstRunRet )
3334
3335
{
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 ();
3335
3341
LOCK (cs_wallet);
3336
3342
3337
3343
fFirstRunRet = false ;
@@ -4042,7 +4048,7 @@ void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<C
4042
4048
for (const auto & entry : mapWallet) {
4043
4049
// iterate over all wallet transactions...
4044
4050
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 )) {
4046
4052
// ... which are already in a block
4047
4053
for (const CTxOut &txout : wtx.tx ->vout ) {
4048
4054
// iterate over all their outputs
@@ -4085,9 +4091,9 @@ void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<C
4085
4091
unsigned int CWallet::ComputeTimeSmart (const CWalletTx& wtx) const
4086
4092
{
4087
4093
unsigned int nTimeSmart = wtx.nTimeReceived ;
4088
- if (!wtx.hashUnset ()) {
4094
+ if (!wtx.isUnconfirmed () && !wtx. isAbandoned ()) {
4089
4095
int64_t blocktime;
4090
- if (chain ().findBlock (wtx.hashBlock , nullptr /* block */ , &blocktime)) {
4096
+ if (chain ().findBlock (wtx.m_confirm . hashBlock , nullptr /* block */ , &blocktime)) {
4091
4097
int64_t latestNow = wtx.nTimeReceived ;
4092
4098
int64_t latestEntry = 0 ;
4093
4099
@@ -4115,7 +4121,7 @@ unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx) const
4115
4121
4116
4122
nTimeSmart = std::max (latestEntry, std::min (blocktime, latestNow));
4117
4123
} 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 ());
4119
4125
}
4120
4126
}
4121
4127
return nTimeSmart;
@@ -4233,6 +4239,11 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b
4233
4239
// Recover readable keypairs:
4234
4240
CWallet dummyWallet (&chain, WalletLocation (), WalletDatabase::CreateDummy ());
4235
4241
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 ();
4236
4247
if (!WalletBatch::Recover (wallet_path, (void *)&dummyWallet, WalletBatch::RecoverKeysOnlyFilter, backup_filename)) {
4237
4248
return false ;
4238
4249
}
@@ -4627,21 +4638,23 @@ CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn)
4627
4638
m_pre_split = false ;
4628
4639
}
4629
4640
4630
- void CWalletTx::SetMerkleBranch ( const uint256& block_hash, int posInBlock)
4641
+ void CWalletTx::SetConf (Status status, const uint256& block_hash, int posInBlock)
4631
4642
{
4643
+ // Update tx status
4644
+ m_confirm.status = status;
4645
+
4632
4646
// Update the tx's hashBlock
4633
- hashBlock = block_hash;
4647
+ m_confirm. hashBlock = block_hash;
4634
4648
4635
4649
// set the position of the transaction in the block
4636
- nIndex = posInBlock;
4650
+ m_confirm. nIndex = posInBlock;
4637
4651
}
4638
4652
4639
4653
int CWalletTx::GetDepthInMainChain (interfaces::Chain::Lock& locked_chain) const
4640
4654
{
4641
- if (hashUnset ())
4642
- return 0 ;
4655
+ if (isUnconfirmed () || isAbandoned ()) return 0 ;
4643
4656
4644
- return locked_chain.getBlockDepth (hashBlock) * (nIndex == - 1 ? -1 : 1 );
4657
+ return locked_chain.getBlockDepth (m_confirm. hashBlock ) * (isConflicted () ? -1 : 1 );
4645
4658
}
4646
4659
4647
4660
int CWalletTx::GetBlocksToMaturity (interfaces::Chain::Lock& locked_chain) const
0 commit comments