-
Notifications
You must be signed in to change notification settings - Fork 35.6k
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
refactor: Move mutable globals cleared in ::UnloadBlockIndex
to BlockManager
#22564
refactor: Move mutable globals cleared in ::UnloadBlockIndex
to BlockManager
#22564
Conversation
Ping @jnewbery, since #21866 (comment) |
Concept ACK. Globals must fall! |
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.
Concept ACK
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. |
src/deploymentstatus.h
Outdated
/** Determine if a deployment is active for the next block */ | ||
inline bool DeploymentActiveAfter(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::BuriedDeployment dep) | ||
{ | ||
assert(Consensus::ValidDeployment(dep)); | ||
return (pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1) >= params.DeploymentHeight(dep); | ||
} | ||
|
||
inline bool DeploymentActiveAfter(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos dep) | ||
inline bool DeploymentActiveAfter(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos dep, VersionBitsCache& versionbitscache) |
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.
Hard NACK -- the signatures for the two versions of DeploymentActiveAfter
(and ..At
) should only differ by whether they accept a DeploymentPos
or a BuriedDeployment
, otherwise we're back to having to edit bunches of call sites when burying deployments.
BUT... I think you can get the result you're trying for by making DeploymentActiveAfter
(and ..At
) inline member functions of BlockManager
. Proof of concept at https://github.com/ajtowns/bitcoin/commits/202108-deplotmentstatus-blockmanager
Would it be feasible to move BlockManager
to its own module to reduce the amount of stuff validation.cpp does? Giving BlockManager a CChainParams
member reference and having CChainState
and ChainstateManager
reference it via their BlockManager references, and also removing the Consensus::Params
and CChainParams
params from bunches of functions (including the DeploymentActive..
ones) seems like it would be a win.
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.
Wouldn't another class in validation be better suited to host the deployment status? My goal was to move BlockManger to node/storage and have it deal with storage only, not validation.
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.
@MarcoFalke Maybe? I look at deployment status as an expansion of Consensus::Params -- so more as input to validation than a result of it. If we switch to using the coinbase witness nonce for deployment signalling we'd need to change the way we store blocks so that it's easy to access that info without having to load every signalling block from disk, so it's kind-of storage related in that sense?
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.
the signatures for the two versions of
DeploymentActiveAfter
(and..At
) should only differ by whether they accept aDeploymentPos
or aBuriedDeployment
, otherwise we're back to having to edit bunches of call sites when burying deployments.
Oh I did not realize that this was the case! Note to self: add a code comment documenting this.
BUT... I think you can get the result you're trying for by making
DeploymentActiveAfter
(and..At
) inline member functions ofBlockManager
. Proof of concept at https://github.com/ajtowns/bitcoin/commits/202108-deplotmentstatus-blockmanager
This looks good to me, and it seems like they would eventually end up having to interact with BlockManager
anyway according to the following?
If we switch to using the coinbase witness nonce for deployment signalling we'd need to change the way we store blocks so that it's easy to access that info without having to load every signalling block from disk, so it's kind-of storage related in that sense?
Would it be feasible to move
BlockManager
to its own module to reduce the amount of stuff validation.cpp does? Giving BlockManager aCChainParams
member reference and havingCChainState
andChainstateManager
reference it via their BlockManager references, and also removing theConsensus::Params
andCChainParams
params from bunches of functions (including theDeploymentActive..
ones) seems like it would be a win.
Yes I think as part of some other work I'll have to pull validation apart anyway, I will look into this!
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.
This looks good to me, and it seems like they would eventually end up having to interact with
BlockManager
anyway according to the following?
Well, using the cb wit nonce for activation signalling might never happen, and even if it does, I think it's a fair way off (since it would need getblocktemplate changes or stratumv2 or similar to make it even possible), so I wouldn't put too much weight on it, but it seems plausible.
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 think this is dealt with by #24595
@@ -4106,7 +4107,6 @@ void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman) | |||
LOCK(cs_main); | |||
chainman.Unload(); | |||
if (mempool) mempool->clear(); | |||
g_versionbitscache.Clear(); |
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 that there shouldn't be any logical need for this to be cleared here -- the cached information doesn't become invalid when we load up different blocks, only if the consensus parameters are changed.
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.
Is this the same case for warningcache
as well? If so, perhaps we'll just move the clearing logic to SelectParams
and not move these two structures into BlockManager
?
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.
Yes, warningcache
only relies on the chain history, consensus params, and versionbitscache state. Moving the clearing logic to SelectParams
would introduce a circular dependency though, I think?
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.
Here's a rebase of this that replaces moving g_versionbits with just removing g_versionbitscache.Clear()
from UnloadBlockIndex
-- https://github.com/ajtowns/bitcoin/commits/202201-vb-unloadblockindex ... What do you think about doing that instead? I've got some updates for the versionbits code I'd like to PR, and would prefer to minimise conflicts with this one...
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.
If warningcache
and g_versionbitscache
are both only dependent on consensus parameters, then perhaps (perhaps not in this PR) we should move them to ChainstateManager
or NodeContext
? At the very least we would need to clear them in ~ChainTestingSetup
so that tests with different consensus params won't get confused.
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.
Moving them somewhere rather than having them be globals would be good eventually; I had a look at moving them into ChainstateManager
but it felt a bit too invasive when what I was hoping to do was avoid/defer introducing conflicts...
The patch above clears g_versionbitscache
in the BasicTestingSetup
constructor instead (where SelectParams
is invoked), which I think should be good enough? It's also already cleared explicitly in versionbits_tests.cpp
which manually changes the consensus params being used.
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.
#24595 moves g_versionbitscache
into ChainstateManager
and builds on this PR, so hopefully resolves this thread
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.
This PR generally looks pretty good and it would be nice to see an update.
re: #22564 (comment)
In particular, I'd love to get some feedback on 6a897c0, since it changes the key type of BlockMap from CBlockIndex* to CBlockIndex
This seems like an improvement in every respect: removing a level of indirection, removing need for manual deletions, avoiding pointer bugs. No downsides are apparent to me at least.
f741623
to
6e6472e
Compare
6e6472e
to
6bb9ed3
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.
ACK b1ab563 🏐
Show signature
Signature:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
ACK b1ab56316ab8cad391676be2e9ca749215603de 🏐
-----BEGIN PGP SIGNATURE-----
iQGzBAEBCgAdFiEE+rVPoUahrI9sLGYTzit1aX5ppUgFAlwqrYAACgkQzit1aX5p
pUgrhAwAlBimHVBdqGT+/XIgR4/6KDDdTo7KHYTiKNoCwjOtFk3qOleBfXyhPgUr
8mmwG4RZ/UyAWut71omZs6yQI+7SyhcWZGSOihPk+k08QJFHbT0Ss9Kvb1xuujvs
Y+arP/Zv6zPWfgkbEJbYg6yPRLNRfESBnoRGmTY4JzUV3Y7wkgjMw40PcJyO6DBb
i2JFI1Pap5wqOYBFMXO/ElITTka+nqAPgPIRLhH6Ld4CJjd4bLW6xbPQLCHf4naj
yy5HvV30NCFeB9mZyVW6qnXxGMmWLsI9TzFXPSOE+v/YDD0Hb4FLg8a7X80sZs+F
E11avAyixd61w5+xcAUpbRUbEA+6FRgByodLVeHBi5xyOibHXO8zlKqfyA86u8Yx
Te4ARRWifX2bsz88dnuaeQZy/rpwJbxA+E6jSZDTAwsaS9l7DbtNk2SK7YnB2COq
WctKXdBzXzZRRi+c0Q7nG2heNlhR9KQFkvsfiw7Am4yqU61fy8L8orkAvHUrGMya
A9Kg72e8
=ZuTN
-----END PGP SIGNATURE-----
@@ -2533,7 +2533,7 @@ void CChainState::UpdateTip(const CBlockIndex* pindexNew) | |||
const CBlockIndex* pindex = pindexNew; | |||
for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) { | |||
WarningBitsConditionChecker checker(bit); | |||
ThresholdState state = checker.GetStateFor(pindex, m_params.GetConsensus(), warningcache[bit]); | |||
ThresholdState state = checker.GetStateFor(pindex, m_params.GetConsensus(), warningcache.at(bit)); |
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.
There is a compile-time erroring at()
-- std::get<N>(my_array)
won't compile if N
is outside the range. I've got a patchset that switches versionbits to working that way, fwiw; needs a lot of tidying up yet though, and builds on these patches.
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.
src/init.cpp
Outdated
@@ -1269,18 +1269,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) | |||
// as they would never get updated. | |||
if (!ignores_incoming_txs) node.fee_estimator = std::make_unique<CBlockPolicyEstimator>(); | |||
|
|||
assert(!node.mempool); | |||
int check_ratio = std::min<int>(std::max<int>(args.GetIntArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000); |
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.
In the third commit:
I think this should be moved as well, otherwise it is easy to confuse it with some other check_ratio
Edit: I see it is renamed later, but I still think it could make sense to move as well.
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.
Also, would it be better to split the peerman move into a separate commit?
Also, it would be best to do a rebase first to pull in the intermediate changes to init.cpp?
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 think this should be moved as well, otherwise it is easy to confuse it with some other check_ratio
Done in 976679b
Also, would it be better to split the peerman move into a separate commit?
Don't think so, the diff itself isn't that large, and the peerman move only makes sense in the context of moving the mempool and chainman
Also, it would be best to do a rebase first to pull in the intermediate changes to init.cpp?
Done as of the 133d98f push
b1ab563
to
133d98f
Compare
133d98f
to
678f5c5
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.
The m_total_coins*_cache
changes seem wrong to me, otherwise looks good.
Commit 678f5c5
Fixes bitcoin#22964 Previously, we used UnloadBlockIndex() in order to reset node.mempool and node.chainman. However, that has proven to be fragile (see bitcoin#22964), and requires UnloadBlockIndex and its callees to be updated manually for each member that's introduced to the mempool and chainman classes. In this commit, we stop using the UnloadBlockIndex function and we simply reconstruct node.mempool and node.chainman. Since PeerManager needs a valid reference to both node.mempool and node.chainman, we also move PeerManager's construction via `::make` to after the chainstate activation sequence is complete. There are no more callers to UnloadBlockIndex after this commit, so it and its sole callees can be pruned.
It's a bit clearer and restricts the scope of fLoaded
Also add TODO item to deglobalize the {versionbits,warning}cache, which should really only need to be cleared if we change the chainparams.
The only caller that uses this is ~ChainTestingSetup() where we immediately destroy the mempool afterwards.
In previous commits in this patchset, we've made sure that every Unload/UnloadBlockIndex member function resets its own members, and does not reach out to globals. This means that their corresponding classes' default destructors can now replace them, and do an even more thorough job without the need to be updated for every new member variable. Therefore, we can remove them, and also remove UnloadBlockIndex since that's not used anymore. Unfortunately, chainstatemanager_loadblockindex relies on CChainState::UnloadBlockIndex, so that needs to stay for now.
678f5c5
to
7ab07e0
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.
Code review ACK 7ab07e0. This all looks good and simplifies things nicely. I left some minor suggestions below but feel free to ignore.
@@ -1921,7 +1921,7 @@ class WarningBitsConditionChecker : public AbstractThresholdConditionChecker | |||
} | |||
}; | |||
|
|||
static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS] GUARDED_BY(cs_main); | |||
static std::array<ThresholdConditionCache, VERSIONBITS_NUM_BITS> warningcache GUARDED_BY(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.
In commit "refactor: Convert warningcache to std::array" (98f4bda)
This commit seems ok but it's not clear why it's good or what the connection is to the PR. Would be good to mention in commit message.
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 think it is just "updating the code", since it is already touched for other reasons in this PR
assert(!node.peerman); | ||
node.peerman = PeerManager::make(chainparams, *node.connman, *node.addrman, node.banman.get(), | ||
chainman, *node.mempool, ignores_incoming_txs); | ||
RegisterValidationInterface(node.peerman.get()); |
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.
In commit "init: Reset mempool and chainman via reconstruction" (976679b)
Is it ok for peerman to register for validation notification events later than before? Maybe because none will be sent above, or because peerman doesn't care about ones that would be sent? (would be helpful info for commit message)
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.
My understanding was that no events are sent, since blocks and the mempool are only imported later on
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.
And it looks like (from testing) with existing blocks and without import/reindex/empty datadir no events are sent at all during startup. I can only see events when the block import thread is running.
@@ -32,8 +32,6 @@ std::optional<ChainstateLoadingError> LoadChainstate(bool fReset, | |||
chainman.m_total_coinstip_cache = nCoinCacheUsage; | |||
chainman.m_total_coinsdb_cache = nCoinDBCache; | |||
|
|||
UnloadBlockIndex(mempool, chainman); |
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.
In commit "init: Reset mempool and chainman via reconstruction" (5921b86)
UnloadBlockIndex is still clearing g_versionbitscache and warningcache at this point. Should this commit come after the later commit "Clear {versionbits,warning}cache in ~Chainstatemanager" (572d831) to avoid changing behavior?
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.
Have you seen #22564 (comment) ?
// TODO: The version bits cache and warning cache should probably become | ||
// non-globals | ||
g_versionbitscache.Clear(); | ||
for (auto& i : warningcache) { | ||
i.clear(); | ||
} |
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.
In commit "Clear {versionbits,warning}cache in ~Chainstatemanager" (572d831)
It would seem more appropriate to clear these variables in the constructor not the destructor. Using the constructor to initialize things a class needs is more straightforward than doing things in the destructor to prepare for a future instance being created.
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.
Maybe this can be discussed in the versionbits follow-up?
ACK 7ab07e0 👘 Show signatureSignature:
|
ACK 7ab07e0 |
Summary: This is a partial backport of [[bitcoin/bitcoin#22564 | core#22564]] bitcoin/bitcoin@6e747e8 Test Plan: With clang and DEBUG `ninja all check-all` Reviewers: #bitcoin_abc, sdulfari Reviewed By: #bitcoin_abc, sdulfari Differential Revision: https://reviews.bitcoinabc.org/D13086
Summary: Fixes bitcoin/bitcoin#22964 Previously, we used UnloadBlockIndex() in order to reset node.mempool and node.chainman. However, that has proven to be fragile (see bitcoin/bitcoin#22964), and requires UnloadBlockIndex and its callees to be updated manually for each member that's introduced to the mempool and chainman classes. In this commit, we stop using the UnloadBlockIndex function and we simply reconstruct node.mempool and node.chainman. Since PeerManager needs a valid reference to both node.mempool and node.chainman, we also move PeerManager's construction via `::make` to after the chainstate activation sequence is complete. There are no more callers to UnloadBlockIndex after this commit, so it and its sole callees can be pruned. This is a partial backport of [[bitcoin/bitcoin#22564 | core#22564]] bitcoin/bitcoin@5921b86 Depends on D13086 and D13081 Test Plan: `ninja all check-all` Reviewers: #bitcoin_abc, sdulfari Reviewed By: #bitcoin_abc, sdulfari Differential Revision: https://reviews.bitcoinabc.org/D13087
Summary: It's a bit clearer and restricts the scope of fLoaded This is a partial backport of [[bitcoin/bitcoin#22564 | core#22564]] bitcoin/bitcoin@fe96a2e Depends on D13087 Test Plan: `ninja all check-all` Reviewers: #bitcoin_abc, sdulfari Reviewed By: #bitcoin_abc, sdulfari Differential Revision: https://reviews.bitcoinabc.org/D13088
Summary: This is a partial backport of [[bitcoin/bitcoin#22564 | core#22564]] bitcoin/bitcoin@eca4ca4 Depends on D13088 Test Plan: `ninja all check-all` Reviewers: #bitcoin_abc, sdulfari Reviewed By: #bitcoin_abc, sdulfari Differential Revision: https://reviews.bitcoinabc.org/D13089
Summary: The only caller that uses this is ~ChainTestingSetup() where we immediately destroy the mempool afterwards. This is a partial backport of [[bitcoin/bitcoin#22564 | core#22564]] bitcoin/bitcoin@572d831 bitcoin/bitcoin@7d99d72 Note: the first commit is mostly not applicable to Bitcoin ABC, I mention it only because of the moving of `~ChainstateManager()` from `validation.h` to `validation.cpp`. `g_versionbitscache` and `warningcache` are related to Core's BIP 9 and "buried deployments" business. Depends on D13089 Test Plan: `ninja all check-all` Reviewers: #bitcoin_abc, sdulfari Reviewed By: #bitcoin_abc, sdulfari Subscribers: sdulfari Differential Revision: https://reviews.bitcoinabc.org/D13090
Summary: In previous commits in this patchset, we've made sure that every Unload/UnloadBlockIndex member function resets its own members, and does not reach out to globals. This means that their corresponding classes' default destructors can now replace them, and do an even more thorough job without the need to be updated for every new member variable. Therefore, we can remove them, and also remove UnloadBlockIndex since that's not used anymore. Unfortunately, chainstatemanager_loadblockindex relies on CChainState::UnloadBlockIndex, so that needs to stay for now. This concludes backport of [[bitcoin/bitcoin#22564 | core#22564]] bitcoin/bitcoin@7ab07e0 Depends on D13091 Test Plan: `ninja all check-all` Reviewers: #bitcoin_abc, sdulfari Reviewed By: #bitcoin_abc, sdulfari Differential Revision: https://reviews.bitcoinabc.org/D13092
Fixes #22964
This is a small part of the work to accomplish what I described in 972c516:
::UnloadBlockIndex
manually resets a subset of our mutable globals in addition to unloading theChainstateManager
and clearing the mempool. The need for this manual reset (AFAICT) arises out of the fact that many of these globals are closely related to the block index (hence::UnloadBlockIndex
), and need to be reset with it.I've shot this "manual reset" gun at my foot several times while doing the de-globalize chainman work.
Thankfully, now that we have a
BlockManager
class that owns the block index, these globals should be moved under that class so that they can live and die with the block index. These moves, along with making the block index non-heap-based, eliminates:::UnloadBlockIndex
(this decision can at times seem almost arbitrary)::UnloadBlockIndex
or explicit~ChainstateManager
at all