Skip to content

Commit 4702cad

Browse files
author
MarcoFalke
committed
Merge #17954: wallet: Remove calls to Chain::Lock methods
4897340 wallet: Avoid use of Chain::Lock in CWallet::GetKeyBirthTimes (Russell Yanofsky) e958ff9 wallet: Avoid use of Chain::Lock in CWallet::CreateTransaction (Russell Yanofsky) c0d07dc wallet: Avoid use of Chain::Lock in CWallet::ScanForWalletTransactions (Russell Yanofsky) 1be8ff2 wallet: Avoid use of Chain::Lock in rescanblockchain (Russell Yanofsky) 3cb85ac wallet refactor: Avoid use of Chain::Lock in CWallet::RescanFromTime (Russell Yanofsky) f7ba881 wallet: Avoid use of Chain::Lock in listsinceblock (Russell Yanofsky) bc96a9b wallet: Avoid use of Chain::Lock in importmulti (Russell Yanofsky) 25a9fcf wallet: Avoid use of Chain::Lock in importwallet and dumpwallet (Russell Yanofsky) c1694ce wallet: Avoid use of Chain::Lock in importprunedfunds (Russell Yanofsky) ade5f87 wallet refactor: Avoid use of Chain::Lock in qt wallettests (Russell Yanofsky) f6da44c wallet: Avoid use of Chain::Lock in tryGetTxStatus and tryGetBalances (Russell Yanofsky) bf30cd4 refactor: Add interfaces::FoundBlock class to selectively return block data (Russell Yanofsky) Pull request description: This is a set of changes updating wallet code to make fewer calls to `Chain::Lock` methods, so the `Chain::Lock` class will be easier to remove in #16426 with fewer code changes and small changes to behavior. ACKs for top commit: MarcoFalke: re-ACK 4897340, only change is fixing bug 📀 fjahr: re-ACK 4897340, reviewed rebase and changes since last review, built and ran tests locally ariard: Coce Review ACK 4897340, only changes are one suggested by last review on more accurate variable naming, human-readable output, args comments in `findCommonAncestor` Tree-SHA512: cfd2f559f976b6faaa032794c40c9659191d5597b013abcb6c7968d36b2abb2b14d4e596f8ed8b9a077e96522365261299a241a939b3111eaf729ba0c3ef519b
2 parents 6110ae8 + 4897340 commit 4702cad

File tree

12 files changed

+409
-199
lines changed

12 files changed

+409
-199
lines changed

src/Makefile.test.include

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ BITCOIN_TESTS =\
201201
test/fs_tests.cpp \
202202
test/getarg_tests.cpp \
203203
test/hash_tests.cpp \
204+
test/interfaces_tests.cpp \
204205
test/key_io_tests.cpp \
205206
test/key_tests.cpp \
206207
test/limitedmap_tests.cpp \

src/interfaces/chain.cpp

Lines changed: 73 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,21 @@
3838
namespace interfaces {
3939
namespace {
4040

41+
bool FillBlock(const CBlockIndex* index, const FoundBlock& block, UniqueLock<RecursiveMutex>& lock)
42+
{
43+
if (!index) return false;
44+
if (block.m_hash) *block.m_hash = index->GetBlockHash();
45+
if (block.m_height) *block.m_height = index->nHeight;
46+
if (block.m_time) *block.m_time = index->GetBlockTime();
47+
if (block.m_max_time) *block.m_max_time = index->GetBlockTimeMax();
48+
if (block.m_mtp_time) *block.m_mtp_time = index->GetMedianTimePast();
49+
if (block.m_data) {
50+
REVERSE_LOCK(lock);
51+
if (!ReadBlockFromDisk(*block.m_data, index, Params().GetConsensus())) block.m_data->SetNull();
52+
}
53+
return true;
54+
}
55+
4156
class LockImpl : public Chain::Lock, public UniqueLock<RecursiveMutex>
4257
{
4358
Optional<int> getHeight() override
@@ -65,20 +80,6 @@ class LockImpl : public Chain::Lock, public UniqueLock<RecursiveMutex>
6580
assert(block != nullptr);
6681
return block->GetBlockHash();
6782
}
68-
int64_t getBlockTime(int height) override
69-
{
70-
LockAssertion lock(::cs_main);
71-
CBlockIndex* block = ::ChainActive()[height];
72-
assert(block != nullptr);
73-
return block->GetBlockTime();
74-
}
75-
int64_t getBlockMedianTimePast(int height) override
76-
{
77-
LockAssertion lock(::cs_main);
78-
CBlockIndex* block = ::ChainActive()[height];
79-
assert(block != nullptr);
80-
return block->GetMedianTimePast();
81-
}
8283
bool haveBlockOnDisk(int height) override
8384
{
8485
LockAssertion lock(::cs_main);
@@ -95,20 +96,6 @@ class LockImpl : public Chain::Lock, public UniqueLock<RecursiveMutex>
9596
}
9697
return nullopt;
9798
}
98-
Optional<int> findPruned(int start_height, Optional<int> stop_height) override
99-
{
100-
LockAssertion lock(::cs_main);
101-
if (::fPruneMode) {
102-
CBlockIndex* block = stop_height ? ::ChainActive()[*stop_height] : ::ChainActive().Tip();
103-
while (block && block->nHeight >= start_height) {
104-
if ((block->nStatus & BLOCK_HAVE_DATA) == 0) {
105-
return block->nHeight;
106-
}
107-
block = block->pprev;
108-
}
109-
}
110-
return nullopt;
111-
}
11299
Optional<int> findFork(const uint256& hash, Optional<int>* height) override
113100
{
114101
LockAssertion lock(::cs_main);
@@ -247,33 +234,74 @@ class ChainImpl : public Chain
247234
std::unique_ptr<Chain::Lock> result = std::move(lock); // Temporary to avoid CWG 1579
248235
return result;
249236
}
250-
bool findBlock(const uint256& hash, CBlock* block, int64_t* time, int64_t* time_max) override
237+
bool findBlock(const uint256& hash, const FoundBlock& block) override
251238
{
252-
CBlockIndex* index;
253-
{
254-
LOCK(cs_main);
255-
index = LookupBlockIndex(hash);
256-
if (!index) {
257-
return false;
258-
}
259-
if (time) {
260-
*time = index->GetBlockTime();
261-
}
262-
if (time_max) {
263-
*time_max = index->GetBlockTimeMax();
239+
WAIT_LOCK(cs_main, lock);
240+
return FillBlock(LookupBlockIndex(hash), block, lock);
241+
}
242+
bool findFirstBlockWithTimeAndHeight(int64_t min_time, int min_height, const FoundBlock& block) override
243+
{
244+
WAIT_LOCK(cs_main, lock);
245+
return FillBlock(ChainActive().FindEarliestAtLeast(min_time, min_height), block, lock);
246+
}
247+
bool findNextBlock(const uint256& block_hash, int block_height, const FoundBlock& next, bool* reorg) override {
248+
WAIT_LOCK(cs_main, lock);
249+
CBlockIndex* block = ChainActive()[block_height];
250+
if (block && block->GetBlockHash() != block_hash) block = nullptr;
251+
if (reorg) *reorg = !block;
252+
return FillBlock(block ? ChainActive()[block_height + 1] : nullptr, next, lock);
253+
}
254+
bool findAncestorByHeight(const uint256& block_hash, int ancestor_height, const FoundBlock& ancestor_out) override
255+
{
256+
WAIT_LOCK(cs_main, lock);
257+
if (const CBlockIndex* block = LookupBlockIndex(block_hash)) {
258+
if (const CBlockIndex* ancestor = block->GetAncestor(ancestor_height)) {
259+
return FillBlock(ancestor, ancestor_out, lock);
264260
}
265261
}
266-
if (block && !ReadBlockFromDisk(*block, index, Params().GetConsensus())) {
267-
block->SetNull();
268-
}
269-
return true;
262+
return FillBlock(nullptr, ancestor_out, lock);
263+
}
264+
bool findAncestorByHash(const uint256& block_hash, const uint256& ancestor_hash, const FoundBlock& ancestor_out) override
265+
{
266+
WAIT_LOCK(cs_main, lock);
267+
const CBlockIndex* block = LookupBlockIndex(block_hash);
268+
const CBlockIndex* ancestor = LookupBlockIndex(ancestor_hash);
269+
if (block && ancestor && block->GetAncestor(ancestor->nHeight) != ancestor) ancestor = nullptr;
270+
return FillBlock(ancestor, ancestor_out, lock);
271+
}
272+
bool findCommonAncestor(const uint256& block_hash1, const uint256& block_hash2, const FoundBlock& ancestor_out, const FoundBlock& block1_out, const FoundBlock& block2_out) override
273+
{
274+
WAIT_LOCK(cs_main, lock);
275+
const CBlockIndex* block1 = LookupBlockIndex(block_hash1);
276+
const CBlockIndex* block2 = LookupBlockIndex(block_hash2);
277+
const CBlockIndex* ancestor = block1 && block2 ? LastCommonAncestor(block1, block2) : nullptr;
278+
return FillBlock(ancestor, ancestor_out, lock) & FillBlock(block1, block1_out, lock) & FillBlock(block2, block2_out, lock);
270279
}
271280
void findCoins(std::map<COutPoint, Coin>& coins) override { return FindCoins(m_node, coins); }
272281
double guessVerificationProgress(const uint256& block_hash) override
273282
{
274283
LOCK(cs_main);
275284
return GuessVerificationProgress(Params().TxData(), LookupBlockIndex(block_hash));
276285
}
286+
bool hasBlocks(const uint256& block_hash, int min_height, Optional<int> max_height) override
287+
{
288+
// hasBlocks returns true if all ancestors of block_hash in specified
289+
// range have block data (are not pruned), false if any ancestors in
290+
// specified range are missing data.
291+
//
292+
// For simplicity and robustness, min_height and max_height are only
293+
// used to limit the range, and passing min_height that's too low or
294+
// max_height that's too high will not crash or change the result.
295+
LOCK(::cs_main);
296+
if (CBlockIndex* block = LookupBlockIndex(block_hash)) {
297+
if (max_height && block->nHeight >= *max_height) block = block->GetAncestor(*max_height);
298+
for (; block->nStatus & BLOCK_HAVE_DATA; block = block->pprev) {
299+
// Check pprev to not segfault if min_height is too low
300+
if (block->nHeight <= min_height || !block->pprev) return true;
301+
}
302+
}
303+
return false;
304+
}
277305
RBFTransactionState isRBFOptIn(const CTransaction& tx) override
278306
{
279307
LOCK(::mempool.cs);

src/interfaces/chain.h

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,27 @@ namespace interfaces {
3030
class Handler;
3131
class Wallet;
3232

33+
//! Helper for findBlock to selectively return pieces of block data.
34+
class FoundBlock
35+
{
36+
public:
37+
FoundBlock& hash(uint256& hash) { m_hash = &hash; return *this; }
38+
FoundBlock& height(int& height) { m_height = &height; return *this; }
39+
FoundBlock& time(int64_t& time) { m_time = &time; return *this; }
40+
FoundBlock& maxTime(int64_t& max_time) { m_max_time = &max_time; return *this; }
41+
FoundBlock& mtpTime(int64_t& mtp_time) { m_mtp_time = &mtp_time; return *this; }
42+
//! Read block data from disk. If the block exists but doesn't have data
43+
//! (for example due to pruning), the CBlock variable will be set to null.
44+
FoundBlock& data(CBlock& data) { m_data = &data; return *this; }
45+
46+
uint256* m_hash = nullptr;
47+
int* m_height = nullptr;
48+
int64_t* m_time = nullptr;
49+
int64_t* m_max_time = nullptr;
50+
int64_t* m_mtp_time = nullptr;
51+
CBlock* m_data = nullptr;
52+
};
53+
3354
//! Interface giving clients (wallet processes, maybe other analysis tools in
3455
//! the future) ability to access to the chain state, receive notifications,
3556
//! estimate fees, and submit transactions.
@@ -79,13 +100,6 @@ class Chain
79100
//! Get block hash. Height must be valid or this function will abort.
80101
virtual uint256 getBlockHash(int height) = 0;
81102

82-
//! Get block time. Height must be valid or this function will abort.
83-
virtual int64_t getBlockTime(int height) = 0;
84-
85-
//! Get block median time past. Height must be valid or this function
86-
//! will abort.
87-
virtual int64_t getBlockMedianTimePast(int height) = 0;
88-
89103
//! Check that the block is available on disk (i.e. has not been
90104
//! pruned), and contains transactions.
91105
virtual bool haveBlockOnDisk(int height) = 0;
@@ -97,10 +111,6 @@ class Chain
97111
//! (to avoid the cost of a second lookup in case this information is needed.)
98112
virtual Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height, uint256* hash) = 0;
99113

100-
//! Return height of last block in the specified range which is pruned, or
101-
//! nullopt if no block in the range is pruned. Range is inclusive.
102-
virtual Optional<int> findPruned(int start_height = 0, Optional<int> stop_height = nullopt) = 0;
103-
104114
//! Return height of the specified block if it is on the chain, otherwise
105115
//! return the height of the highest block on chain that's an ancestor
106116
//! of the specified block, or nullopt if there is no common ancestor.
@@ -127,14 +137,36 @@ class Chain
127137

128138
//! Return whether node has the block and optionally return block metadata
129139
//! or contents.
130-
//!
131-
//! If a block pointer is provided to retrieve the block contents, and the
132-
//! block exists but doesn't have data (for example due to pruning), the
133-
//! block will be empty and all fields set to null.
134-
virtual bool findBlock(const uint256& hash,
135-
CBlock* block = nullptr,
136-
int64_t* time = nullptr,
137-
int64_t* max_time = nullptr) = 0;
140+
virtual bool findBlock(const uint256& hash, const FoundBlock& block={}) = 0;
141+
142+
//! Find first block in the chain with timestamp >= the given time
143+
//! and height >= than the given height, return false if there is no block
144+
//! with a high enough timestamp and height. Optionally return block
145+
//! information.
146+
virtual bool findFirstBlockWithTimeAndHeight(int64_t min_time, int min_height, const FoundBlock& block={}) = 0;
147+
148+
//! Find next block if block is part of current chain. Also flag if
149+
//! there was a reorg and the specified block hash is no longer in the
150+
//! current chain, and optionally return block information.
151+
virtual bool findNextBlock(const uint256& block_hash, int block_height, const FoundBlock& next={}, bool* reorg=nullptr) = 0;
152+
153+
//! Find ancestor of block at specified height and optionally return
154+
//! ancestor information.
155+
virtual bool findAncestorByHeight(const uint256& block_hash, int ancestor_height, const FoundBlock& ancestor_out={}) = 0;
156+
157+
//! Return whether block descends from a specified ancestor, and
158+
//! optionally return ancestor information.
159+
virtual bool findAncestorByHash(const uint256& block_hash,
160+
const uint256& ancestor_hash,
161+
const FoundBlock& ancestor_out={}) = 0;
162+
163+
//! Find most recent common ancestor between two blocks and optionally
164+
//! return block information.
165+
virtual bool findCommonAncestor(const uint256& block_hash1,
166+
const uint256& block_hash2,
167+
const FoundBlock& ancestor_out={},
168+
const FoundBlock& block1_out={},
169+
const FoundBlock& block2_out={}) = 0;
138170

139171
//! Look up unspent output information. Returns coins in the mempool and in
140172
//! the current chain UTXO set. Iterates through all the keys in the map and
@@ -145,6 +177,11 @@ class Chain
145177
//! the specified block hash are verified.
146178
virtual double guessVerificationProgress(const uint256& block_hash) = 0;
147179

180+
//! Return true if data is available for all blocks in the specified range
181+
//! of blocks. This checks all blocks that are ancestors of block_hash in
182+
//! the height range from min_height to max_height, inclusive.
183+
virtual bool hasBlocks(const uint256& block_hash, int min_height = 0, Optional<int> max_height = {}) = 0;
184+
148185
//! Check if transaction is RBF opt in.
149186
virtual RBFTransactionState isRBFOptIn(const CTransaction& tx) = 0;
150187

src/interfaces/wallet.cpp

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <sync.h>
1515
#include <ui_interface.h>
1616
#include <uint256.h>
17+
#include <util/check.h>
1718
#include <util/system.h>
1819
#include <wallet/feebumper.h>
1920
#include <wallet/fees.h>
@@ -62,7 +63,7 @@ WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx)
6263
WalletTxStatus MakeWalletTxStatus(interfaces::Chain::Lock& locked_chain, const CWalletTx& wtx)
6364
{
6465
WalletTxStatus result;
65-
result.block_height = locked_chain.getBlockHeight(wtx.m_confirm.hashBlock).get_value_or(std::numeric_limits<int>::max());
66+
result.block_height = wtx.m_confirm.block_height > 0 ? wtx.m_confirm.block_height : std::numeric_limits<int>::max();
6667
result.blocks_to_maturity = wtx.GetBlocksToMaturity();
6768
result.depth_in_main_chain = wtx.GetDepthInMainChain();
6869
result.time_received = wtx.nTimeReceived;
@@ -318,13 +319,9 @@ class WalletImpl : public Wallet
318319
if (mi == m_wallet->mapWallet.end()) {
319320
return false;
320321
}
321-
if (Optional<int> height = locked_chain->getHeight()) {
322-
num_blocks = *height;
323-
block_time = locked_chain->getBlockTime(*height);
324-
} else {
325-
num_blocks = -1;
326-
block_time = -1;
327-
}
322+
num_blocks = m_wallet->GetLastBlockHeight();
323+
block_time = -1;
324+
CHECK_NONFATAL(m_wallet->chain().findBlock(m_wallet->GetLastBlockHash(), FoundBlock().time(block_time)));
328325
tx_status = MakeWalletTxStatus(*locked_chain, mi->second);
329326
return true;
330327
}
@@ -373,12 +370,12 @@ class WalletImpl : public Wallet
373370
{
374371
auto locked_chain = m_wallet->chain().lock(true /* try_lock */);
375372
if (!locked_chain) return false;
376-
num_blocks = locked_chain->getHeight().get_value_or(-1);
377-
if (!force && num_blocks == cached_num_blocks) return false;
378373
TRY_LOCK(m_wallet->cs_wallet, locked_wallet);
379374
if (!locked_wallet) {
380375
return false;
381376
}
377+
num_blocks = m_wallet->GetLastBlockHeight();
378+
if (!force && num_blocks == cached_num_blocks) return false;
382379
balances = getBalances();
383380
return true;
384381
}

src/qt/test/wallettests.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,9 @@ void TestGUI(interfaces::Node& node)
152152
wallet->SetLastBlockProcessed(105, ::ChainActive().Tip()->GetBlockHash());
153153
}
154154
{
155-
auto locked_chain = wallet->chain().lock();
156-
LockAssertion lock(::cs_main);
157-
158155
WalletRescanReserver reserver(wallet.get());
159156
reserver.reserve();
160-
CWallet::ScanResult result = wallet->ScanForWalletTransactions(locked_chain->getBlockHash(0), {} /* stop_block */, reserver, true /* fUpdate */);
157+
CWallet::ScanResult result = wallet->ScanForWalletTransactions(Params().GetConsensus().hashGenesisBlock, 0 /* block height */, {} /* max height */, reserver, true /* fUpdate */);
161158
QCOMPARE(result.status, CWallet::ScanResult::SUCCESS);
162159
QCOMPARE(result.last_scanned_block, ::ChainActive().Tip()->GetBlockHash());
163160
QVERIFY(result.last_failed_block.IsNull());

src/sync.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ class SCOPED_LOCKABLE UniqueLock : public Base
210210
friend class reverse_lock;
211211
};
212212

213-
#define REVERSE_LOCK(g) decltype(g)::reverse_lock PASTE2(revlock, __COUNTER__)(g, #g, __FILE__, __LINE__)
213+
#define REVERSE_LOCK(g) typename std::decay<decltype(g)>::type::reverse_lock PASTE2(revlock, __COUNTER__)(g, #g, __FILE__, __LINE__)
214214

215215
template<typename MutexArg>
216216
using DebugLock = UniqueLock<typename std::remove_reference<typename std::remove_pointer<MutexArg>::type>::type>;

0 commit comments

Comments
 (0)