-
Notifications
You must be signed in to change notification settings - Fork 37.4k
assumeutxo: snapshot initialization #25667
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
38d511b
to
0a482e9
Compare
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
0a482e9
to
15da925
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -4720,6 +4708,46 @@ const AssumeutxoData* ExpectedAssumeutxo( | |||
return nullptr; | |||
} | |||
|
|||
static bool DeleteCoinsDBFromDisk(const fs::path db_path, bool is_snapshot) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note for reviewers: for now is_snapshot
is always true as there is a single usage. This is not the case anymore with the remaining commits of the #24232 patchset.
//! snapshot that is in the process of being validated. | ||
bool DetectSnapshotChainstate(CTxMemPool* mempool) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); | ||
|
||
void ResetChainstates() EXCLUSIVE_LOCKS_REQUIRED(::cs_main); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note to reviewers: this method is used in the remaining #24232 patchset.
@dergoegge I think you could be interested to review the assumeutxo work. In case of future hypothetical integration of libutreexo in Core, I believe there would be intersecting code paths and data structure modified. It might also prepare for future synergies between utreexo and assumeutxo. |
Planning on diving deeply into this, thanks for splitting it up @jamesob! Questions from going over the commits: Q1: Is the intended layout:
Q2: The last 6 commits seem to be focused on unit tests, but it seems to me that the functionality being added here is better tested with functional tests. Is there something we need to test here that a functional test won't be able to do? |
bool removed = fs::remove(base_blockhash_path); | ||
if (!removed) { | ||
LogPrintf("[snapshot] failed to remove file %s\n", | ||
fs::PathToString(base_blockhash_path)); | ||
} | ||
} else { | ||
LogPrintf("[snapshot] snapshot chainstate dir being removed lacks %s file\n", | ||
fs::PathToString(node::SNAPSHOT_BLOCKHASH_FILENAME)); | ||
} | ||
} | ||
|
||
std::string path_str = fs::PathToString(db_path); | ||
LogPrintf("Removing leveldb dir at %s\n", path_str); | ||
|
||
// We have to destruct before this call leveldb::DB in order to release the db | ||
// lock, otherwise `DestroyDB` will fail. See `leveldb::~DBImpl()`. | ||
const bool destroyed = dbwrapper::DestroyDB(path_str, {}).ok(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to other reviewers: the call to DestroyDB
will a
10000
ctually remove the entire directory, including the blockhash file if it still existed since we place the blockhash file inside what leveldb considers to be its directory, but it's probably better (more polite?) to remove it ourselves just so we can print nice errors.
bitcoin/src/leveldb/db/db_impl.cc
Line 1550 in de3c46c
env->DeleteDir(dbname); // Ignore error in case dir contains other files |
This PR needs rebase. |
15da925
to
2dcb2bd
Compare
Hi all, sorry for the delay. My spirit has been weak the last few months, and I took two weeks off to try and shake the burnout. I've addressed all feedback listed here and rebased. @dongcarl asks:
Yep, it is.
We can't have functional tests to exercise this behavior until we add |
2dcb2bd
to
f73ca14
Compare
This CreateAndActivateUTXOSnapshot parameter is necessary once we perform snapshot completion within ABC, since the existing UpdateTip test will fail because the IBD chain that has generated the snapshot will exceed the base of the snapshot. Being able to test snapshots being loaded into a mostly-uninitialized datadir allows for more realistic unittest scenarios.
Used when testing cleanup of on-disk chainstate data for snapshot testcases. Also necessary for simulating node restart in .cpp tests.
in TestingSetup(). This is used in the following commit to test reinitializing chainstates after snapshot validation and cleanup. Best reviewed with `git diff --color-moved=dimmed-zebra`.
1430518
to
bf95976
Compare
Thanks for the feedback @MarcoFalke, all addressed. |
utACK bf95976 |
There was a problem hiding this comment A8C6 .
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review ACK bf95976
Changes since last ACK:
CChainState
->Chainstate
, after CChainState -> Chainstate #24513- replacing
CAutoFile
usage inutxo_snapshot.cpp
byAutoFile
- commenting
SNAPSHOT_BLOCKHASH_FILENAME
- a new informative log L116 in
src/node/chainstate.cpp
- replacing
InitializeChainstate
byActivateExistingSnapshot
in tests - dropping the optional
snapshot_blockash
argument fromInitializeChainstate
and adding asserts - commenting
ActivateExistingSnapshot
- comment, lock simplification and minor code changes in unit tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code review ACK bf95976. Changes since last review: rebasing, switching from CAutoFile to AutoFile, adding comments, switching from BOOST_CHECK to Assert in test util, using chainman.GetMutex() in tests, destroying one ChainstateManager before creating a new one in tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK bf95976
@@ -9,6 +9,7 @@ | |||
#include <coins.h> | |||
#include <dbwrapper.h> | |||
#include <sync.h> | |||
#include <fs.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Can be swapped with sync.h
above to have alphabetical order
@@ -76,8 +76,9 @@ original chainstate remains in use as active. | |||
|
|||
Once the snapshot chainstate is loaded and validated, it is promoted to active | |||
chainstate and a sync to tip begins. A new chainstate directory is created in the | |||
datadir for the snapshot chainstate called | |||
`chainstate_[SHA256 blockhash of snapshot base block]`. | |||
datadir for the snapshot chainstate called `chainstate_snapshot`. When this directory |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
line 117 in this file still mentions chainstate_[hash]
I'm counting 4 ACKs here - what more is needed to merge? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, I also verified that all commits compile and pass unit tests.
Left some minor/nit comments.
//! @returns filesystem path to the on-disk data. | ||
std::optional<fs::path> StoragePath() { | ||
if (m_is_memory) { | ||
return {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
d14bebf
also missing #include <optional>
return {}; | |
return std::nullopt; |
@@ -524,6 +524,16 @@ void BlockManager::FlushUndoFile(int block_file, bool finalize) | |||
void BlockManager::FlushBlockFile(bool fFinalize, bool finalize_undo) | |||
{ | |||
LOCK(cs_LastBlockFile); | |||
|
|||
if (m_blockfile_info.size() < 1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
8153bd9
nit: easier to read and guaranteed constant time
if (m_blockfile_info.size() < 1) { | |
if (m_blockfile_info.empty()) { |
return true; | ||
} | ||
|
||
std::optional<uint256> ReadSnapshotBaseBlockhash(fs::path chaindir) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
f9f1735
nit
std::optional<uint256> ReadSnapshotBaseBlockhash(fs::path chaindir) | |
std::optional<uint256> ReadSnapshotBaseBlockhash(const fs::path& chaindir) |
const std::vector<const char*>& extra_args, | ||
const bool coins_db_in_memory, | ||
const bool block_tree_db_in_memory) | ||
: TestingSetup{CBaseChainParams::REGTEST, extra_args, coins_db_in_memory, block_tree_db_in_memory} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
51fc924
I believe it should be like this?
: TestingSetup{CBaseChainParams::REGTEST, extra_args, coins_db_in_memory, block_tree_db_in_memory} | |
: TestingSetup{chain_name, extra_args, coins_db_in_memory, block_tree_db_in_memory} |
|
||
// Background chainstate should be unaware of new blocks on the snapshot | ||
// chainstate. | ||
for (Chainstate* cs : chainman_restarted.GetAll()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
e4d7995
nit
for (Chainstate* cs : chainman_restarted.GetAll()) { | |
for (const Chainstate* cs : chainman_restarted.GetAll()) { |
@@ -4780,6 +4767,46 @@ const AssumeutxoData* ExpectedAssumeutxo( | |||
return nullptr; | |||
} | |||
|
|||
static bool DeleteCoinsDBFromDisk(const fs::path db_path, bool is_snapshot) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
34d1590
nit
static bool DeleteCoinsDBFromDisk(const fs::path db_path, bool is_snapshot) | |
static bool DeleteCoinsDBFromDisk(const fs::path& db_path, bool is_snapshot) |
I'm going to leave the nits as-is to avoid ACK churn unless maints feel they are blocking merge. I'll be very quick to review any followups that @aureleoules or @fjahr would like to do towards addressing them. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK bf95976
if (fs::exists(base_blockhash_path)) { | ||
bool removed = fs::remove(base_blockhash_path); | ||
if (!removed) { | ||
LogPrintf("[snapshot] failed to remove file %s\n", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this code is the intended behavior. This is redundant to the previous check 3 lines up. If fs::remove
without a second parameter returns false
it is only because the file does not exist. If it actually fails to remove an existing file it will throw an exception (see https://en.cppreference.com/w/cpp/filesystem/remove).
If intended behavior is to only log error and continue fs::remove
should be wrapped in a try/catch block or passed a std::error_code
.
See https://github.com/bitcoin/bitcoin/blob/master/src/init.cpp#L326-L332.
Summary: > db: add StoragePath to CDBWrapper/CCoinsViewDB > > This is used in subsequent commits. It allows us to clean up UTXO > snapshot chainstate after background validation completes. bitcoin/bitcoin@d14bebf > validation: rename snapshot chainstate dir > > This changes the snapshot's leveldb chainstate dir name from > `chainstate_[blockhash]` to `chainstate_snapshot`. This simplifies > later logic that loads snapshot data, and enforces the limitation > of a single snapshot at any given time. > > Since we still need to persist the blockhash of the base block, we > write that out to a file (`chainstate_snapshot/base_blockhash`) for > later use during initialization, so that we can reinitialize the > snapshot chainstate. > > Co-authored-by: Russell Yanofsky <russ@yanofsky.org> bitcoin/bitcoin@f9f1735 > test: allow on-disk coins and block tree dbs in tests > > Used when testing cleanup of on-disk chainstate data for snapshot > testcases. Also necessary for simulating node restart in .cpp tests. bitcoin/bitcoin@51fc924 Note: TestChain100Setup now inherits TestingSetup instead of RegTestingSetup. This would normally be done in [[bitcoin/bitcoin#22818 | core#22818]] which we didn't backport because the whole softfork machiney is not relevant to us. This is a partial backport of [[bitcoin/bitcoin#25667 | core#25667]] Test Plan: `ninja all check-all` Reviewers: #bitcoin_abc, Fabien Reviewed By: #bitcoin_abc, Fabien Subscribers: Fabien Differential Revision: https://reviews.bitcoinabc.org/D14650
… dbs in tests Summary: > init: add utxo snapshot detection > > Add functionality for activating a snapshot-based chainstate if one is > detected on-disk. > > Also cautiously initialize chainstate cache usages so that we don't > somehow blow past our cache allowances during initialization, then > rebalance at the end of init. > > Co-authored-by: Russell Yanofsky <russ@yanofsky.org> bitcoin/bitcoin@252abd1 > blockmanager: avoid undefined behavior during FlushBlockFile > > If we call FlushBlockFile() without having intitialized the block index > with LoadBlockIndexDB(), we may be indexing into an empty vector. > > Specifically this is an issue when we call MaybeRebalanceCaches() during > chainstate init before the block index has been loaded, which calls > FlushBlockFile(). > > Also add an assert to avoid undefined behavior. > > Co-authored-by: Russell Yanofsky <russ@yanofsky.org> bitcoin/bitcoin@8153bd9 This is a partial backport of [[bitcoin/bitcoin#25667 | core#25667]] Depends on D14650 Test Plan: `ninja all check-all` Reviewers: #bitcoin_abc, Fabien Reviewed By: #bitcoin_abc, Fabien Differential Revision: https://reviews.bitcoinabc.org/D14651
Summary: For use in next commit. Most easily reviewed with `--color-moved=dimmed_zebra --color-moved-ws=allow-indentation-change`. This is a partial backport of [[bitcoin/bitcoin#25667 | core#25667]] bitcoin/bitcoin@3a29dfb Depends on D14651 Test Plan: `ninja check` Reviewers: #bitcoin_abc, Fabien Reviewed By: #bitcoin_abc, Fabien Subscribers: Fabien Differential Revision: https://reviews.bitcoinabc.org/D14652
Summary: > validation: add ResetChainstates() > > Necessary for the following test commit. bitcoin/bitcoin@00b357c > test: add reset_chainstate parameter for snapshot unittests > > This CreateAndActivateUTXOSnapshot parameter is necessary once we > perform snapshot completion within ABC, since the existing UpdateTip > test will fail because the IBD chain that has generated the snapshot > will exceed the base of the snapshot. > > Being able to test snapshots being loaded into a mostly-uninitialized > datadir allows for more realistic unittest scenarios. bitcoin/bitcoin@3c36139 Backport note: this process of resetting the chainstate without also resetting the block index would cause `CheckBlockIndex` to fail on `assert(setBlockIndexCandidates.count(pindex)));` after D4717 made `CheckBlockIndex` run on intermediate steps in `ActiveBestChain`. I disabled `CheckBlockIndex` until after the snapshot chain is activated. This is a partial backport of [[bitcoin/bitcoin#25667 | core#25667]] Depends on D14652 Test Plan: `ninja all check-all` Reviewers: #bitcoin_abc, Fabien Reviewed By: #bitcoin_abc, Fabien Subscribers: Fabien Differential Revision: https://reviews.bitcoinabc.org/D14653
Summary: > add utilities for deleting on-disk leveldb data > > Used in later commits to remove leveldb directories for > - invalid snapshot chainstates, and > - background-vaildation chainstates that have finished serving their > purpose. bitcoin/bitcoin@34d1590 > validation: remove snapshot datadirs upon validation failure > > If a UTXO snapshot fails to validate, don't leave the resulting datadir > on disk as this will confuse initialization on next startup and we'll > get an assertion error. bitcoin/bitcoin@ad67ff3 This is a partial backport of [[bitcoin/bitcoin#25667 | core#25667]] Depends on D14653 Test Plan: `ninja all check-all` Reviewers: #bitcoin_abc, Fabien Reviewed By: #bitcoin_abc, Fabien Subscribers: Fabien Differential Revision: https://reviews.bitcoinabc.org/D14654
Summary: This is a partial backport of [[bitcoin/bitcoin#25667 | core#25667]] bitcoin/bitcoin@e4d7995 Depends on D14654 Test Plan: `ninja check` Reviewers: #bitcoin_abc, Fabien Reviewed By: #bitcoin_abc, Fabien Subscribers: Fabien Differential Revision: https://reviews.bitcoinabc.org/D14655
Summary: This is a backport of [[bitcoin/bitcoin#23154 | core#23154]] with updates from [[bitcoin/bitcoin#25667 | core#25667]] and [[bitcoin/bitcoin#25740 | core#25740]] Depends on D14655 Test Plan: proof-reading Reviewers: #bitcoin_abc, Fabien Reviewed By: #bitcoin_abc, Fabien Subscribers: Fabien Differential Revision: https://reviews.bitcoinabc.org/D14656
This is part of the assumeutxo project (parent PR: #15606)
Half of the replacement for #24232. The original PR grew larger than expected throughout the review process.
This change adds the ability to initialize a snapshot-based chainstate during init if one is detected on disk. This is of course unused as of now (aside from in unittests) given that we haven't yet enabled actually loading snapshots.
Don't be scared! There are some big move-only commits in here.
Accompanying changes include:
chainstate_[base blockhash]
tochainstate_snapshot
, since we only support one snapshot in use at a time. This simplifies some logic, but it necessitates writing that base blockhash out to a file within the coinsdb dir. See discussion here.FlushBlockFile()
that avoids a crash when attemping to flush to disk beforeLoadBlockIndexDB()
is called, which happens when callingMaybeRebalanceCaches()
during multiple chainstate init.