Skip to content

Commit

Permalink
Merge bitcoin/bitcoin#28562: AssumeUTXO follow-ups
Browse files Browse the repository at this point in the history
5d227a6 rpc: Use Ensure(Any)Chainman in assumeutxo related RPCs (Fabian Jahr)
710e5db doc: Drop references to assumevalid in assumeutxo docs (Fabian Jahr)
1ff1c34 test: Rename wait_until_helper to wait_until_helper_internal (Fabian Jahr)
a482f86 chain: Rename HaveTxsDownloaded to HaveNumChainTxs (Fabian Jahr)
82e48d2 blockstorage: Let FlushChainstateBlockFile return true in case of missing cursor (Fabian Jahr)
73700fb validation, test: Improve and document nChainTx check for testability (Fabian Jahr)
2c9354f doc: Add snapshot chainstate removal warning to reindexing documentation (Fabian Jahr)
4e915e9 test: Improvements of feature_assumeutxo (Fabian Jahr)
a47fbe7 doc: Add and edit some comments around assumeutxo (Fabian Jahr)
0a39b8c 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 5d227a6. Just suggested doc change and new EnsureChainman RPC cleanup commit since last review.

Tree-SHA512: 6f7c762100e18f82946b881676db23e67da7dc3a8bf04e4999a183e90b4f150a0b1202bcb95920ba937a358867bbf2eca300bd84b9b1776c7c490410e707c267
  • Loading branch information
fanquake committed Oct 7, 2023
2 parents 1472df6 + 5d227a6 commit 38f4b0d
Show file tree
Hide file tree
Showing 18 changed files with 73 additions and 73 deletions.
2 changes: 1 addition & 1 deletion doc/design/assumeutxo.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion doc/release-notes-27596.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 1 addition & 3 deletions src/chain.h
Original file line number Diff line number Diff line change
Expand Up @@ -280,10 +280,8 @@ class CBlockIndex
* 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
{
Expand Down
4 changes: 2 additions & 2 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/kernel/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ class CRegTestParams : public CChainParams
{
.height = 110,
.hash_serialized = AssumeutxoHash{uint256S("0x1ebbf5850204c0bdb15bf030f47c7fe91d45c44c712697e4509ba67adb01c618")},
.nChainTx = 110,
.nChainTx = 111,
.blockhash = uint256S("0x696e92821f65549c7ee134edceeeeaaa4105647a3c4fd9f298c0aec0ab50425c")
},
{
Expand Down
6 changes: 4 additions & 2 deletions src/net_processing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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
Expand Down
8 changes: 5 additions & 3 deletions src/node/blockstorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion src/node/chainstate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
9 changes: 4 additions & 5 deletions src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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"

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/test/fuzz/chain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down
4 changes: 2 additions & 2 deletions src/test/validation_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
40 changes: 22 additions & 18 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@
#include <numeric>
#include <optional>
#include <string>
#include <utility>
#include <tuple>
#include <utility>

using kernel::CCoinsStats;
using kernel::CoinStatsHashType;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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));
}
}
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
Expand Down
Loading

0 comments on commit 38f4b0d

Please sign in to comment.