Skip to content
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

Move parsing from init.cpp to where values are needed #17434

Closed
wants to merge 1 commit into from

Conversation

murchandamus
Copy link
Contributor

There appear to be a few more of these globals that are only used once. I could do more of them, if this sort of change is welcome.

Reference: #17383 (comment)

@@ -4729,6 +4728,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi

void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
{
static bool fCheckBlockIndex = gArgs.GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validation.cpp:4731:73: error: use of undeclared identifier 'chainparams'

    static bool fCheckBlockIndex = gArgs.GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks());

chainparams is not available in the scope, you should give a CChainParams argument in the function (like in the other functions) or use a global variable

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your review @emilengler. I've updated the CheckBlockIndex function header to take all of chainparams instead of only the consensusParams subset.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of parsing command line arguments every time this function is called, what about adding this as a member variable that is only parsed once (e.g. at the time of construction).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I was wrong this was parsed on every call (missed the static), however I'd still argue that this should be parsed at the time of construction (during initialization), so that parse errors can be returned to the caller.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(See also the reply by @ryanofsky which says exactly the same)

{
static bool fCheckBlockIndex = gArgs.GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In commit "Move CheckBlockIndex to validation.cpp" (340acd0)

Using a static local variable here isn't great because it makes makes the method harder to test and reuse. For example, it would be impossible to write a unit test that invokes this fucntion with different fCheckBlockIndex values. I haven't looked at all the other variables you want to extend this to, but using static locals could create real problems in other cases.

Would suggest making this a CChainState object member and initializing it in the constructor as an alternative to leaving it as a global, and in general avoiding static locals in application logic outside of specialized init code.

Also, it'd be good to clarify the goal and advantages of this PR. It's not obvious that it's better to parse config values when they are first used instead of in AppInitParameterInteraction. For example an advantage of parsing in AppInitParameterInteraction is that if the user specifies an invalid or unparseable value, they can get error feedback immediately, and code that runs later doesn't need to deal with errors.

@DrahtBot
Copy link
Contributor

DrahtBot commented Nov 11, 2019

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Conflicts

Reviewers, 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.

@jnewbery
Copy link
Contributor

@MarcoFalke

I'd still argue that this should be parsed at the time of construction...

@ryanofsky

It's not obvious that it's better to parse config values when they are first used instead of in AppInitParameterInteraction...

I think that AppInitParameterInteraction() should be used for checking for parameter interactions. In cases where a parameter is just being read and stored in a global, it's better to keep that in the translation unit where it's used to avoid polluting the global namespace. Parsing and storing parameters in their own components also reduce code churn hotspots and reduces merge conflicts and rebases.

Would suggest making this a CChainState object...

This seems reasonable, although note g_chainstate has't yet been initialized at the point that we call AppInitParameterInteraction.

@maflcko
Copy link
Member

maflcko commented Nov 11, 2019

This seems reasonable, although note g_chainstate has't yet been initialized at the point that we call AppInitParameterInteraction.

I think we agree that AppInitParameterInteraction is the wrong place for this, so obviously this code would need to be called when the chainstate is initialized.

@ryanofsky
Copy link
Contributor

Ok, would be good to look at specific cases, but the general tradeoff with this PR seems to be better error feedback for users and potentially simpler error handling in code vs "polluting the global namespace" and "code churn hotspots", and "merge conflicts and rebases".

This seems reasonable, although note g_chainstate has't yet been initialized at the point that we call AppInitParameterInteraction.

Yeah, sorry I was suggesting two different things. My first preference would be to keep parsing in AppInitParameterInteraction so it can happen as early as possible. But if that's a losing proposition, CChainState member would be preferable to static local.

If there are a lot of related options, it might make sense to introduce a struct similar to the coin control struct which could hold parsed options and be passed around where needed.

@maflcko
Copy link
Member

maflcko commented Nov 11, 2019

If there are a lot of related options, it might make sense to introduce a struct similar to the coin control struct which could hold parsed options and be passed around where needed.

We already do that for connman: https://dev.visucore.com/bitcoin/doxygen/struct_c_connman_1_1_options.html

@ryanofsky
Copy link
Contributor

I think we agree that AppInitParameterInteraction is the wrong place for this

I don't agree AppInitParameterInteraction is the wrong place to parse and validate options. Or, I don't think it's the wrong time to validate and parse options. If relevant parsing were moved into a ValidationParameterInteraction function to be more modular, that would seem good too.

@jnewbery
Copy link
Contributor

If relevant parsing were moved into a ValidationParameterInteraction function to be more modular, that would seem good too.

I agree this would be good. We do something similar for wallet:

if (!g_wallet_init_interface.ParameterInteraction()) return false;

@murchandamus
Copy link
Contributor Author

Alright. I've moved the fCheckBlockIndex value to the members of Validation.cpp and added a function ValidationParameterInteraction(const CChainParams& chainparams) function to validation.h and validation.cpp which I'm calling from init.cpp::AppInitParameterInteraction.

@murchandamus murchandamus force-pushed the trimGlobals branch 3 times, most recently from 4092aa1 to 43d7bab Compare November 11, 2019 23:01
The global fCheckBlockIndex is only used once and has no interaction.
Copy link
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review ACK eec8608. Seems good. Param checking is now split between:

  • InitParameterInteraction
  • AppInitParameterInteraction
    • WalletInit::ParameterInteraction
    • ValidationParameterInteraction

EDIT: Removed ack. Seems to have bug pointed out by Marco below that test code isn't calling ValidationParameterInteraction so gArgs.ForceSetArg("-checkblockindex", "1"); has no effect and checks are being skipped during testing.

@murchandamus
Copy link
Contributor Author

Code review ACK eec8608. Seems good. Param checking is now split between:

* InitParameterInteraction

* AppInitParameterInteraction
  
  * WalletInit::ParameterInteraction
  * ValidationParameterInteraction

Great, thanks. I'm thinking that I'll add another commit to move fCheckpointsEnabled in the same style next.

@maflcko
Copy link
Member

maflcko commented Nov 12, 2019

Could you add a motivation to the OP explaining the change, please. This is still a global (while hidden from other compilation untis) and it seems that the only way for tests to set this is to force set the argument and then call ValidationParameterInteraction.

It doesn't look like the tests are currently calling ValidationParameterInteraction. That looks like a regression and raises the question if the change and bundled complication is worth it.

If the long-term goal is to limit parameter parsing to a single function per module FooParameteriInteraction, then maybe gArgs should be passed in to ValidationParameterInteraction, so that it some day can be removed (or limited to live in init.cpp).

@ryanofsky
Copy link
Contributor

Could you add a motivation to the OP explaining the change, please. This is still a global (while hidden from other compilation untis) and it seems that the only way for tests to set this is to force set the argument and then call ValidationParameterInteraction.

I assume that is actually the goal of the PR: to limit access to the global so it's internal to validation.cpp and outside code (including test code) can't access it without setting a up a real configuration and initializing it with ValidationParameterInteraction.

I don't know if this a good goal or not. I'm +0. Maybe isolating these global variables more will reduce merge conflicts and so on as suggested #17434 (comment).

It doesn't look like the tests are currently calling ValidationParameterInteraction. That looks like a regression and raises the question if the change and bundled complication is worth it.

That does seem like a bug. Could be fixed by test code calling ValidationParameterInteraction, or by changing the default value from false to true:

bool fCheckBlockIndex = false;

ValidationParameterInteraction could also take an option to set up all relevant variables for testing without the test framework having to poke specific variables.

If the long-term goal is to limit parameter parsing to a single function per module FooParameteriInteraction, then maybe gArgs should be passed in to ValidationParameterInteraction, so that it some day can be removed (or limited to live in init.cpp).

This would be reasonable. Could be partially implemented here. Implementing it fully might expand this PR too much to non-validation code.

@DrahtBot
Copy link
Contributor

🐙 This pull request conflicts with the target branch and needs rebase.

@maflcko
Copy link
Member

maflcko commented Apr 25, 2020

@xekyo Are you still working on this? Otherwise I suggest to close

@fanquake fanquake closed this Jul 26, 2020
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Feb 15, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants