Merge bitcoin/bitcoin#28562: AssumeUTXO follow-ups

5d227a6862 rpc: Use Ensure(Any)Chainman in assumeutxo related RPCs (Fabian Jahr)
710e5db61b doc: Drop references to assumevalid in assumeutxo docs (Fabian Jahr)
1ff1c34656 test: Rename wait_until_helper to wait_until_helper_internal (Fabian Jahr)
a482f86779 chain: Rename HaveTxsDownloaded to HaveNumChainTxs (Fabian Jahr)
82e48d20f1 blockstorage: Let FlushChainstateBlockFile return true in case of missing cursor (Fabian Jahr)
73700fb554 validation, test: Improve and document nChainTx check for testability (Fabian Jahr)
2c9354facb doc: Add snapshot chainstate removal warning to reindexing documentation (Fabian Jahr)
4e915e926b test: Improvements of feature_assumeutxo (Fabian Jahr)
a47fbe7d49 doc: Add and edit some comments around assumeutxo (Fabian Jahr)
0a39b8cbd8 validation: remove unused mempool param in DetectSnapshotChainstate (Fabian Jahr)

Pull request description:

  Addressing what I consider to be non- or not-too-controversial comments from #27596.

  Let me know if I missed anything among the many comments that can be easily included here.

ACKs for top commit:
  ryanofsky:
    Code review ACK 5d227a6862. Just suggested doc change and new EnsureChainman RPC cleanup commit since last review.

Tree-SHA512: 6f7c762100e18f82946b881676db23e67da7dc3a8bf04e4999a183e90b4f150a0b1202bcb95920ba937a358867bbf2eca300bd84b9b1776c7c490410e707c267
This commit is contained in:
fanquake 2023-10-07 11:07:53 +01:00
commit 38f4b0d9d1
No known key found for this signature in database
GPG key ID: 2EEB9F5CC09526C1
18 changed files with 73 additions and 73 deletions

View file

@ -1,7 +1,7 @@
# assumeutxo
Assumeutxo is a feature that allows fast bootstrapping of a validating bitcoind
instance with a very similar security model to assumevalid.
instance.
The RPC commands `dumptxoutset` and `loadtxoutset` are used to
respectively generate and load UTXO snapshots. The utility script

View file

@ -12,7 +12,7 @@ RPC
`loadtxoutset` has been added, which allows loading a UTXO snapshot of the format
generated by `dumptxoutset`. Once this snapshot is loaded, its contents will be
deserialized into a second chainstate data structure, which is then used to sync to
the network's tip under a security model very much like `assumevalid`.
the network's tip.
Meanwhile, the original chainstate will complete the initial block download process in
the background, eventually validating up to the block that the snapshot is based upon.

View file

@ -280,10 +280,8 @@ public:
* Note that this will be true for the snapshot base block, if one is loaded (and
* all subsequent assumed-valid blocks) since its nChainTx value will have been set
* manually based on the related AssumeutxoData entry.
*
* TODO: potentially change the name of this based on the fact above.
*/
bool HaveTxsDownloaded() const { return nChainTx != 0; }
bool HaveNumChainTxs() const { return nChainTx != 0; }
NodeSeconds Time() const
{

View file

@ -462,8 +462,8 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-prune=<n>", strprintf("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex. "
"Warning: Reverting this setting requires re-downloading the entire blockchain. "
"(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >=%u = automatically prune block files to stay under the specified target size in MiB)", MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-reindex", "Rebuild chain state and block index from the blk*.dat files on disk. This will also rebuild active optional indexes.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-reindex-chainstate", "Rebuild chain state from the currently indexed blocks. When in pruning mode or if blocks on disk might be corrupted, use full -reindex instead.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-reindex", "If enabled, wipe chain state and block index, and rebuild them from blk*.dat files on disk. Also wipe and rebuild other optional indexes that are active. If an assumeutxo snapshot was loaded, its chainstate will be wiped as well. The snapshot can then be reloaded via RPC.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-reindex-chainstate", "If enabled, wipe chain state, and rebuild it from blk*.dat files on disk. If an assumeutxo snapshot was loaded, its chainstate will be wiped as well. The snapshot can then be reloaded via RPC.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-settings=<file>", strprintf("Specify path to dynamic settings data file. Can be disabled with -nosettings. File is written at runtime and not meant to be edited by users (use %s instead for custom settings). Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME, BITCOIN_SETTINGS_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#if HAVE_SYSTEM
argsman.AddArg("-startupnotify=<cmd>", "Execute command on startup.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);

View file

@ -495,7 +495,7 @@ public:
{
.height = 110,
.hash_serialized = AssumeutxoHash{uint256S("0x1ebbf5850204c0bdb15bf030f47c7fe91d45c44c712697e4509ba67adb01c618")},
.nChainTx = 110,
.nChainTx = 111,
.blockhash = uint256S("0x696e92821f65549c7ee134edceeeeaaa4105647a3c4fd9f298c0aec0ab50425c")
},
{

View file

@ -1448,7 +1448,7 @@ void PeerManagerImpl::FindNextBlocks(std::vector<const CBlockIndex*>& vBlocks, c
return;
}
if (pindex->nStatus & BLOCK_HAVE_DATA || (activeChain && activeChain->Contains(pindex))) {
if (activeChain && pindex->HaveTxsDownloaded())
if (activeChain && pindex->HaveNumChainTxs())
state->pindexLastCommonBlock = pindex;
} else if (!IsBlockRequested(pindex->GetBlockHash())) {
// The block is not already downloaded, and not yet in flight.
@ -1937,6 +1937,8 @@ void PeerManagerImpl::BlockConnected(
}
}
// The following task can be skipped since we don't maintain a mempool for
// the ibd/background chainstate.
if (role == ChainstateRole::BACKGROUND) {
return;
}
@ -2231,7 +2233,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
LOCK(cs_main);
const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(inv.hash);
if (pindex) {
if (pindex->HaveTxsDownloaded() && !pindex->IsValid(BLOCK_VALID_SCRIPTS) &&
if (pindex->HaveNumChainTxs() && !pindex->IsValid(BLOCK_VALID_SCRIPTS) &&
pindex->IsValid(BLOCK_VALID_TREE)) {
// If we have the block and all of its parents, but have not yet validated it,
// we might be in the middle of connecting it (ie in the unlock of cs_main

View file

@ -761,12 +761,14 @@ bool BlockManager::FlushChainstateBlockFile(int tip_height)
{
LOCK(cs_LastBlockFile);
auto& cursor = m_blockfile_cursors[BlockfileTypeForHeight(tip_height)];
// If the cursor does not exist, it means an assumeutxo snapshot is loaded,
// but no blocks past the snapshot height have been written yet, so there
// is no data associated with the chainstate, and it is safe not to flush.
if (cursor) {
// The cursor may not exist after a snapshot has been loaded but before any
// blocks have been downloaded.
return FlushBlockFile(cursor->file_num, /*fFinalize=*/false, /*finalize_undo=*/false);
}
return false;
// No need to log warnings in this case.
return true;
}
uint64_t BlockManager::CalculateCurrentUsage()

View file

@ -185,7 +185,7 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize
chainman.InitializeChainstate(options.mempool);
// Load a chain created from a UTXO snapshot, if any exist.
bool has_snapshot = chainman.DetectSnapshotChainstate(options.mempool);
bool has_snapshot = chainman.DetectSnapshotChainstate();
if (has_snapshot && (options.reindex || options.reindex_chainstate)) {
LogPrintf("[snapshot] deleting snapshot chainstate due to reindexing\n");

View file

@ -1455,7 +1455,7 @@ static RPCHelpMan getchaintips()
} else if (block->nStatus & BLOCK_FAILED_MASK) {
// This block or one of its ancestors is invalid.
status = "invalid";
} else if (!block->HaveTxsDownloaded()) {
} else if (!block->HaveNumChainTxs()) {
// This block cannot be connected because full block data for it or one of its parents is missing.
status = "headers-only";
} else if (block->IsValid(BLOCK_VALID_SCRIPTS)) {
@ -2707,7 +2707,7 @@ static RPCHelpMan loadtxoutset()
"Load the serialized UTXO set from disk.\n"
"Once this snapshot is loaded, its contents will be "
"deserialized into a second chainstate data structure, which is then used to sync to "
"the network's tip under a security model very much like `assumevalid`. "
"the network's tip. "
"Meanwhile, the original chainstate will complete the initial block download process in "
"the background, eventually validating up to the block that the snapshot is based upon.\n\n"
@ -2759,7 +2759,7 @@ static RPCHelpMan loadtxoutset()
LogPrintf("[snapshot] waiting to see blockheader %s in headers chain before snapshot activation\n",
base_blockhash.ToString());
ChainstateManager& chainman = *node.chainman;
ChainstateManager& chainman = EnsureChainman(node);
while (max_secs_to_wait_for_headers > 0) {
snapshot_start_block = WITH_LOCK(::cs_main,
@ -2831,8 +2831,7 @@ return RPCHelpMan{
LOCK(cs_main);
UniValue obj(UniValue::VOBJ);
NodeContext& node = EnsureAnyNodeContext(request.context);
ChainstateManager& chainman = *node.chainman;
ChainstateManager& chainman = EnsureAnyChainman(request.context);
auto make_chain_data = [&](const Chainstate& cs, bool validated) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
AssertLockHeld(::cs_main);

View file

@ -29,7 +29,7 @@ FUZZ_TARGET(chain)
(void)disk_block_index->GetBlockTimeMax();
(void)disk_block_index->GetMedianTimePast();
(void)disk_block_index->GetUndoPos();
(void)disk_block_index->HaveTxsDownloaded();
(void)disk_block_index->HaveNumChainTxs();
(void)disk_block_index->IsValid();
}

View file

@ -138,11 +138,11 @@ BOOST_AUTO_TEST_CASE(test_assumeutxo)
const auto out110 = *params->AssumeutxoForHeight(110);
BOOST_CHECK_EQUAL(out110.hash_serialized.ToString(), "1ebbf5850204c0bdb15bf030f47c7fe91d45c44c712697e4509ba67adb01c618");
BOOST_CHECK_EQUAL(out110.nChainTx, 110U);
BOOST_CHECK_EQUAL(out110.nChainTx, 111U);
const auto out110_2 = *params->AssumeutxoForBlockhash(uint256S("0x696e92821f65549c7ee134edceeeeaaa4105647a3c4fd9f298c0aec0ab50425c"));
BOOST_CHECK_EQUAL(out110_2.hash_serialized.ToString(), "1ebbf5850204c0bdb15bf030f47c7fe91d45c44c712697e4509ba67adb01c618");
BOOST_CHECK_EQUAL(out110_2.nChainTx, 110U);
BOOST_CHECK_EQUAL(out110_2.nChainTx, 111U);
}
BOOST_AUTO_TEST_SUITE_END()

View file

@ -68,8 +68,8 @@
#include <numeric>
#include <optional>
#include <string>
#include <utility>
#include <tuple>
#include <utility>
using kernel::CCoinsStats;
using kernel::CoinStatsHashType;
@ -2996,7 +2996,7 @@ CBlockIndex* Chainstate::FindMostWorkChain()
CBlockIndex *pindexTest = pindexNew;
bool fInvalidAncestor = false;
while (pindexTest && !m_chain.Contains(pindexTest)) {
assert(pindexTest->HaveTxsDownloaded() || pindexTest->nHeight == 0);
assert(pindexTest->HaveNumChainTxs() || pindexTest->nHeight == 0);
// Pruned nodes may have entries in setBlockIndexCandidates for
// which block files have been deleted. Remove those as candidates
@ -3351,7 +3351,7 @@ bool Chainstate::PreciousBlock(BlockValidationState& state, CBlockIndex* pindex)
// call preciousblock 2**31-1 times on the same set of tips...
m_chainman.nBlockReverseSequenceId--;
}
if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && pindex->HaveTxsDownloaded()) {
if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && pindex->HaveNumChainTxs()) {
setBlockIndexCandidates.insert(pindex);
PruneBlockIndexCandidates();
}
@ -3399,7 +3399,7 @@ bool Chainstate::InvalidateBlock(BlockValidationState& state, CBlockIndex* pinde
if (!m_chain.Contains(candidate) &&
!CBlockIndexWorkComparator()(candidate, pindex->pprev) &&
candidate->IsValid(BLOCK_VALID_TRANSACTIONS) &&
candidate->HaveTxsDownloaded()) {
candidate->HaveNumChainTxs()) {
candidate_blocks_by_work.insert(std::make_pair(candidate->nChainWork, candidate));
}
}
@ -3488,7 +3488,7 @@ bool Chainstate::InvalidateBlock(BlockValidationState& state, CBlockIndex* pinde
// Loop back over all block index entries and add any missing entries
// to setBlockIndexCandidates.
for (auto& [_, block_index] : m_blockman.m_block_index) {
if (block_index.IsValid(BLOCK_VALID_TRANSACTIONS) && block_index.HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(&block_index, m_chain.Tip())) {
if (block_index.IsValid(BLOCK_VALID_TRANSACTIONS) && block_index.HaveNumChainTxs() && !setBlockIndexCandidates.value_comp()(&block_index, m_chain.Tip())) {
setBlockIndexCandidates.insert(&block_index);
}
}
@ -3520,7 +3520,7 @@ void Chainstate::ResetBlockFailureFlags(CBlockIndex *pindex) {
if (!block_index.IsValid() && block_index.GetAncestor(nHeight) == pindex) {
block_index.nStatus &= ~BLOCK_FAILED_MASK;
m_blockman.m_dirty_blockindex.insert(&block_index);
if (block_index.IsValid(BLOCK_VALID_TRANSACTIONS) && block_index.HaveTxsDownloaded() && setBlockIndexCandidates.value_comp()(m_chain.Tip(), &block_index)) {
if (block_index.IsValid(BLOCK_VALID_TRANSACTIONS) && block_index.HaveNumChainTxs() && setBlockIndexCandidates.value_comp()(m_chain.Tip(), &block_index)) {
setBlockIndexCandidates.insert(&block_index);
}
if (&block_index == m_chainman.m_best_invalid) {
@ -3583,7 +3583,7 @@ void ChainstateManager::ReceivedBlockTransactions(const CBlock& block, CBlockInd
pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS);
m_blockman.m_dirty_blockindex.insert(pindexNew);
if (pindexNew->pprev == nullptr || pindexNew->pprev->HaveTxsDownloaded()) {
if (pindexNew->pprev == nullptr || pindexNew->pprev->HaveNumChainTxs()) {
// If pindexNew is the genesis block or all parents are BLOCK_VALID_TRANSACTIONS.
std::deque<CBlockIndex*> queue;
queue.push_back(pindexNew);
@ -4566,7 +4566,7 @@ bool ChainstateManager::LoadBlockIndex()
// here.
if (pindex == GetSnapshotBaseBlock() ||
(pindex->IsValid(BLOCK_VALID_TRANSACTIONS) &&
(pindex->HaveTxsDownloaded() || pindex->pprev == nullptr))) {
(pindex->HaveNumChainTxs() || pindex->pprev == nullptr))) {
for (Chainstate* chainstate : GetAll()) {
chainstate->TryAddBlockIndexCandidate(pindex);
@ -4844,10 +4844,14 @@ void ChainstateManager::CheckBlockIndex()
CBlockIndex* pindexFirstAssumeValid = nullptr; // Oldest ancestor of pindex which has BLOCK_ASSUMED_VALID
while (pindex != nullptr) {
nNodes++;
if (pindex->pprev && pindex->nTx > 0) {
// nChainTx should increase monotonically
assert(pindex->pprev->nChainTx <= pindex->nChainTx);
}
// Make sure nChainTx sum is correctly computed.
unsigned int prev_chain_tx = pindex->pprev ? pindex->pprev->nChainTx : 0;
assert((pindex->nChainTx == pindex->nTx + prev_chain_tx)
// For testing, allow transaction counts to be completely unset.
|| (pindex->nChainTx == 0 && pindex->nTx == 0)
// For testing, allow this nChainTx to be unset if previous is also unset.
|| (pindex->nChainTx == 0 && prev_chain_tx == 0 && pindex->pprev));
if (pindexFirstAssumeValid == nullptr && pindex->nStatus & BLOCK_ASSUMED_VALID) pindexFirstAssumeValid = pindex;
if (pindexFirstInvalid == nullptr && pindex->nStatus & BLOCK_FAILED_VALID) pindexFirstInvalid = pindex;
if (pindexFirstMissing == nullptr && !(pindex->nStatus & BLOCK_HAVE_DATA)) {
@ -4886,7 +4890,7 @@ void ChainstateManager::CheckBlockIndex()
}
}
}
if (!pindex->HaveTxsDownloaded()) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock)
if (!pindex->HaveNumChainTxs()) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock)
// VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or not pruning has occurred).
// HAVE_DATA is only equivalent to nTx > 0 (or VALID_TRANSACTIONS) if no pruning has occurred.
// Unless these indexes are assumed valid and pending block download on a
@ -4916,9 +4920,9 @@ void ChainstateManager::CheckBlockIndex()
// actually seen a block's transactions.
assert(((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS) == (pindex->nTx > 0)); // This is pruning-independent.
}
// All parents having had data (at some point) is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to HaveTxsDownloaded().
assert((pindexFirstNeverProcessed == nullptr) == pindex->HaveTxsDownloaded());
assert((pindexFirstNotTransactionsValid == nullptr) == pindex->HaveTxsDownloaded());
// All parents having had data (at some point) is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to HaveNumChainTxs().
assert((pindexFirstNeverProcessed == nullptr) == pindex->HaveNumChainTxs());
assert((pindexFirstNotTransactionsValid == nullptr) == pindex->HaveNumChainTxs());
assert(pindex->nHeight == nHeight); // nHeight must be consistent.
assert(pindex->pprev == nullptr || pindex->nChainWork >= pindex->pprev->nChainWork); // For every block except the genesis block, the chainwork must be larger than the parent's.
assert(nHeight < 2 || (pindex->pskip && (pindex->pskip->nHeight < nHeight))); // The pskip pointer must point back for all but the first 2 blocks.
@ -5367,7 +5371,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
// ActivateSnapshot(), but is done so that we avoid doing the long work of staging
// a snapshot that isn't actually usable.
if (WITH_LOCK(::cs_main, return !CBlockIndexWorkComparator()(ActiveTip(), snapshot_start_block))) {
LogPrintf("[snapshot] activation failed - height does not exceed active chainstate\n");
LogPrintf("[snapshot] activation failed - work does not exceed active chainstate\n");
return false;
}
@ -5768,7 +5772,7 @@ ChainstateManager::~ChainstateManager()
m_versionbitscache.Clear();
}
bool ChainstateManager::DetectSnapshotChainstate(CTxMemPool* mempool)
bool ChainstateManager::DetectSnapshotChainstate()
{
assert(!m_snapshot_chainstate);
std::optional<fs::path> path = node::FindSnapshotChainstateDir(m_options.datadir);

View file

@ -836,9 +836,10 @@ private:
//! Once this pointer is set to a corresponding chainstate, it will not
//! be reset until init.cpp:Shutdown().
//!
//! This is especially important when, e.g., calling ActivateBestChain()
//! on all chainstates because we are not able to hold ::cs_main going into
//! that call.
//! It is important for the pointer to not be deleted until shutdown,
//! because cs_main is not always held when the pointer is accessed, for
//! example when calling ActivateBestChain, so there's no way you could
//! prevent code from using the pointer while deleting it.
std::unique_ptr<Chainstate> m_ibd_chainstate GUARDED_BY(::cs_main);
//! A chainstate initialized on the basis of a UTXO snapshot. If this is
@ -847,17 +848,14 @@ private:
//! Once this pointer is set to a corresponding chainstate, it will not
//! be reset until init.cpp:Shutdown().
//!
//! This is especially important when, e.g., calling ActivateBestChain()
//! on all chainstates because we are not able to hold ::cs_main going into
//! that call.
//! It is important for the pointer to not be deleted until shutdown,
//! because cs_main is not always held when the pointer is accessed, for
//! example when calling ActivateBestChain, so there's no way you could
//! prevent code from using the pointer while deleting it.
std::unique_ptr<Chainstate> m_snapshot_chainstate GUARDED_BY(::cs_main);
//! Points to either the ibd or snapshot chainstate; indicates our
//! most-work chain.
//!
//! This is especially important when, e.g., calling ActivateBestChain()
//! on all chainstates because we are not able to hold ::cs_main going into
//! that call.
Chainstate* m_active_chainstate GUARDED_BY(::cs_main) {nullptr};
CBlockIndex* m_best_invalid GUARDED_BY(::cs_main){nullptr};
@ -1203,7 +1201,7 @@ public:
//! When starting up, search the datadir for a chainstate based on a UTXO
//! snapshot that is in the process of being validated.
bool DetectSnapshotChainstate(CTxMemPool* mempool) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
bool DetectSnapshotChainstate() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
void ResetChainstates() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);

View file

@ -36,7 +36,7 @@ Interesting starting states could be loading a snapshot when the current chain t
"""
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, wait_until_helper
from test_framework.util import assert_equal
START_HEIGHT = 199
SNAPSHOT_BASE_HEIGHT = 299
@ -80,16 +80,13 @@ class AssumeutxoTest(BitcoinTestFramework):
self.sync_blocks()
def no_sync():
pass
# Generate a series of blocks that `n0` will have in the snapshot,
# but that n1 doesn't yet see. In order for the snapshot to activate,
# though, we have to ferry over the new headers to n1 so that it
# isn't waiting forever to see the header of the snapshot's base block
# while disconnected from n0.
for i in range(100):
self.generate(n0, nblocks=1, sync_fun=no_sync)
self.generate(n0, nblocks=1, sync_fun=self.no_op)
newblock = n0.getblock(n0.getbestblockhash(), 0)
# make n1 aware of the new header, but don't give it the block.
@ -116,7 +113,7 @@ class AssumeutxoTest(BitcoinTestFramework):
# Mine more blocks on top of the snapshot that n1 hasn't yet seen. This
# will allow us to test n1's sync-to-tip on top of a snapshot.
self.generate(n0, nblocks=100, sync_fun=no_sync)
self.generate(n0, nblocks=100, sync_fun=self.no_op)
assert_equal(n0.getblockcount(), FINAL_HEIGHT)
assert_equal(n1.getblockcount(), START_HEIGHT)
@ -162,11 +159,11 @@ class AssumeutxoTest(BitcoinTestFramework):
self.connect_nodes(0, 1)
self.log.info(f"Ensuring snapshot chain syncs to tip. ({FINAL_HEIGHT})")
wait_until_helper(lambda: n1.getchainstates()['chainstates'][-1]['blocks'] == FINAL_HEIGHT)
self.wait_until(lambda: n1.getchainstates()['chainstates'][-1]['blocks'] == FINAL_HEIGHT)
self.sync_blocks(nodes=(n0, n1))
self.log.info("Ensuring background validation completes")
wait_until_helper(lambda: len(n1.getchainstates()['chainstates']) == 1)
self.wait_until(lambda: len(n1.getchainstates()['chainstates']) == 1)
# Ensure indexes have synced.
completed_idx_state = {
@ -211,11 +208,11 @@ class AssumeutxoTest(BitcoinTestFramework):
assert_equal(snapshot['validated'], False)
self.connect_nodes(0, 2)
wait_until_helper(lambda: n2.getchainstates()['chainstates'][-1]['blocks'] == FINAL_HEIGHT)
self.wait_until(lambda: n2.getchainstates()['chainstates'][-1]['blocks'] == FINAL_HEIGHT)
self.sync_blocks()
self.log.info("Ensuring background validation completes")
wait_until_helper(lambda: len(n2.getchainstates()['chainstates']) == 1)
self.wait_until(lambda: len(n2.getchainstates()['chainstates']) == 1)
completed_idx_state = {
'basic block filter index': COMPLETE_IDX,
@ -242,12 +239,12 @@ class AssumeutxoTest(BitcoinTestFramework):
self.restart_node(2, extra_args=[
'-reindex-chainstate=1', *self.extra_args[2]])
assert_equal(n2.getblockchaininfo()["blocks"], FINAL_HEIGHT)
wait_until_helper(lambda: n2.getblockcount() == FINAL_HEIGHT)
self.wait_until(lambda: n2.getblockcount() == FINAL_HEIGHT)
self.log.info("Test -reindex of an assumeutxo-synced node")
self.restart_node(2, extra_args=['-reindex=1', *self.extra_args[2]])
self.connect_nodes(0, 2)
wait_until_helper(lambda: n2.getblockcount() == FINAL_HEIGHT)
self.wait_until(lambda: n2.getblockcount() == FINAL_HEIGHT)
if __name__ == '__main__':

View file

@ -77,7 +77,7 @@ from test_framework.messages import (
from test_framework.util import (
MAX_NODES,
p2p_port,
wait_until_helper,
wait_until_helper_internal,
)
logger = logging.getLogger("TestFramework.p2p")
@ -466,7 +466,7 @@ class P2PInterface(P2PConnection):
assert self.is_connected
return test_function_in()
wait_until_helper(test_function, timeout=timeout, lock=p2p_lock, timeout_factor=self.timeout_factor)
wait_until_helper_internal(test_function, timeout=timeout, lock=p2p_lock, timeout_factor=self.timeout_factor)
def wait_for_connect(self, timeout=60):
test_function = lambda: self.is_connected
@ -602,7 +602,7 @@ class NetworkThread(threading.Thread):
def close(self, timeout=10):
"""Close the connections and network event loop."""
self.network_event_loop.call_soon_threadsafe(self.network_event_loop.stop)
wait_until_helper(lambda: not self.network_event_loop.is_running(), timeout=timeout)
wait_until_helper_internal(lambda: not self.network_event_loop.is_running(), timeout=timeout)
self.network_event_loop.close()
self.join(timeout)
# Safe to remove event loop.

View file

@ -33,7 +33,7 @@ from .util import (
get_datadir_path,
initialize_datadir,
p2p_port,
wait_until_helper,
wait_until_helper_internal,
)
@ -747,7 +747,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.sync_mempools(nodes)
def wait_until(self, test_function, timeout=60):
return wait_until_helper(test_function, timeout=timeout, timeout_factor=self.options.timeout_factor)
return wait_until_helper_internal(test_function, timeout=timeout, timeout_factor=self.options.timeout_factor)
# Private helper methods. These should not be accessed by the subclass test scripts.

View file

@ -36,7 +36,7 @@ from .util import (
get_auth_cookie,
get_rpc_proxy,
rpc_url,
wait_until_helper,
wait_until_helper_internal,
p2p_port,
)
@ -253,7 +253,7 @@ class TestNode():
if self.version_is_at_least(190000):
# getmempoolinfo.loaded is available since commit
# bb8ae2c (version 0.19.0)
wait_until_helper(lambda: rpc.getmempoolinfo()['loaded'], timeout_factor=self.timeout_factor)
wait_until_helper_internal(lambda: rpc.getmempoolinfo()['loaded'], timeout_factor=self.timeout_factor)
# Wait for the node to finish reindex, block import, and
# loading the mempool. Usually importing happens fast or
# even "immediate" when the node is started. However, there
@ -407,7 +407,7 @@ class TestNode():
def wait_until_stopped(self, *, timeout=BITCOIND_PROC_WAIT_TIMEOUT, expect_error=False, **kwargs):
expected_ret_code = 1 if expect_error else 0 # Whether node shutdown return EXIT_FAILURE or EXIT_SUCCESS
wait_until_helper(lambda: self.is_node_stopped(expected_ret_code=expected_ret_code, **kwargs), timeout=timeout, timeout_factor=self.timeout_factor)
wait_until_helper_internal(lambda: self.is_node_stopped(expected_ret_code=expected_ret_code, **kwargs), timeout=timeout, timeout_factor=self.timeout_factor)
def replace_in_config(self, replacements):
"""
@ -718,7 +718,7 @@ class TestNode():
p.peer_disconnect()
del self.p2ps[:]
wait_until_helper(lambda: self.num_test_p2p_connections() == 0, timeout_factor=self.timeout_factor)
wait_until_helper_internal(lambda: self.num_test_p2p_connections() == 0, timeout_factor=self.timeout_factor)
def bumpmocktime(self, seconds):
"""Fast forward using setmocktime to self.mocktime + seconds. Requires setmocktime to have

View file

@ -241,7 +241,7 @@ def satoshi_round(amount):
return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN)
def wait_until_helper(predicate, *, attempts=float('inf'), timeout=float('inf'), lock=None, timeout_factor=1.0):
def wait_until_helper_internal(predicate, *, attempts=float('inf'), timeout=float('inf'), lock=None, timeout_factor=1.0):
"""Sleep until the predicate resolves to be True.
Warning: Note that this method is not recommended to be used in tests as it is