diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -121,6 +121,7 @@ wallet->LoadWallet(firstRun); { auto spk_man = wallet->GetLegacyScriptPubKeyMan(); + auto locked_chain = wallet->chain().lock(); LOCK(wallet->cs_wallet); AssertLockHeld(spk_man->cs_wallet); wallet->SetAddressBook( @@ -128,6 +129,8 @@ wallet->m_default_address_type), "", "receive"); spk_man->AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey()); + wallet->SetLastBlockProcessed(105, + ::ChainActive().Tip()->GetBlockHash()); } { auto locked_chain = wallet->chain().lock(); diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -50,6 +50,11 @@ { CWallet wallet(Params(), chain.get(), WalletLocation(), WalletDatabase::CreateDummy()); + { + LOCK(wallet.cs_wallet); + wallet.SetLastBlockProcessed(::ChainActive().Height(), + ::ChainActive().Tip()->GetBlockHash()); + } AddKey(wallet, coinbaseKey); WalletRescanReserver reserver(&wallet); reserver.reserve(); @@ -67,6 +72,11 @@ { CWallet wallet(Params(), chain.get(), WalletLocation(), WalletDatabase::CreateDummy()); + { + LOCK(wallet.cs_wallet); + wallet.SetLastBlockProcessed(::ChainActive().Height(), + ::ChainActive().Tip()->GetBlockHash()); + } AddKey(wallet, coinbaseKey); WalletRescanReserver reserver(&wallet); reserver.reserve(); @@ -88,6 +98,11 @@ { CWallet wallet(Params(), chain.get(), WalletLocation(), WalletDatabase::CreateDummy()); + { + LOCK(wallet.cs_wallet); + wallet.SetLastBlockProcessed(::ChainActive().Height(), + ::ChainActive().Tip()->GetBlockHash()); + } AddKey(wallet, coinbaseKey); WalletRescanReserver reserver(&wallet); reserver.reserve(); @@ -108,6 +123,11 @@ { CWallet wallet(Params(), chain.get(), WalletLocation(), WalletDatabase::CreateDummy()); + { + LOCK(wallet.cs_wallet); + wallet.SetLastBlockProcessed(::ChainActive().Height(), + ::ChainActive().Tip()->GetBlockHash()); + } AddKey(wallet, coinbaseKey); WalletRescanReserver reserver(&wallet); reserver.reserve(); @@ -284,6 +304,8 @@ LockAssertion lock(::cs_main); LOCK(wallet.cs_wallet); AssertLockHeld(spk_man->cs_wallet); + wallet.SetLastBlockProcessed(::ChainActive().Height(), + ::ChainActive().Tip()->GetBlockHash()); wtx.SetConf(CWalletTx::Status::CONFIRMED, ::ChainActive().Tip()->GetBlockHash(), 0); @@ -465,6 +487,12 @@ wallet = std::make_unique(Params(), m_chain.get(), WalletLocation(), WalletDatabase::CreateMock()); + { + LOCK(wallet->cs_wallet); + wallet->SetLastBlockProcessed( + ::ChainActive().Height(), + ::ChainActive().Tip()->GetBlockHash()); + } bool firstRun; wallet->LoadWallet(firstRun); AddKey(*wallet, coinbaseKey); @@ -506,6 +534,8 @@ LOCK(cs_main); LOCK(wallet->cs_wallet); + wallet->SetLastBlockProcessed(wallet->GetLastBlockHeight() + 1, + ::ChainActive().Tip()->GetBlockHash()); auto it = wallet->mapWallet.find(tx->GetId()); BOOST_CHECK(it != wallet->mapWallet.end()); it->second.SetConf(CWalletTx::Status::CONFIRMED, diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -750,12 +750,19 @@ * The following is used to keep track of how far behind the wallet is * from the chain sync, and to allow clients to block on us being caught up. * - * Note that this is *not* how far we've processed, we may need some rescan - * to have seen all transactions in the chain, but is only used to track - * live BlockConnected callbacks. + * Processed hash is a pointer on node's tip and doesn't imply that the + * wallet has scanned sequentially all blocks up to this one. */ BlockHash m_last_block_processed GUARDED_BY(cs_wallet); + /* Height of last block processed is used by wallet to know depth of + * transactions without relying on Chain interface beyond asynchronous + * updates. For safety, we initialize it to -1. Height is a pointer on + * node's tip and doesn't imply that the wallet has scanned sequentially all + * blocks up to this one. + */ + int m_last_block_processed_height GUARDED_BY(cs_wallet) = -1; + public: const CChainParams &chainParams; /* @@ -1363,6 +1370,20 @@ WalletBatch *& encrypted_batch GUARDED_BY(cs_wallet) = m_spk_man->encrypted_batch; using CryptedKeyMap = LegacyScriptPubKeyMan::CryptedKeyMap; + + /** Get last block processed height */ + int GetLastBlockHeight() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { + AssertLockHeld(cs_wallet); + assert(m_last_block_processed_height >= 0); + return m_last_block_processed_height; + }; + /** Set last block processed height, currently only use in unit test */ + void SetLastBlockProcessed(int block_height, BlockHash block_hash) + EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { + AssertLockHeld(cs_wallet); + m_last_block_processed_height = block_height; + m_last_block_processed = block_hash; + }; }; /** diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1142,6 +1142,8 @@ auto locked_chain = chain().lock(); LOCK(cs_wallet); + m_last_block_processed_height = height; + m_last_block_processed = block_hash; for (size_t i = 0; i < block.vtx.size(); i++) { SyncTransaction(block.vtx[i], CWalletTx::Status::CONFIRMED, block_hash, i); @@ -1150,8 +1152,6 @@ for (const CTransactionRef &ptx : vtxConflicted) { TransactionRemovedFromMempool(ptx); } - - m_last_block_processed = block_hash; } void CWallet::BlockDisconnected(const CBlock &block, int height) { @@ -1163,6 +1163,8 @@ // mempool. User may have to call abandontransaction again. It may be // addressed in the future with a stickier abandoned state or even removing // abandontransaction call. + m_last_block_processed_height = height - 1; + m_last_block_processed = block.hashPrevBlock; for (const CTransactionRef &ptx : block.vtx) { SyncTransaction(ptx, CWalletTx::Status::UNCONFIRMED, BlockHash() /* block hash */, @@ -4222,8 +4224,10 @@ if (tip_height) { walletInstance->m_last_block_processed = locked_chain->getBlockHash(*tip_height); + walletInstance->m_last_block_processed_height = *tip_height; } else { walletInstance->m_last_block_processed.SetNull(); + walletInstance->m_last_block_processed_height = -1; } if (tip_height && *tip_height != rescan_height) {