-
Notifications
You must be signed in to change notification settings - Fork 39.2k
doc: add assumeutxo notes #23154
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
doc: add assumeutxo notes #23154
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,138 @@ | ||
| # assumeutxo | ||
|
|
||
| Assumeutxo is a feature that allows fast bootstrapping of a validating bitcoind | ||
| instance with a very similar security model to assumevalid. | ||
|
|
||
| The RPC commands `dumptxoutset` and `loadtxoutset` are used to respectively generate | ||
| and load UTXO snapshots. The utility script `./contrib/devtools/utxo_snapshot.sh` may | ||
| be of use. | ||
|
|
||
| ## General background | ||
|
|
||
| - [assumeutxo proposal](https://github.com/jamesob/assumeutxo-docs/tree/2019-04-proposal/proposal) | ||
| - [Github issue](https://github.com/bitcoin/bitcoin/issues/15605) | ||
| - [draft PR](https://github.com/bitcoin/bitcoin/pull/15606) | ||
|
|
||
| ## Design notes | ||
|
|
||
| - A new block index `nStatus` flag is introduced, `BLOCK_ASSUMED_VALID`, to mark block | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am wondering who is the audience of this document? If it is a developer, it might be better to put the documentation in the source code. Otherwise it will be hard for developers to find it and it will more easily get out of date. If the target audience is a user, I am wondering why internal implementation details are mentioned and why they are relevant to the user. If the target audience is a reviewer of your pull request, I am wondering why this needs a doc at all in the source tree. Wouldn't a pull request description or so be a better place?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. re: #23154 (comment)
I think the concerns here go a little beyond scope of this PR. Some of the files in the doc/ folder are clearly targeted at developers and not helpful to users like https://github.com/bitcoin/bitcoin/blob/master/doc/developer-notes.md. Some files like https://github.com/bitcoin/bitcoin/blob/master/doc/JSON-RPC-interface.md and the release notes are mostly useful for end users, and definitely intended to be read by them. I think ideally someone would go through the doc folder, keeping the files that are intended to be read by users but moving the other ones that are intended for developers/testers/contributors somewhere else like This document is a basically a design document (other examples: golang, chromium) intended to by read by developers. It should be useful not just to people reviewing the PR, but also anybody going forward who want to understand how this feature which touches different parts of the source code is implemented and what some of design considerations are. At the top of this file, there is a small amount of information that could be useful to users, but it is brief and basically just linking to RPCs and a script. If bitcoin had a user manual like many other projects do, this section could be copied there. But bitcoin doesn't have a user manual so, 🤷
I think this could be addressed by organizing the doc directory a little better and linking to the document from the source code so it is easier to find.
Users are not target audience here, AFAICT.
I think this is a false dichotomy. If I am reviewing a pull request that includes end-user release notes, those release notes are usually more helpful to me for understanding behavior of the PR than any of the weedsy implementation details in the PR description or other parts of the diff. PR descriptions do tend to be better places than design documents for certain information, like describing details or drawbacks of previous code, but design documents are useful both for current developers reviewing changes, and for future developers want to understand how a feature works. It is not an either/or. |
||
| index entries that are required to be assumed-valid by a chainstate created | ||
| from a UTXO snapshot. This flag is mostly used as a way to modify certain | ||
| CheckBlockIndex() logic to account for index entries that are pending validation by a | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would like some backticks at least for the functions: |
||
| chainstate running asynchronously in the background. We also use this flag to control | ||
| which index entries are added to setBlockIndexCandidates during LoadBlockIndex(). | ||
|
|
||
| - Indexing implementations via BaseIndex can no longer assume that indexation happens | ||
| sequentially, since background validation chainstates can submit BlockConnected | ||
| events out of order with the active chain. | ||
|
|
||
| - The concept of UTXO snapshots is treated as an implementation detail that lives | ||
| behind the ChainstateManager interface. The external presentation of the changes | ||
| required to facilitate the use of UTXO snapshots is the understanding that there are | ||
| now certain regions of the chain that can be temporarily assumed to be valid (using | ||
| the nStatus flag mentioned above). In certain cases, e.g. wallet rescanning, this is | ||
| very similar to dealing with a pruned chain. | ||
|
|
||
| Logic outside ChainstateManager should try not to know about snapshots, instead | ||
| preferring to work in terms of more general states like assumed-valid. | ||
|
|
||
|
|
||
| ## Chainstate phases | ||
|
|
||
| Chainstate within the system goes through a number of phases when UTXO snapshots are | ||
| used, as managed by `ChainstateManager`. At various points there can be multiple | ||
| `CChainState` objects in existence to facilitate both maintaining the network tip and | ||
| performing historical validation of the assumed-valid chain. | ||
|
|
||
| It is worth noting that though there are multiple separate chainstates, those | ||
| chainstates share use of a common block index (i.e. they hold the same `BlockManager` | ||
| reference). | ||
|
|
||
| The subheadings below outline the phases and the corresponding changes to chainstate | ||
| data. | ||
|
|
||
| ### "Normal" operation via initial block download | ||
|
|
||
| `ChainstateManager` manages a single CChainState object, for which | ||
| `m_snapshot_blockhash` is null. This chainstate is (maybe obviously) | ||
| considered active. This is the "traditional" mode of operation for bitcoind. | ||
|
|
||
| | | | | ||
| | ---------- | ----------- | | ||
| | number of chainstates | 1 | | ||
| | active chainstate | ibd | | ||
|
|
||
| ### User loads a UTXO snapshot via `loadtxoutset` RPC | ||
|
|
||
| `ChainstateManager` initializes a new chainstate (see `ActivateSnapshot()`) to load the | ||
| snapshot contents into. During snapshot load and validation (see | ||
| `PopulateAndValidateSnapshot()`), the new chainstate is not considered active and the | ||
| original chainstate remains in use as active. | ||
|
|
||
| | | | | ||
| | ---------- | ----------- | | ||
| | number of chainstates | 2 | | ||
| | active chainstate | ibd | | ||
|
|
||
| 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]`. | ||
|
|
||
| | | | | ||
| | ---------- | ----------- | | ||
| | number of chainstates | 2 | | ||
| | active chainstate | snapshot | | ||
|
|
||
| The snapshot begins to sync to tip from its base block, technically in parallel with | ||
| the original chainstate, but it is given priority during block download and is | ||
| allocated most of the cache (see `MaybeRebalanceCaches()` and usages) as our chief | ||
| consideration is getting to network tip. | ||
|
|
||
| **Failure consideration:** if shutdown happens at any point during this phase, both | ||
| chainstates will be detected during the next init and the process will resume. | ||
|
|
||
| ### Snapshot chainstate hits network tip | ||
|
|
||
| Once the snapshot chainstate leaves IBD, caches are rebalanced | ||
| (via `MaybeRebalanceCaches()` in `ActivateBestChain()`) and more cache is given | ||
| to the background chainstate, which is responsible for doing full validation of the | ||
| assumed-valid parts of the chain. | ||
|
|
||
| **Note:** at this point, ValidationInterface callbacks will be coming in from both | ||
| chainstates. Considerations here must be made for indexing, which may no longer be happening | ||
| sequentially. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you have a Big Comment(tm) warning users of ValidationInterface callbacks of this subtlety ? Grepping quickly
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point - I'll verify that I'm making a note somewhere in comments about this. |
||
|
|
||
| ### Background chainstate hits snapshot base block | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps you could call this ibd chainstate, and not background? Or anything not background, because at some point background also refers to the snapshot as well
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I had been calling it IBD chainstate, but eventually standardized on background chainstate to cut down on jargon (see #15606 (review)). |
||
|
|
||
| Once the tip of the background chainstate hits the base block of the snapshot | ||
| chainstate, we stop use of the background chainstate by setting `m_stop_use` (not yet | ||
| committed - see #15606), in `CompleteSnapshotValidation()`, which is checked in | ||
| `ActivateBestChain()`). We hash the background chainstate's UTXO set contents and | ||
| ensure it matches the compiled value in `CMainParams::m_assumeutxo_data`. | ||
|
|
||
| The background chainstate data lingers on disk until shutdown, when in | ||
| `ChainstateManager::Reset()`, the background chainstate is cleaned up with | ||
| `ValidatedSnapshotShutdownCleanup()`, which renames the `chainstate_[hash]` datadir as | ||
| `chainstate`. | ||
|
|
||
| | | | | ||
| | ---------- | ----------- | | ||
| | number of chainstates | 2 (ibd has `m_stop_use=true`) | | ||
| | active chainstate | snapshot | | ||
|
|
||
| **Failure consideration:** if bitcoind unexpectedly halts after `m_stop_use` is set on | ||
| the background chainstate but before `CompleteSnapshotValidation()` can finish, the | ||
| need to complete snapshot validation will be detected on subsequent init by | ||
| `ChainstateManager::CheckForUncleanShutdown()`. | ||
|
|
||
| ### Bitcoind restarts sometime after snapshot validation has completed | ||
|
|
||
| When bitcoind initializes again, what began as the snapshot chainstate is now | ||
| indistinguishable from a chainstate that has been built from the traditional IBD | ||
| process, and will be initialized as such. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From a user, observing the chainstate from RPC calls, do you have any difference with the above "Normal" operation" at that stage or they're fully similar ?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At the stage they're fully similar, but we could probably leave behind some indication that a snapshot was used if that's preferable for some reason. But at the moment I don't think there are any artifacts in the block index that could be used to indicate this. |
||
|
|
||
| | | | | ||
| | ---------- | ----------- | | ||
| | number of chainstates | 1 | | ||
| | active chainstate | ibd | | ||
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.
Would be good to tell the reader for what it is of use:
"[...]may be of use to create or validate historic snapshots."