Skip to content

Commit 5971d38

Browse files
author
Antoine Riard
committed
Add block_height field in struct Confirmation
At wallet loading, we rely on chain state querying to retrieve height of txn, to do so we ensure that lock order is respected between cs_main and cs_wallet. If wallet loaded is the wallet-tool one, all wallet txn will show up with a height of zero. It doesn't matter as confirmation height is not used by wallet-tool. Reorder arguments and document Confirmation calls to avoid ambiguity. Fixes nits left from #16624
1 parent 9700fcb commit 5971d38

File tree

4 files changed

+43
-26
lines changed

4 files changed

+43
-26
lines changed

src/wallet/rpcdump.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -364,10 +364,12 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
364364
std::vector<uint256> vMatch;
365365
std::vector<unsigned int> vIndex;
366366
unsigned int txnIndex = 0;
367+
Optional<int> height;
367368
if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) == merkleBlock.header.hashMerkleRoot) {
368369

369370
auto locked_chain = pwallet->chain().lock();
370-
if (locked_chain->getBlockHeight(merkleBlock.header.GetHash()) == nullopt) {
371+
height = locked_chain->getBlockHeight(merkleBlock.header.GetHash());
372+
if (height == nullopt) {
371373
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
372374
}
373375

@@ -382,7 +384,7 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
382384
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Something wrong with merkleblock");
383385
}
384386

385-
CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, merkleBlock.header.GetHash(), txnIndex);
387+
CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, *height, merkleBlock.header.GetHash(), txnIndex);
386388
wtx.m_confirm = confirm;
387389

388390
auto locked_chain = pwallet->chain().lock();

src/wallet/test/wallet_tests.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
276276
AssertLockHeld(spk_man->cs_wallet);
277277
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
278278

279-
CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, ::ChainActive().Tip()->GetBlockHash(), 0);
279+
CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, ::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash(), 0);
280280
wtx.m_confirm = confirm;
281281

282282
// Call GetImmatureCredit() once before adding the key to the wallet to
@@ -318,7 +318,7 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64
318318
wallet.AddToWallet(wtx);
319319
}
320320
if (block) {
321-
CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, block->GetBlockHash(), 0);
321+
CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, block->nHeight, block->GetBlockHash(), 0);
322322
wtx.m_confirm = confirm;
323323
}
324324
wallet.AddToWallet(wtx);
@@ -499,7 +499,7 @@ class ListCoinsTestingSetup : public TestChain100Setup
499499
wallet->SetLastBlockProcessed(wallet->GetLastBlockHeight() + 1, ::ChainActive().Tip()->GetBlockHash());
500500
auto it = wallet->mapWallet.find(tx->GetHash());
501501
BOOST_CHECK(it != wallet->mapWallet.end());
502-
CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, ::ChainActive().Tip()->GetBlockHash(), 1);
502+
CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, ::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash(), 1);
503503
it->second.m_confirm = confirm;
504504
return it->second;
505505
}

src/wallet/wallet.cpp

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -766,10 +766,12 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
766766
wtx.m_confirm.status = wtxIn.m_confirm.status;
767767
wtx.m_confirm.nIndex = wtxIn.m_confirm.nIndex;
768768
wtx.m_confirm.hashBlock = wtxIn.m_confirm.hashBlock;
769+
wtx.m_confirm.block_height = wtxIn.m_confirm.block_height;
769770
fUpdated = true;
770771
} else {
771772
assert(wtx.m_confirm.nIndex == wtxIn.m_confirm.nIndex);
772773
assert(wtx.m_confirm.hashBlock == wtxIn.m_confirm.hashBlock);
774+
assert(wtx.m_confirm.block_height == wtxIn.m_confirm.block_height);
773775
}
774776
if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe)
775777
{
@@ -820,12 +822,22 @@ void CWallet::LoadToWallet(CWalletTx& wtxIn)
820822
{
821823
// If wallet doesn't have a chain (e.g wallet-tool), lock can't be taken.
822824
auto locked_chain = LockChain();
823-
// If tx hasn't been reorged out of chain while wallet being shutdown
824-
// change tx status to UNCONFIRMED and reset hashBlock/nIndex.
825-
if (!wtxIn.m_confirm.hashBlock.IsNull()) {
826-
if (locked_chain && !locked_chain->getBlockHeight(wtxIn.m_confirm.hashBlock)) {
825+
if (locked_chain) {
826+
Optional<int> block_height = locked_chain->getBlockHeight(wtxIn.m_confirm.hashBlock);
827+
if (block_height) {
828+
// Update cached block height variable since it not stored in the
829+
// serialized transaction.
830+
wtxIn.m_confirm.block_height = *block_height;
831+
} else if (wtxIn.isConflicted() || wtxIn.isConfirmed()) {
832+
// If tx block (or conflicting block) was reorged out of chain
833+
// while the wallet was shutdown, change tx status to UNCONFIRMED
834+
// and reset block height, hash, and index. ABANDONED tx don't have
835+
// associated blocks and don't need to be updated. The case where a
836+
// transaction was reorged out while online and then reconfirmed
837+
// while offline is covered by the rescan logic.
827838
wtxIn.setUnconfirmed();
828839
wtxIn.m_confirm.hashBlock = uint256();
840+
wtxIn.m_confirm.block_height = 0;
829841
wtxIn.m_confirm.nIndex = 0;
830842
}
831843
}
@@ -842,7 +854,7 @@ void CWallet::LoadToWallet(CWalletTx& wtxIn)
842854
if (it != mapWallet.end()) {
843855
CWalletTx& prevtx = it->second;
844856
if (prevtx.isConflicted()) {
845-
MarkConflicted(prevtx.m_confirm.hashBlock, wtx.GetHash());
857+
MarkConflicted(prevtx.m_confirm.hashBlock, prevtx.m_confirm.block_height, wtx.GetHash());
846858
}
847859
}
848860
}
@@ -860,7 +872,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, CWalletTx::Co
860872
while (range.first != range.second) {
861873
if (range.first->second != tx.GetHash()) {
862874
WalletLogPrintf("Transaction %s (in block %s) conflicts with wallet transaction %s (both spend %s:%i)\n", tx.GetHash().ToString(), confirm.hashBlock.ToString(), range.first->second.ToString(), range.first->first.hash.ToString(), range.first->first.n);
863-
MarkConflicted(confirm.hashBlock, range.first->second);
875+
MarkConflicted(confirm.hashBlock, confirm.block_height, range.first->second);
864876
}
865877
range.first++;
866878
}
@@ -948,7 +960,6 @@ bool CWallet::AbandonTransaction(interfaces::Chain::Lock& locked_chain, const ui
948960
if (currentconfirm == 0 && !wtx.isAbandoned()) {
949961
// If the orig tx was not in block/mempool, none of its spends can be in mempool
950962
assert(!wtx.InMempool());
951-
wtx.m_confirm.nIndex = 0;
952963
wtx.setAbandoned();
953964
wtx.MarkDirty();
954965
batch.WriteTx(wtx);
@@ -970,7 +981,7 @@ bool CWallet::AbandonTransaction(interfaces::Chain::Lock& locked_chain, const ui
970981
return true;
971982
}
972983

973-
void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
984+
void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, const uint256& hashTx)
974985
{
975986
auto locked_chain = chain().lock();
976987
LOCK(cs_wallet);
@@ -1004,6 +1015,7 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
10041015
// Mark transaction as conflicted with this block.
10051016
wtx.m_confirm.nIndex = 0;
10061017
wtx.m_confirm.hashBlock = hashBlock;
1018+
wtx.m_confirm.block_height = conflicting_height;
10071019
wtx.setConflicted();
10081020
wtx.MarkDirty();
10091021
batch.WriteTx(wtx);
@@ -1036,7 +1048,7 @@ void CWallet::SyncTransaction(const CTransactionRef& ptx, CWalletTx::Confirmatio
10361048
void CWallet::TransactionAddedToMempool(const CTransactionRef& ptx) {
10371049
auto locked_chain = chain().lock();
10381050
LOCK(cs_wallet);
1039-
CWalletTx::Confirmation confirm(CWalletTx::Status::UNCONFIRMED, {}, 0);
1051+
CWalletTx::Confirmation confirm(CWalletTx::Status::UNCONFIRMED, /* block_height */ 0, {}, /* nIndex */ 0);
10401052
SyncTransaction(ptx, confirm);
10411053

10421054
auto it = mapWallet.find(ptx->GetHash());
@@ -1061,10 +1073,10 @@ void CWallet::BlockConnected(const CBlock& block, const std::vector<CTransaction
10611073

10621074
m_last_block_processed_height = height;
10631075
m_last_block_processed = block_hash;
1064-
for (size_t i = 0; i < block.vtx.size(); i++) {
1065-
CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, m_last_block_processed, i);
1066-
SyncTransaction(block.vtx[i], confirm);
1067-
TransactionRemovedFromMempool(block.vtx[i]);
1076+
for (size_t index = 0; index < block.vtx.size(); index++) {
1077+
CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, height, block_hash, index);
1078+
SyncTransaction(block.vtx[index], confirm);
1079+
TransactionRemovedFromMempool(block.vtx[index]);
10681080
}
10691081
for (const CTransactionRef& ptx : vtxConflicted) {
10701082
TransactionRemovedFromMempool(ptx);
@@ -1083,7 +1095,7 @@ void CWallet::BlockDisconnected(const CBlock& block, int height)
10831095
m_last_block_processed_height = height - 1;
10841096
m_last_block_processed = block.hashPrevBlock;
10851097
for (const CTransactionRef& ptx : block.vtx) {
1086-
CWalletTx::Confirmation confirm(CWalletTx::Status::UNCONFIRMED, {}, 0);
1098+
CWalletTx::Confirmation confirm(CWalletTx::Status::UNCONFIRMED, /* block_height */ 0, {}, /* nIndex */ 0);
10871099
SyncTransaction(ptx, confirm);
10881100
}
10891101
}
@@ -1630,7 +1642,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
16301642
break;
16311643
}
16321644
for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
1633-
CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, block_hash, posInBlock);
1645+
CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, *block_height, block_hash, posInBlock);
16341646
SyncTransaction(block.vtx[posInBlock], confirm, fUpdate);
16351647
}
16361648
// scan succeeded, record block as most recent successfully scanned

src/wallet/wallet.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -356,15 +356,17 @@ class CWalletTx
356356
ABANDONED
357357
};
358358

359-
/* Confirmation includes tx status and a pair of {block hash/tx index in block} at which tx has been confirmed.
360-
* This pair is both 0 if tx hasn't confirmed yet. Meaning of these fields changes with CONFLICTED state
361-
* where they instead point to block hash and index of the deepest conflicting tx.
359+
/* Confirmation includes tx status and a triplet of {block height/block hash/tx index in block}
360+
* at which tx has been confirmed. All three are set to 0 if tx is unconfirmed or abandoned.
361+
* Meaning of these fields changes with CONFLICTED state where they instead point to block hash
362+
* and block height of the deepest conflicting tx.
362363
*/
363364
struct Confirmation {
364365
Status status;
366+
int block_height;
365367
uint256 hashBlock;
366368
int nIndex;
367-
Confirmation(Status s = UNCONFIRMED, uint256 h = uint256(), int i = 0) : status(s), hashBlock(h), nIndex(i) {}
369+
Confirmation(Status s = UNCONFIRMED, int b = 0, uint256 h = uint256(), int i = 0) : status(s), block_height(b), hashBlock(h), nIndex(i) {}
368370
};
369371

370372
Confirmation m_confirm;
@@ -407,7 +409,6 @@ class CWalletTx
407409
* compatibility (pre-commit 9ac63d6).
408410
*/
409411
if (serializedIndex == -1 && m_confirm.hashBlock == ABANDON_HASH) {
410-
m_confirm.hashBlock = uint256();
411412
setAbandoned();
412413
} else if (serializedIndex == -1) {
413414
setConflicted();
@@ -512,12 +513,14 @@ class CWalletTx
512513
{
513514
m_confirm.status = CWalletTx::ABANDONED;
514515
m_confirm.hashBlock = uint256();
516+
m_confirm.block_height = 0;
515517
m_confirm.nIndex = 0;
516518
}
517519
bool isConflicted() const { return m_confirm.status == CWalletTx::CONFLICTED; }
518520
void setConflicted() { m_confirm.status = CWalletTx::CONFLICTED; }
519521
bool isUnconfirmed() const { return m_confirm.status == CWalletTx::UNCONFIRMED; }
520522
void setUnconfirmed() { m_confirm.status = CWalletTx::UNCONFIRMED; }
523+
bool isConfirmed() const { return m_confirm.status == CWalletTx::CONFIRMED; }
521524
void setConfirmed() { m_confirm.status = CWalletTx::CONFIRMED; }
522525
const uint256& GetHash() const { return tx->GetHash(); }
523526
bool IsCoinBase() const { return tx->IsCoinBase(); }
@@ -644,7 +647,7 @@ class CWallet final : public WalletStorage, private interfaces::Chain::Notificat
644647
bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, CWalletTx::Confirmation confirm, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
645648

646649
/* Mark a transaction (and its in-wallet descendants) as conflicting with a particular block. */
647-
void MarkConflicted(const uint256& hashBlock, const uint256& hashTx);
650+
void MarkConflicted(const uint256& hashBlock, int conflicting_height, const uint256& hashTx);
648651

649652
/* Mark a transaction's inputs dirty, thus forcing the outputs to be recomputed */
650653
void MarkInputsDirty(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);

0 commit comments

Comments
 (0)