refactor: combine Chain::findFirstBlockWithTime/findFirstBlockWithTimeAndHeight
As suggested in #14711, pass height to CChain::FindEarliestAtLeast to simplify Chain interface by combining findFirstBlockWithTime and findFirstBlockWithTimeAndHeight into one Extend findearliestatleast_edge_test in consequence
This commit is contained in:
parent
8a8b03ecd2
commit
765c0b364d
7 changed files with 42 additions and 52 deletions
|
@ -59,10 +59,11 @@ const CBlockIndex *CChain::FindFork(const CBlockIndex *pindex) const {
|
||||||
return pindex;
|
return pindex;
|
||||||
}
|
}
|
||||||
|
|
||||||
CBlockIndex* CChain::FindEarliestAtLeast(int64_t nTime) const
|
CBlockIndex* CChain::FindEarliestAtLeast(int64_t nTime, int height) const
|
||||||
{
|
{
|
||||||
std::vector<CBlockIndex*>::const_iterator lower = std::lower_bound(vChain.begin(), vChain.end(), nTime,
|
std::pair<int64_t, int> blockparams = std::make_pair(nTime, height);
|
||||||
[](CBlockIndex* pBlock, const int64_t& time) -> bool { return pBlock->GetBlockTimeMax() < time; });
|
std::vector<CBlockIndex*>::const_iterator lower = std::lower_bound(vChain.begin(), vChain.end(), blockparams,
|
||||||
|
[](CBlockIndex* pBlock, const std::pair<int64_t, int>& blockparams) -> bool { return pBlock->GetBlockTimeMax() < blockparams.first || pBlock->nHeight < blockparams.second; });
|
||||||
return (lower == vChain.end() ? nullptr : *lower);
|
return (lower == vChain.end() ? nullptr : *lower);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -465,8 +465,8 @@ public:
|
||||||
/** Find the last common block between this chain and a block index entry. */
|
/** Find the last common block between this chain and a block index entry. */
|
||||||
const CBlockIndex *FindFork(const CBlockIndex *pindex) const;
|
const CBlockIndex *FindFork(const CBlockIndex *pindex) const;
|
||||||
|
|
||||||
/** Find the earliest block with timestamp equal or greater than the given. */
|
/** Find the earliest block with timestamp equal or greater than the given time and height equal or greater than the given height. */
|
||||||
CBlockIndex* FindEarliestAtLeast(int64_t nTime) const;
|
CBlockIndex* FindEarliestAtLeast(int64_t nTime, int height) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // BITCOIN_CHAIN_H
|
#endif // BITCOIN_CHAIN_H
|
||||||
|
|
|
@ -83,29 +83,15 @@ class LockImpl : public Chain::Lock
|
||||||
CBlockIndex* block = ::chainActive[height];
|
CBlockIndex* block = ::chainActive[height];
|
||||||
return block && ((block->nStatus & BLOCK_HAVE_DATA) != 0) && block->nTx > 0;
|
return block && ((block->nStatus & BLOCK_HAVE_DATA) != 0) && block->nTx > 0;
|
||||||
}
|
}
|
||||||
Optional<int> findFirstBlockWithTime(int64_t time, uint256* hash) override
|
Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height, uint256* hash) override
|
||||||
{
|
{
|
||||||
CBlockIndex* block = ::chainActive.FindEarliestAtLeast(time);
|
CBlockIndex* block = ::chainActive.FindEarliestAtLeast(time, height);
|
||||||
if (block) {
|
if (block) {
|
||||||
if (hash) *hash = block->GetBlockHash();
|
if (hash) *hash = block->GetBlockHash();
|
||||||
return block->nHeight;
|
return block->nHeight;
|
||||||
}
|
}
|
||||||
return nullopt;
|
return nullopt;
|
||||||
}
|
}
|
||||||
Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height) override
|
|
||||||
{
|
|
||||||
// TODO: Could update CChain::FindEarliestAtLeast() to take a height
|
|
||||||
// parameter and use it with std::lower_bound() to make this
|
|
||||||
// implementation more efficient and allow combining
|
|
||||||
// findFirstBlockWithTime and findFirstBlockWithTimeAndHeight into one
|
|
||||||
// method.
|
|
||||||
for (CBlockIndex* block = ::chainActive[height]; block; block = ::chainActive.Next(block)) {
|
|
||||||
if (block->GetBlockTime() >= time) {
|
|
||||||
return block->nHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullopt;
|
|
||||||
}
|
|
||||||
Optional<int> findPruned(int start_height, Optional<int> stop_height) override
|
Optional<int> findPruned(int start_height, Optional<int> stop_height) override
|
||||||
{
|
{
|
||||||
if (::fPruneMode) {
|
if (::fPruneMode) {
|
||||||
|
|
|
@ -100,21 +100,12 @@ public:
|
||||||
//! pruned), and contains transactions.
|
//! pruned), and contains transactions.
|
||||||
virtual bool haveBlockOnDisk(int height) = 0;
|
virtual bool haveBlockOnDisk(int height) = 0;
|
||||||
|
|
||||||
//! Return height of the first block in the chain with timestamp equal
|
|
||||||
//! or greater than the given time, or nullopt if there is no block with
|
|
||||||
//! a high enough timestamp. Also return the block hash as an optional
|
|
||||||
//! output parameter (to avoid the cost of a second lookup in case this
|
|
||||||
//! information is needed.)
|
|
||||||
virtual Optional<int> findFirstBlockWithTime(int64_t time, uint256* hash) = 0;
|
|
||||||
|
|
||||||
//! Return height of the first block in the chain with timestamp equal
|
//! Return height of the first block in the chain with timestamp equal
|
||||||
//! or greater than the given time and height equal or greater than the
|
//! or greater than the given time and height equal or greater than the
|
||||||
//! given height, or nullopt if there is no such block.
|
//! given height, or nullopt if there is no block with a high enough
|
||||||
//!
|
//! timestamp and height. Also return the block hash as an optional output parameter
|
||||||
//! Calling this with height 0 is equivalent to calling
|
//! (to avoid the cost of a second lookup in case this information is needed.)
|
||||||
//! findFirstBlockWithTime, but less efficient because it requires a
|
virtual Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height, uint256* hash) = 0;
|
||||||
//! linear instead of a binary search.
|
|
||||||
virtual Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height) = 0;
|
|
||||||
|
|
||||||
//! Return height of last block in the specified range which is pruned, or
|
//! Return height of last block in the specified range which is pruned, or
|
||||||
//! nullopt if no block in the range is pruned. Range is inclusive.
|
//! nullopt if no block in the range is pruned. Range is inclusive.
|
||||||
|
|
|
@ -1007,7 +1007,7 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
|
||||||
// too low to be a block time (corresponds to timestamp from Sep 2001).
|
// too low to be a block time (corresponds to timestamp from Sep 2001).
|
||||||
if (heightParam > 1000000000) {
|
if (heightParam > 1000000000) {
|
||||||
// Add a 2 hour buffer to include blocks which might have had old timestamps
|
// Add a 2 hour buffer to include blocks which might have had old timestamps
|
||||||
CBlockIndex* pindex = chainActive.FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW);
|
CBlockIndex* pindex = chainActive.FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW, 0);
|
||||||
if (!pindex) {
|
if (!pindex) {
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Could not find block with at least the specified timestamp.");
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Could not find block with at least the specified timestamp.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,7 +136,7 @@ BOOST_AUTO_TEST_CASE(findearliestatleast_test)
|
||||||
// Pick a random element in vBlocksMain.
|
// Pick a random element in vBlocksMain.
|
||||||
int r = InsecureRandRange(vBlocksMain.size());
|
int r = InsecureRandRange(vBlocksMain.size());
|
||||||
int64_t test_time = vBlocksMain[r].nTime;
|
int64_t test_time = vBlocksMain[r].nTime;
|
||||||
CBlockIndex *ret = chain.FindEarliestAtLeast(test_time);
|
CBlockIndex* ret = chain.FindEarliestAtLeast(test_time, 0);
|
||||||
BOOST_CHECK(ret->nTimeMax >= test_time);
|
BOOST_CHECK(ret->nTimeMax >= test_time);
|
||||||
BOOST_CHECK((ret->pprev==nullptr) || ret->pprev->nTimeMax < test_time);
|
BOOST_CHECK((ret->pprev==nullptr) || ret->pprev->nTimeMax < test_time);
|
||||||
BOOST_CHECK(vBlocksMain[r].GetAncestor(ret->nHeight) == ret);
|
BOOST_CHECK(vBlocksMain[r].GetAncestor(ret->nHeight) == ret);
|
||||||
|
@ -158,22 +158,34 @@ BOOST_AUTO_TEST_CASE(findearliestatleast_edge_test)
|
||||||
CChain chain;
|
CChain chain;
|
||||||
chain.SetTip(&blocks.back());
|
chain.SetTip(&blocks.back());
|
||||||
|
|
||||||
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(50)->nHeight, 0);
|
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(50, 0)->nHeight, 0);
|
||||||
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(100)->nHeight, 0);
|
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(100, 0)->nHeight, 0);
|
||||||
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(150)->nHeight, 3);
|
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(150, 0)->nHeight, 3);
|
||||||
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(200)->nHeight, 3);
|
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(200, 0)->nHeight, 3);
|
||||||
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(250)->nHeight, 6);
|
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(250, 0)->nHeight, 6);
|
||||||
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(300)->nHeight, 6);
|
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(300, 0)->nHeight, 6);
|
||||||
BOOST_CHECK(!chain.FindEarliestAtLeast(350));
|
BOOST_CHECK(!chain.FindEarliestAtLeast(350, 0));
|
||||||
|
|
||||||
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0)->nHeight, 0);
|
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0, 0)->nHeight, 0);
|
||||||
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(-1)->nHeight, 0);
|
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(-1, 0)->nHeight, 0);
|
||||||
|
|
||||||
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(std::numeric_limits<int64_t>::min())->nHeight, 0);
|
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(std::numeric_limits<int64_t>::min(), 0)->nHeight, 0);
|
||||||
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(-int64_t(std::numeric_limits<unsigned int>::max()) - 1)->nHeight, 0);
|
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(-int64_t(std::numeric_limits<unsigned int>::max()) - 1, 0)->nHeight, 0);
|
||||||
BOOST_CHECK(!chain.FindEarliestAtLeast(std::numeric_limits<int64_t>::max()));
|
BOOST_CHECK(!chain.FindEarliestAtLeast(std::numeric_limits<int64_t>::max(), 0));
|
||||||
BOOST_CHECK(!chain.FindEarliestAtLeast(std::numeric_limits<unsigned int>::max()));
|
BOOST_CHECK(!chain.FindEarliestAtLeast(std::numeric_limits<unsigned int>::max(), 0));
|
||||||
BOOST_CHECK(!chain.FindEarliestAtLeast(int64_t(std::numeric_limits<unsigned int>::max()) + 1));
|
BOOST_CHECK(!chain.FindEarliestAtLeast(int64_t(std::numeric_limits<unsigned int>::max()) + 1, 0));
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0, -1)->nHeight, 0);
|
||||||
|
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0, 0)->nHeight, 0);
|
||||||
|
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0, 3)->nHeight, 3);
|
||||||
|
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0, 8)->nHeight, 8);
|
||||||
|
BOOST_CHECK(!chain.FindEarliestAtLeast(0, 9));
|
||||||
|
|
||||||
|
CBlockIndex* ret1 = chain.FindEarliestAtLeast(100, 2);
|
||||||
|
BOOST_CHECK(ret1->nTimeMax >= 100 && ret1->nHeight == 2);
|
||||||
|
BOOST_CHECK(!chain.FindEarliestAtLeast(300, 9));
|
||||||
|
CBlockIndex* ret2 = chain.FindEarliestAtLeast(200, 4);
|
||||||
|
BOOST_CHECK(ret2->nTimeMax >= 200 && ret2->nHeight == 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
|
@ -1722,7 +1722,7 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
|
||||||
uint256 start_block;
|
uint256 start_block;
|
||||||
{
|
{
|
||||||
auto locked_chain = chain().lock();
|
auto locked_chain = chain().lock();
|
||||||
const Optional<int> start_height = locked_chain->findFirstBlockWithTime(startTime - TIMESTAMP_WINDOW, &start_block);
|
const Optional<int> start_height = locked_chain->findFirstBlockWithTimeAndHeight(startTime - TIMESTAMP_WINDOW, 0, &start_block);
|
||||||
const Optional<int> tip_height = locked_chain->getHeight();
|
const Optional<int> tip_height = locked_chain->getHeight();
|
||||||
WalletLogPrintf("%s: Rescanning last %i blocks\n", __func__, tip_height && start_height ? *tip_height - *start_height + 1 : 0);
|
WalletLogPrintf("%s: Rescanning last %i blocks\n", __func__, tip_height && start_height ? *tip_height - *start_height + 1 : 0);
|
||||||
}
|
}
|
||||||
|
@ -4338,7 +4338,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
|
||||||
// No need to read and scan block if block was created before
|
// No need to read and scan block if block was created before
|
||||||
// our wallet birthday (as adjusted for block time variability)
|
// our wallet birthday (as adjusted for block time variability)
|
||||||
if (walletInstance->nTimeFirstKey) {
|
if (walletInstance->nTimeFirstKey) {
|
||||||
if (Optional<int> first_block = locked_chain->findFirstBlockWithTimeAndHeight(walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW, rescan_height)) {
|
if (Optional<int> first_block = locked_chain->findFirstBlockWithTimeAndHeight(walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW, rescan_height, nullptr)) {
|
||||||
rescan_height = *first_block;
|
rescan_height = *first_block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue