diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h --- a/src/interfaces/chain.h +++ b/src/interfaces/chain.h @@ -175,6 +175,14 @@ virtual bool findBlock(const BlockHash &hash, const FoundBlock &block = {}) = 0; + //! Find first block in the chain with timestamp >= the given time + //! and height >= than the given height, return false if there is no block + //! with a high enough timestamp and height. Optionally return block + //! information. + virtual bool + findFirstBlockWithTimeAndHeight(int64_t min_time, int min_height, + const FoundBlock &block = {}) = 0; + //! Find ancestor of block at specified height and optionally return //! ancestor information. virtual bool findAncestorByHeight(const BlockHash &block_hash, diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp --- a/src/interfaces/chain.cpp +++ b/src/interfaces/chain.cpp @@ -283,6 +283,13 @@ WAIT_LOCK(cs_main, lock); return FillBlock(LookupBlockIndex(hash), block, lock); } + bool findFirstBlockWithTimeAndHeight(int64_t min_time, int min_height, + const FoundBlock &block) override { + WAIT_LOCK(cs_main, lock); + return FillBlock( + ChainActive().FindEarliestAtLeast(min_time, min_height), block, + lock); + } bool findAncestorByHeight(const BlockHash &block_hash, int ancestor_height, const FoundBlock &ancestor_out) override { diff --git a/src/test/interfaces_tests.cpp b/src/test/interfaces_tests.cpp --- a/src/test/interfaces_tests.cpp +++ b/src/test/interfaces_tests.cpp @@ -53,6 +53,21 @@ BOOST_CHECK(!chain->findBlock(BlockHash(), FoundBlock())); } +BOOST_AUTO_TEST_CASE(findFirstBlockWithTimeAndHeight) { + auto chain = interfaces::MakeChain(m_node, Params()); + auto &active = ChainActive(); + BlockHash hash; + int height; + BOOST_CHECK(chain->findFirstBlockWithTimeAndHeight( + /* min_time= */ 0, /* min_height= */ 5, + FoundBlock().hash(hash).height(height))); + BOOST_CHECK_EQUAL(hash, active[5]->GetBlockHash()); + BOOST_CHECK_EQUAL(height, 5); + BOOST_CHECK(!chain->findFirstBlockWithTimeAndHeight( + /* min_time= */ active.Tip()->GetBlockTimeMax() + 1, + /* min_height= */ 0)); +} + BOOST_AUTO_TEST_CASE(findAncestorByHeight) { auto chain = interfaces::MakeChain(m_node, Params()); auto &active = ChainActive(); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1708,19 +1708,17 @@ // Find starting block. May be null if nCreateTime is greater than the // highest blockchain timestamp, in which case there is nothing that needs // to be scanned. + int start_height = 0; BlockHash start_block; - { - auto locked_chain = chain().lock(); - const Optional start_height = - locked_chain->findFirstBlockWithTimeAndHeight( - startTime - TIMESTAMP_WINDOW, 0, &start_block); - const Optional tip_height = locked_chain->getHeight(); - WalletLogPrintf( - "%s: Rescanning last %i blocks\n", __func__, - tip_height && start_height ? *tip_height - *start_height + 1 : 0); - } - - if (!start_block.IsNull()) { + bool start = chain().findFirstBlockWithTimeAndHeight( + startTime - TIMESTAMP_WINDOW, 0, + FoundBlock().hash(start_block).height(start_height)); + WalletLogPrintf("%s: Rescanning last %i blocks\n", __func__, + start ? WITH_LOCK(cs_wallet, return GetLastBlockHeight()) - + start_height + 1 + : 0); + + if (start) { // TODO: this should take into account failure by ScanResult::USER_ABORT ScanResult result = ScanForWalletTransactions(start_block, BlockHash(), reserver, update);