Speedy trial support for versionbits #21377

pull ajtowns wants to merge 9 commits into bitcoin:master from ajtowns:202103-bip9-speedy-trial-support changing 9 files +311 −158
  1. ajtowns commented at 9:21 am on March 6, 2021: member

    BIP9-based implementation of “speedy trial” activation specification, see https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2021-March/018583.html

    Edge cases are tested by fuzzing added in #21380.

  2. DrahtBot added the label Consensus on Mar 6, 2021
  3. DrahtBot added the label Validation on Mar 6, 2021
  4. evoskuil commented at 10:21 am on March 6, 2021: none
    +1
  5. jonatack commented at 12:06 pm on March 6, 2021: member
    Concept ACK
  6. michaelfolkson commented at 12:41 pm on March 6, 2021: contributor
    Concept ACK
  7. roconnor-blockstream commented at 2:14 pm on March 6, 2021: contributor
    I take it that this PR doesn’t preclude also migrating to BIP8-style height based activation?
  8. benthecarman commented at 2:56 pm on March 6, 2021: contributor

    I take it that this PR doesn’t preclude also migrating to BIP8-style height based activation?

    Concept ACK but would much rather have height based activation

  9. in src/test/versionbits_tests.cpp:25 in 3ba9283a47 outdated
    20+    case ThresholdState::DEFINED:   return "DEFINED";
    21+    case ThresholdState::STARTED:   return "STARTED";
    22+    case ThresholdState::LOCKED_IN: return "LOCKED_IN";
    23+    case ThresholdState::ACTIVE:    return "ACTIVE";
    24+    case ThresholdState::FAILED:    return "FAILED";
    25+    } // no default case, so the compiler can warn about missing cases
    


    unknown commented at 3:47 pm on March 6, 2021:
    nit: Can we add empty default statement which does nothing but avoids compiler warning and possible errors in future?

    sipa commented at 5:14 pm on March 6, 2021:

    That’s exactly what the lack of default case here does.

    If a case is missed, the compiler will warn.


    evoskuil commented at 8:44 pm on March 6, 2021:
    This bothered be too. IMO better to use default. Enums are bounded. The default state can reasonably be either ACTIVE or FAILED. This can be future-proofed by using a static_asset on the enum, which documents the assumption and guarantees catching it at compile time. Warnings aren’t nearly as safe.

    ajtowns commented at 9:00 am on March 7, 2021:
    It’s already safe as a missing default case will simply cause the return "" after the switch to execute. The only time the return value of this function is used is to print a message if a test case is already failing, so the consequences of having an empty string are trivial – CI is already failing at that point. Additionally, we bump those missing-case warnings to errors (-Werror=switch) when configured with enable-werror and I think we have that enabled on most CI builds (all except for win64?).

    evoskuil commented at 9:35 am on March 7, 2021:
    No worries, I agree it’s a nit. Just don’t like to rely on comments and warnings.

    Sjors commented at 4:47 pm on March 7, 2021:
    Note that the first two commits of this PR have already been merged, in #21334.
  10. JeremyRubin commented at 4:35 pm on March 6, 2021: contributor

    Concept ACK.

    It does seem like there is a preference for height based activation as opposed to time.

    i’d further request that – to the extent this is getting BIP’d, that the activation date must not be within the range of the provided interval or we introduce some other sort of quantization / min interval parameter (e.g., every 3 block-months there is a new-rule activation window and we use the next one, or the activation blocks is something like current + max(2016*N, earliest - current).

  11. michaelfolkson commented at 4:48 pm on March 6, 2021: contributor

    I don’t know (though I can guess) why people are trying to refer to BIP 9 now instead of BIP 8, especially as height based activation seems the superior option.

    As a reminder, there have been community IRC meetings (which many Core contributors attended and disclosed their views) where there was broad consensus on using revised BIP 8 e.g. http://gnusha.org/taproot-activation/2021-02-02.log

    Rusty’s conclusion post that meeting was “Unanimous support for BIP 8. RIP BIP 9.”

    The outline of the various proposals used BIP 8 rather than BIP 9: https://en.bitcoin.it/wiki/Taproot_activation_proposals

    And various media articles reporting on the discussion referred to BIP 8 rather than BIP 9.

    Though I don’t expect it to scupper the momentum this promising proposal is generating if people want to call this BIP 9 (or do mental gymnastics by opening a new BIP) please be clear on why it doesn’t fit within the template of BIP 8.

    Other than that, I am delighted with the progress this proposal is making and having a PR up in such quick time really is great, so many thanks for your work on this.

    edit: Thinking about this more I don’t think it would be mental gymnastics to open a new BIP for “Speedy trial” especially if it doesn’t fit cleanly into a more flexible BIP 8. New BIP or more flexible BIP 8. BIP 9 is dead and if “Speedy trial” could be used for other soft forks it shouldn’t be included in the Taproot BIPs.

  12. roconnor-blockstream commented at 4:57 pm on March 6, 2021: contributor

    I believe the best way to proceed would be to rebase a BIP8-height PR on top of this PR. Once this PR is merged, we have the ability to produce an acceptable set of parameters for speedy trial. If then the subsequent BIP8-height PR is merged afterwards then we get, what I believe to be, a superior set of activation parameters, but if the BIP8-height subsequent PR fails for whatever reason, that is okay.

    I’m anticipating test cases added to this PR that would in turn be valuable for the BIP8-height PR, and the BIP8-height speedy trial will need an analogous activation_height field as well.

  13. DrahtBot commented at 6:35 pm on March 6, 2021: member

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

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #21401 (Refactor versionbits deployments to avoid potential uninitialized variables by achow101)
    • #19573 (Replace unused BIP 9 logic with draft BIP 8 by luke-jr)
    • #17783 (util: Fix -norpcwhitelist, -norpcallowip, and similar corner case behavior by ryanofsky)
    • #17581 (refactor: Remove settings merge reverse precedence code by ryanofsky)
    • #17580 (refactor: Add ALLOW_LIST flags and enforce usage in CheckArgFlags by ryanofsky)
    • #17493 (util: Forbid ambiguous multiple assignments in config file by ryanofsky)

    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.

  14. JeremyRubin commented at 7:36 pm on March 6, 2021: contributor

    @roconnor-blockstream that sounds like one reasonable plan of attack – merging this now is a concrete step, patching to use height can occur independently. I do fear there would not be consensus on a non-height based activation proposal, so it may be important to not rest on laurels for accepting those changes.

    Making the case for BIP8 merge first then these changes, the BIP8 code is much more reviewed presently (with no code nacks, just conceptual ones?) and it may be a better utilization of developer time to rebase this onto BIP8.

    I think it would be best to hear from @luke-jr and @ajtowns on their preference as they seem to be primary contributors on the implementation/review of both.

  15. benthecarman commented at 8:35 pm on March 6, 2021: contributor

    Once this PR is merged, we have the ability to produce an acceptable set of parameters for speedy trial. If then the subsequent BIP8-height PR is merged afterwards

    This seems dangerous as we could have people compile and start running after the activation params are merged, merging a following change to the params could cause people to be running different activation logic (one time based, one hieght based).

  16. roconnor-blockstream commented at 8:48 pm on March 6, 2021: contributor

    @benthecarman. Not to worry this PR doesn’t have activation parameters in it. I would expect merger of activation parameters to be delayed until after a BIP8-height PR is done consideration.

    That being said, I’ve chatted a bit with @achow101 and it may be the case that a BIP8-min-activation-height PR ends up being easier to implement as a stand alone PR rather than on top of this one. There are issues with merging, and backporting etc. So I’m going to stop myself from further back-seat deving and let the pros figure out the best way to hammer out PRs.

  17. ajtowns commented at 10:17 pm on March 6, 2021: member

    Making the case for BIP8 merge first then these changes, the BIP8 code is much more reviewed presently (with no code nacks, just conceptual ones?) and it may be a better utilization of developer time to rebase this onto BIP8.

    I don’t think the BIP8 code is adequately reviewed yet – we had an off-by-one bug that would have left lot=true and lot=false nodes out of consensus found this past week, that wasn’t discovered by reviewers or the automated tests. (Currently it isn’t a clean merge either)

    The main benefits of bip8 are lockinontimeout=true related – that it specifies the option at all, and that it’s height based, so loss of hashpower (due to miners preferring a chain that is invalid according to lockinontimeout=true) doesn’t reduce the number of retarget periods available for activation. So I don’t think there’s any real benefits to doing this via bip8 rather than bip9 as far as the “Speedy trial” itself goes; but if bip8/height based code is thoroughly reviewed and ready to merge prior to setting activation parameters, that seems fine too as far as I can see.

  18. michaelfolkson commented at 10:34 pm on March 6, 2021: contributor

    I don’t think the BIP8 code is adequately reviewed yet – we had an off-by-one bug that would have left lot=true and lot=false nodes out of consensus found this past week, that wasn’t discovered by reviewers or the automated tests. @ajtowns: Are you referring to a bug Sjors found in the tests? That is hardly a consensus bug for LOT=true and LOT=false nodes on mainnet but you are right to say it wasn’t yet ready to be merged. I have since recommended it be closed as some have expressed that they would not merge it with any LOT code (even if dead code).

  19. ajtowns commented at 11:05 pm on March 6, 2021: member
    @michaelfolkson No, that one wasn’t a bug. I’m referring to this one.
  20. harding commented at 5:27 am on March 7, 2021: contributor

    Concept ACK. Thank you, @ajtowns! I’m happy to see that the change really is minimal.

    Do I understand correctly from reading the code that activation will occur in the first block of the next retarget period after the activation_time? E.g., if the minimum activation time just passed but the height is 1234, the first block where taproot’s rules are enforced still won’t be until height 2016? That seems fine to me for the sake of code simplicity, but it does mean that, if it looks like activation_time is going to fall near a retarget block, we’ll have to communicate that taproot could either happen tomorrow or two weeks from now. Maybe to avoid that communication challenge, it’d be better to use a height? @michaelfolkson

    I don’t know (though I can guess) why people are trying to refer to BIP 9 now instead of BIP 8, especially as height based activation seems the superior option.

    As mentioned in my email, I thought basing the proposal on the existing BIP9 code in Bitcoin Core (as done in this PR) would be the smallest change and so fastest to get reviewed, merged, and released. Since the goal of the Speedy Trial proposal is speed (with safety), that seems highly advantageous to me.

    I completely agree that using block heights for the start and timeout parameters has advantages for this proposal (giving miners a known number of signaling periods) and that, as also mentioned in my email, getting BIP8 included in the code now would have advantages if Speedy Trial fails and we decide to use BIP8 as originally imagined for a follow-up activation attempt.

    My preference would be for whatever solution is most preferred by reviewers.

  21. MaxHillebrand commented at 2:37 pm on March 7, 2021: none
    concept ACK.
  22. in src/chainparams.cpp:81 in d614299d6d outdated
    79@@ -80,7 +80,7 @@ class CMainParams : public CChainParams {
    80         consensus.nPowTargetSpacing = 10 * 60;
    81         consensus.fPowAllowMinDifficultyBlocks = false;
    82         consensus.fPowNoRetargeting = false;
    83-        consensus.nRuleChangeActivationThreshold = 1916; // 95% of 2016
    84+        consensus.nRuleChangeActivationThreshold = 1815; // 90% of 2016
    


    Sjors commented at 4:51 pm on March 7, 2021:
    Maybe add a note here to never change this unless all soft forks are burried. Alternatively maybe we should have a separate variable for this speedy trial.

    MarcoFalke commented at 12:20 pm on March 24, 2021:
    Can be fixed (in a follow-up refactor) with commit 2be730c31de5e1496d893d2fe121af70879ceadf + commit 50eb7f0b28ff1afb816c49870344f0a210b6be6e
  23. Sjors commented at 5:19 pm on March 7, 2021: member

    Concept ACK for speedy trial support. However I’m not convinced we should permanently drop the BIP9 threshold to 90%. @JeremyRubin wrote:

    merging this now is a concrete step, patching to use height can occur independently

    I’m fine with a height based approach too; slightly more code changes, but it seems easier to communicate when things happen. I also agree with Jeremy’s point. Nothing here is set in stone until we actually apply it to Taproot (or another soft fork).

    There is one downside though, in addition to more review work: other implementations probably already have BIP 9 code ready to go. Although compared to implementing all of Taproot this is not a big deal (and without implementing Taproot they can’t verify the new consensus rules anyway).

    Making the case for BIP8 merge first then these changes, the BIP8 code is much more reviewed presently (with no code nacks, just conceptual ones?)

    For me that’s only an option if the LOT=true stuff is stripped from that PR.

    It doesn’t seem terribly complicated to cherry-pick 9762562cc6a2c771ad0ddfb055ea17b90085425c, bcec418304ea3dd524cb3591e4af1b77a04cc968 (though I suggest a fresh BIP for the speedy trial) and a few more.

  24. in src/consensus/params.h:33 in d614299d6d outdated
    28@@ -29,6 +29,8 @@ struct BIP9Deployment {
    29     int64_t nStartTime;
    30     /** Timeout/expiry MedianTime for the deployment attempt. */
    31     int64_t nTimeout;
    32+    /** If lock in occurs, delay activation until at least this time. */
    33+    int64_t activation_time;
    


    Sjors commented at 5:25 pm on March 7, 2021:
    We could add reduced_threshold which, if non-zero, overrides nRuleChangeActivationThreshold.

    JeremyRubin commented at 8:29 pm on March 8, 2021:
    I think we can worry about genericity if we ever need to add 2 simultaneous vbit releases… which I’ve heard rumor not too many people think we’d be likely to ever do that (although I think it is fine)

    Sjors commented at 11:29 am on March 9, 2021:
    IIUC with the latest change, nRuleChangeActivationThreshold is only used for the “the might be an unknown rule change” warning system. Each deployment now has its own threshold.

    Sjors commented at 12:27 pm on March 9, 2021:
    See also https://github.com/bitcoin/bitcoin/pull/21392/commits/9e50401c8a593c569f586786d84554c027fd8e8d which in addition lowers the warning threshold even further.
  25. ajtowns commented at 2:58 am on March 8, 2021: member

    but it does mean that, if it looks like activation_time is going to fall near a retarget block, we’ll have to communicate that taproot could either happen tomorrow or two weeks from now.

    I think it’s worse than that – if activation_time falls very near a retarget block, I think you could have the eleven last blocks of the retarget period have timestamps (relative to activation_time) like: -3000, -2400, -1800, -1200, -600, 0, 600, 1200, 1800, 2400, 3000 giving the last block a median time which satisfies MedianTimePast() >= activation_time, so the next block would switch to ACTIVE and people would likely start transacting with taproot, perhaps even racing to get a tx in the first block as some did with segwit, and setting nlocktime to ensure their transaction isn’t mined early and the funds stolen.

    However in that case a 2 block reorg could change the last block’s timestamp to -400 (still greater than the mediantime of the prior block which is likely -600), which would change its median time from 0 to -400, which would then cause the new first block of the next period to still be in state LOCKED_IN. At that point replaying the taproot-using transactions people had sent prior to the reorg would allow funds to be stolen as taproot is not yet active after the reorg, and nlocktime would not have protected you.

    (EDIT: fixed mediantime rules misinterpretation – blocks must have a timestamp greater than the previous block’s mediantime; block’s mediantime does include their own timestamp. And add the following…)

    I think the conclusion from this is just “the height at which you transition from LOCKED_IN to ACTIVE must be fully determined as soon as you transition from STARTED to LOCKED_IN”. That way the entire LOCKED_IN period has to be reorged if you want to steal funds protected by both nlocktime and the new rules.

  26. ajtowns force-pushed on Mar 8, 2021
  27. ajtowns commented at 3:27 am on March 8, 2021: member
    Bumped to min_activation_height instead of activation_time; also helps github notice that #21334 already merged.
  28. Sjors commented at 9:38 am on March 8, 2021: member
    Shameless plug for #19013 which adds backwards compatibility testing for v0.21; this should be useful when testing soft fork deployment.
  29. JeremyRubin commented at 8:28 pm on March 8, 2021: contributor
    based on the reasoning here: https://gist.github.com/michaelfolkson/92899f27f1ab30aa2ebee82314f8fe7f#gistcomment-3658078 I have very limited concerns around using start/stop times (could be wrong) @ajtowns that’s a great point – MTP just has ambiguity baked into it, and we should prefer heights for the activation point.
  30. ajtowns force-pushed on Mar 9, 2021
  31. ajtowns force-pushed on Mar 9, 2021
  32. ajtowns force-pushed on Mar 9, 2021
  33. ajtowns force-pushed on Mar 9, 2021
  34. ajtowns commented at 9:56 am on March 9, 2021: member
    Rebased on top of versionbits fuzzing harness changes in #21380 – this captures a bunch of the “it’s not really bip9 anymore” refactoring, without getting too hard to read, and should vastly improve test coverage.
  35. Sjors commented at 12:32 pm on March 9, 2021: member
    I think a fully height based approach is easier to reason about, so I’m going to review #21392 first.
  36. ajtowns force-pushed on Mar 16, 2021
  37. ajtowns commented at 2:59 pm on March 16, 2021: member
    #21380 updated to just provide a fuzz test and not do the refactoring, so this now also avoids refactoring – it adds support for min_activation_height, adds support for the unit and fuzz tests to validate behaviour, and drops the threshold from 95% to 90%.
  38. amitiuttarwar commented at 9:14 pm on March 16, 2021: contributor

    concept ACK, I agree with @harding’s line of reasoning:

    As mentioned in my email, I thought basing the proposal on the existing BIP9 code in Bitcoin Core (as done in this PR) would be the smallest change and so fastest to get reviewed, merged, and released. Since the goal of the Speedy Trial proposal is speed (with safety), that seems highly advantageous to me.

    Combined with how minimal this change is- the bulk of this PR is tests, the changes in the source code are +26/-4 lines over 2 commits.

    I’ve taken a first pass and it all looks reasonable, but I plan to do a full review soon. These changes are simple but I’m currently trying to gain the appropriate codebase context.

  39. ajtowns force-pushed on Mar 17, 2021
  40. ajtowns marked this as ready for review on Mar 18, 2021
  41. luke-jr commented at 7:12 pm on March 18, 2021: member
    Concept NACK to MTP.
  42. michaelfolkson commented at 10:04 pm on March 18, 2021: contributor

    I don’t have a strong view but it appears that @achow101 in #21392 and just by reading the comments in this PR @luke-jr @benthecarman @JeremyRubin @Sjors @roconnor-blockstream @harding all have a preference for a fully height based approach over any use of MTP.

    If that isn’t the case please correct me. And if anyone else has a view on entirely block height versus some limited use of MTP can you post it on IRC (##taproot-activation)?

    The fuzzing PR looks very cool. On that PR @MarcoFalke also expresses an expectation that activation will be based on block heights.

    edit: Some additional comments from today’s Core dev meeting

    “it comes down to mtp vs block height” @achow101

    “the main difference between these two PRs are using block height vs MTP " @amitiuttarwar

    “with mtp, we run the risk of losing 2 signaling periods. with already few signaling periods, this has the possibility of failing to activate due to bad luck” @achow101

    “the property that achow101’s PR improves is a fixed (number) of signals” @JeremyRubin

  43. JeremyRubin commented at 11:53 pm on March 18, 2021: contributor

    I have a preference for fully height based design, correct. But I think that given the code base as it stands today, and comparing the diffs across the PR, and the tight timeline desired from merge to release using MTP really is a decent option, especially since the bulk of the code is already more widely tested. Longer term it makes sense to fully develop and test height based – whether we do that, I don’t know. @MarcoFalke’s comment is a slight misunderstanding I believe of how the fuzzer simulates time; I recall AJ saying (somewhere) that block times are steady interval in the fuzzing so that it doesn’t matter (can’t find that comment now tho). Is that correct?

    I also emphasized numerous times in the meeting that I think for the purposes of a 3 month ST, forecasting to be a mid-period start/stopheight means that drift is very unlikely to hurt us, so the notion that we’re “risking two periods” is a bit moot to me.

    The counterarguments can be made that we have bigger issues to solve for if we miss two periods as a result of a hashrate decrease (where did the hashrate go – we probably wouldn’t want to activate if we’re missing a big chunk of hashrate contemporaneously anyways) and that on the flip side, a hashrate increase would yield in the block heights going by more quickly which means there is less real time to coordinate enabling signaling. There are pros and cons either way, I think that height is superior longer term, but in this case I don’t think there is sufficient technical risk to disqualify this PR as an activation method in favor of start/stop height. @luke-jr I would appreciate if you could justify your concept NACK more explicitly as I do not understand your concern. @amitiuttarwar made a great point in the meeting

    my two cents: I don’t think either PR is close to being RFM, both need significantly more review. looks like #21392 is currently failing CI. If the goal is to quickly merge with safety, I think #21377 makes more sense (as I stated on the PR)

    Rough consensus and running code – it seems to me that this is closer to a mergeable state. If there’s a strong reason to the contrary, or if people decide to invest the requisite energy to get #21392 reviewed and RTM before then ok.

  44. ajtowns commented at 0:41 am on March 19, 2021: member
    @JeremyRubin ’s comment above pretty much matches my opinion – heights seem better in the long term, and are much better if there’s a risk of a UASF ending up with a minority hashrate and not being abandoned. But neither of those are concerns here; and making a small modification to bip9 seems a lot easier to get right on a tight deadline to me.
  45. ajtowns force-pushed on Mar 19, 2021
  46. ajtowns commented at 7:40 am on March 19, 2021: member
    Rebased for updates to fuzzer, removed redundant override of MinActivationHeight for WarningBitsConditionChecker.
  47. michaelfolkson commented at 7:59 pm on March 19, 2021: contributor
    If we’re in agreement (which we appear to be) that a fully height based approach over any use of MTP is superior (and an alternative PR has implemented it) going for the inferior option to meet a deadline doesn’t seem optimal to me. We don’t have much wiggle room with regards to timings if we want activation pre December but we have enough to ensure the superior option is implemented imo. Especially as fully height based isn’t a huge change.
  48. jnewbery commented at 12:57 pm on March 20, 2021: member
    Can you rebase on master to drop the fuzzer commit?
  49. in src/consensus/params.h:36 in 5dcc8f70e4 outdated
    28@@ -29,6 +29,11 @@ struct BIP9Deployment {
    29     int64_t nStartTime;
    30     /** Timeout/expiry MedianTime for the deployment attempt. */
    31     int64_t nTimeout;
    32+    /** If lock in occurs, delay activation until at least this block
    33+     *  height.  Note that activation will only occur on a retarget
    34+     *  boundary.
    35+     */
    36+    int min_activation_height{0};
    


    jnewbery commented at 1:00 pm on March 20, 2021:
    Would it make sense to always set min_activation_height to a retarget boundary (and assert that)?

    ajtowns commented at 10:25 pm on March 20, 2021:
    If it’s not on a retarget boundary, it will be treated as if it were at the next higher retarget boundary; so would be misleading but not actually buggy. Added a sanity check to versionbits_tests.

    ariard commented at 4:47 pm on March 22, 2021:

    With this bip9-amended model, there is no such thing as a “max_activation_height” ? Either we reach LOCKED_IN state during the starttime/timeout range and delay activation until a fixed point or we fail back to FAILED ? Fixed point can be time-based or height-based but in anycase this is not an activation window with lower/upper bounds.

    To clear this potential ambiguity, I think this variable should just be called activation_height and the delayed semantic be documented around ThresholdState::ACTIVE as its state transition condition isn’t true anymore.

    Further, consensus risks around this variable setting should be documented.

    Let’s say you have client version=X reach locked_in at block height T, and activation height is scheduled for block height T+N. If a client version=Y is deployed with same activation logic but activation height M, with M < N, it will start to enforce taproot rules at height M.

    Do you have a risk of chain split between X and Y as this latter client version will consider invalid-taproot block between M and N as valid ?


    ajtowns commented at 2:50 am on March 23, 2021:

    It’s a minimum activation height, because activation could be later than that if min_activation_height is reached prior to timeout MTP being reached. With a fast enough increase in hashrate, you could probably reach any reasonable min_activation_height prior to timeout MTP being reached; and of course min_activation_height could be deliberately set in prior to or in the middle of the signalling period.

    minimum activation height is a fixed value, not a delta; so for your example, if client version X specifies min_activation_height H1 and version Y specifies min_activation_height H2 with H2<H1 then you’d expect Y to be enforcing different rules between H2..H1, because that’s what you’ve told it to do. The only time they’d agree on the rules is if lockin occurs at a height >= H1-2016.

    Any change to deployment parameters can cause a change to what a client considers to be a valid block, and cause a consensus split; this isn’t any different to changing the bit, starttime, timeout, period or threshold…


    ariard commented at 5:06 pm on March 24, 2021:

    It’s a minimum activation height, because activation could be later than that if min_activation_height is reached prior to timeout MTP being reached. With a fast enough increase in hashrate, you could probably reach any reasonable min_activation_height prior to timeout MTP being reached; and of course min_activation_height could be deliberately set in prior to or in the middle of the signalling period.

    Right, that said if you want to achieve ST goal’s of backend delay, you should have a retarget period buffer between expected height of timeout and min_activation_height to swallow even a doubling of hashrate, or any other kind of scaling factor we deem physically unlikely to happen. Though this conversation belongs more to activation parameters selection. I agree, this logic could serve beyond ST-style of deployment.

    minimum activation height is a fixed value, not a delta; so for your example, if client version X specifies min_activation_height H1 and version Y specifies min_activation_height H2 with H2<H1 then you’d expect Y to be enforcing different rules between H2..H1, because that’s what you’ve told it to do. The only time they’d agree on the rules is if lockin occurs at a height >= H1-2016.

    I’m just concerned about successfully LOCKED_IN then having uasf folks bothered by the backend delay and willingly to fasten activation by tweaking min_activation_height. I’m not sure the immutable semantic of min_activation_height is that much understood. Though there is a thousand ways to fork yourself of the network if you have the intent, we can’t prevent all of them to happen…

    Any change to deployment parameters can cause a change to what a client considers to be a valid block, and cause a consensus split; this isn’t any different to changing the bit, starttime, timeout, period or threshold…

    Yes, but I think “Once activation parameters have been released for a consensus change, they MUST NOT be changed to avoid risks of consensus split until reaching a final deployment state” on top BIP9Deployment is good practice. Future code reviewers might not be as much familiar than us w.r.t to those consensus subtleties.


    instagibbs commented at 2:20 am on April 8, 2021:

    I’m just concerned about successfully LOCKED_IN then having uasf folks bothered by the backend delay and willingly to fasten activation by tweaking min_activation_height

    I don’t think there’s any community concerns with the backend delay. Making sure economic majority activates is in everyone’s interests.


    JeremyRubin commented at 6:04 pm on April 8, 2021:

    I think this is also desirable in the future in certain circumstances, and unlikely to be an issue for ST.

    1. For ST: The time is really short. Backing it up after lock in would be unfair to smaller miners (< 10% total hash) who need more time to upgrade, so it would run counter to norms…
    2. In the future: it would be possible to do a Long-Range-Activation, e.g. a ST but with min_active_height far in the future (e.g., 2 years). Once it is locked in, then developers can start working on broader ecosystem support & wallet software integration knowing when it will be available. Once that seems ready, it is a soft-fork to decrease the min_height in a future release (but it still should be signalled for, but even if it isn’t decreasing it is just a flag day soft fork, not hard fork AFAIU). I think that sounds like too much unnecessary fuss, but I could imagine it being used.
  50. ajtowns force-pushed on Mar 20, 2021
  51. ajtowns force-pushed on Mar 20, 2021
  52. ajtowns force-pushed on Mar 21, 2021
  53. ajtowns commented at 2:58 am on March 21, 2021: member
    Rebased now that fuzzer is merged; added fuzzing of min_activation_height even in always/never active cases; added sanity check of settings via unit test; added min_activation_height to getblockchaininfo output.
  54. in src/versionbits.h:60 in 85485fb1ab outdated
    56@@ -57,6 +57,7 @@ class AbstractThresholdConditionChecker {
    57     virtual bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const =0;
    58     virtual int64_t BeginTime(const Consensus::Params& params) const =0;
    59     virtual int64_t EndTime(const Consensus::Params& params) const =0;
    60+    virtual int MinActivationHeight(const Consensus::Params& params) const { return 0; }
    


    jnewbery commented at 10:22 am on March 22, 2021:

    Is there a reason that this isn’t pure virtual (when the other simple getter functions are)?

    0    virtual int MinActivationHeight(const Consensus::Params& params) const =0;
    

    ajtowns commented at 2:25 am on March 23, 2021:
    None of the others have reasonable defaults, but min activation height does. Also, having min activation height default to zero means the condition checker maintains the same behaviour if left as default, which then allows the tests to be patched independently to test the new behaviours, and also means there’s no change needed to the warning checker in validation.

    JeremyRubin commented at 5:37 pm on April 8, 2021:
    Noting that MinActivationHeight=0, even though it is a height, is easily compatible with any Signet MTP configuration activation parameters (it’s just disabling the delay between locked-in and active, which is OK for a signet since it is managed by a signer anyways).
  55. in src/rpc/blockchain.cpp:1260 in 85485fb1ab outdated
    1257@@ -1258,6 +1258,9 @@ static void BIP9SoftForkDescPushBack(UniValue& softforks, const std::string &nam
    1258         statsUV.pushKV("possible", statsStruct.possible);
    1259         bip9.pushKV("statistics", statsUV);
    1260     }
    1261+    if (consensusParams.vDeployments[id].min_activation_height != 0) {
    1262+        bip9.pushKV("min_activation_height", consensusParams.vDeployments[id].min_activation_height);
    


    ariard commented at 4:51 pm on March 22, 2021:
    Note, think to update getblockchaininfo documentation around “height” in consequence.

    ajtowns commented at 6:45 am on March 24, 2021:
    Added docs for min_activation_height field; the docs for height don’t need updating?

    instagibbs commented at 2:27 am on April 8, 2021:
    why not just return the variable consistently? 0 is fine?

    JeremyRubin commented at 6:07 pm on April 8, 2021:

    :+1: – may as well always return it rather than having client software infer a value from absence.

    This can be handled in a follow up PR IMO (which cleans up the BIP9 naming too, since we’ve diverged)

  56. ariard commented at 5:34 pm on March 22, 2021: member

    Assuming we would like a ST-style taproot activation logic ready by April 3rd, I’m leaning toward reusing current bip9. It has been successfully exercised at least twice for csv and segwit softforks and the diff to achieve ST semantic is far smaller to focus on.

    Ideally, height-based locked_in parameters would be better, as consensus rules around MTP are quite loose. AFAIU, it doesn’t prevent a coalition of miners to scale down their mined blocks nTime to MTP + 1, thus preventing to reach a bip9 starttime at the expected real-world time. That said, if we see such behavior showing up, it would be at least a good proof of a malicious intent and ST would be a success by providing new, useful data to the softfork conversation . Beyond malicious MTP manipulation, there is a still a concern about a too-small signaling window, but AFAICT selecting starttime/timeoout in mid expected retarget period should minimize such risks.

    This PR might also set a precedent to prefer refinement of existent activation logic in function of social consensus aleas. If so, we should be careful about “activation height” not introducing unintended chain split risks or permanent restriction of softfork deployment flexibility like bip34 did it w.r.t to versionbits. If you have time, maybe propose a quick amendment to bip9, formalizing might help reasoning about such technical risks ?

    That said, I’ll look on competing #21392 before to focus on one or the other.

  57. ajtowns commented at 2:40 am on March 23, 2021: member

    Ideally, height-based locked_in parameters would be better, as consensus rules around MTP are quite loose. AFAIU, it doesn’t prevent a coalition of miners to scale down their mined blocks nTime to MTP + 1, thus preventing to reach a bip9 starttime at the expected real-world time

    Since it uses median time past, you’d need to be doing that for 6 out of every 11 blocks, which would require >50% of hashpower, and you’d have to take care to not manipulate the nTime of the first and last block of each retarget period so as not to make difficulty skyrocket; but you’d only need 10% of hashpower to block signalling.

    Losing hashpower was the concern for MTP with bip148, and it could be a theoretical concern here too – if you lost 75% of hashpower, then instead of 14 weeks between timeout and starttime resulting in 6 or 7 retarget periods, it could result in just 1 or 2 retarget periods. But provided no new rules are added to cause blocks to be considered invalid (such as the required signalling rule in bip148), there’s no reason to expect a drastic loss of hashpower.

    But as you point out, in any event, we’d learn something new, and could try again afterwards taking that new knowledge into account.

  58. in src/test/versionbits_tests.cpp:91 in aed054ff35 outdated
    84@@ -77,14 +85,16 @@ class VersionBitsTester
    85     int num;
    86 
    87 public:
    88-    VersionBitsTester() : num(0) {}
    89+    VersionBitsTester() : num(1000) {}
    90 
    91     VersionBitsTester& Reset() {
    92+        num = num - (num % 1000) + 1000;
    


    sipa commented at 0:49 am on March 24, 2021:

    In commit “tests: test versionbits delayed activation”

    Add a comment: “round up to the next strictly larger multiple of 1000”?


    ajtowns commented at 6:45 am on March 24, 2021:
    Added a comment, though not that
  59. in src/test/versionbits_tests.cpp:265 in aed054ff35 outdated
    259@@ -226,6 +260,13 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
    260         // Make sure that no deployment tries to set an invalid bit.
    261         BOOST_CHECK_EQUAL(bitmask & ~(uint32_t)VERSIONBITS_TOP_MASK, bitmask);
    262 
    263+        // Check min_activation_height is on a retarget boundary
    264+        BOOST_CHECK_EQUAL(mainnetParams.vDeployments[i].min_activation_height % mainnetParams.nMinerConfirmationWindow, 0);
    265+        // Check min_activation_height is 0 for ALWayS_ACTIVE and never active deployments
    


    sipa commented at 0:57 am on March 24, 2021:

    In commit “tests: test versionbits delayed activation”

    Nit: WEirD CapiTAlIZatIoN


    ajtowns commented at 6:46 am on March 24, 2021:
    Should’ve bought the dip while you could, now it’s all uppercase.
  60. sipa commented at 1:10 am on March 24, 2021: member
    Code review ACK 85485fb1abb4fc241548fa65c26f7a6f6f6e2423
  61. ajtowns force-pushed on Mar 24, 2021
  62. ajtowns force-pushed on Mar 24, 2021
  63. ajtowns commented at 6:46 am on March 24, 2021: member
    Rebased on top of #21489 merge.
  64. MarcoFalke commented at 9:15 am on March 24, 2021: member

    review ACK 4c7d858167a7f8cd4cb6b09787f07929d2abc3a1 🍦

    Signature:

     0-----BEGIN PGP SIGNED MESSAGE-----
     1Hash: SHA512
     2
     3review ACK 4c7d858167a7f8cd4cb6b09787f07929d2abc3a1 🍦
     4-----BEGIN PGP SIGNATURE-----
     5
     6iQGzBAEBCgAdFiEE+rVPoUahrI9sLGYTzit1aX5ppUgFAlwqrYAACgkQzit1aX5p
     7pUiG6wwAsBIP2/QWvAMfor3Wt8lSqJnHmHmBzmZY0Vw2Z4QnM+qknHPBHnR9adkb
     8x/G5m/gUnBGYOxMW2AJsRvooU8WiVfvxfhlZE95cjknYkTKTiirsum9dU7idbCxr
     99ODwd44LwO1Y0KknerNXPyTwQ0slHvtMGGpcWgHdHHqSchrVy9aduBNSGbs+i+dh
    10e2YqhcKh2cGtygdpbkDFJrpF3102hDfSr2A85CO0C1lmKpSAE1v0kjltZrshUoVY
    11NPL9ap/srainn1f6dAFf1JHErLORug8UOD3T4UO8Xbf9RrRu22xEt5AYd3xumSw+
    12ZRNOdtMGGgcMi3LebHOU1OewKb7SQIPMjbWs9BC6kV0vI+i4hvP6pxJLV7+kZi/r
    13XXohowtItscV+oP8tmaXcvapPNISSN+DIEXiECObVdvHZ5xsVmag/dKWxQzAK1C5
    14Sr1GLCD8/GiIVGyvqDIs100g4EMHs0bluj0qb2Ylmkvl+PZmMlU7TwT9GS9EuGoP
    15X3ygOyma
    16=xHYS
    17-----END PGP SIGNATURE-----
    

    Timestamp of file with hash be5f7748fa7afdbed03e27c4c76aa836a95078861703dc7750d0df1a9da0dbd7 -

  65. michaelfolkson commented at 8:25 pm on March 25, 2021: contributor

    Approach NACK.

    We need to focus review effort on a single Speedy Trial PR at this stage. Although AJ has done as much as anyone to advance Taproot activation with all sorts of contributions (conceptual, code, tests, fuzzing) I am at a loss as to why to choose a PR with a mix of block height and MTP over a PR with a consistent use of block height.

    I have attempted to summarize the discussion on block height versus mix of block height and MTP here. To me a consistent use of block height is a no brainer. If that needs a little more review and testing so be it. (I’m not convinced it does btw, I find it much easier to reason and think about edge cases with entirely block height than a mixture. It is a slightly larger code diff, that is true.)

    I also have no idea why block height versus mix of block height and MTP has any impact whatsoever on UASF plans. It doesn’t seem to impact the difficulty of implementing a competing compatible UASF and any possible “marketing” gain for a UASF seems illusory.

    Disclosure: I have been happy to work on a UASF project in the recent past when there seemed little chance of progress with Taproot activation in Core. I would also consider doing so again in future depending on the circumstances.

    I hope we can choose a Speedy Trial PR as soon as possible to focus all review effort on it. If this PR is chosen over Andrew’s PR I will disagree with that decision but I will live with it. I think it will be the inferior choice (assuming a new rationale isn’t explained to me) but I don’t think it will cause any material problems with activation.

  66. in src/chainparams.cpp:81 in 4c7d858167 outdated
    77@@ -78,16 +78,18 @@ class CMainParams : public CChainParams {
    78         consensus.nPowTargetSpacing = 10 * 60;
    79         consensus.fPowAllowMinDifficultyBlocks = false;
    80         consensus.fPowNoRetargeting = false;
    81-        consensus.nRuleChangeActivationThreshold = 1916; // 95% of 2016
    82+        consensus.nRuleChangeActivationThreshold = 1815; // 90% of 2016
    


    JeremyRubin commented at 8:30 pm on March 25, 2021:

    ACK using 1815 as the constant.

    1814 has lower error from 90% than 1815, but previously we used 1916 which has greater error from 95% than 1915 but is the smallest exceeds threshold number, so we continue the same precedent (not that it matters much).


    ProofOfKeags commented at 8:38 pm on April 13, 2021:
    Can that precedent/convention be codified somewhere, code or otherwise? Is it already?
  67. JeremyRubin commented at 9:09 pm on March 25, 2021: contributor
    Code Review ACK 4c7d858
  68. harding commented at 1:29 am on March 26, 2021: contributor

    What am I doing wrong that I can’t get minimum_activation_height to have any effect on regtest?

     0diff --git a/src/chainparams.cpp b/src/chainparams.cpp
     1index 40c58f0e67..73be5d3d6d 100644
     2--- a/src/chainparams.cpp
     3+++ b/src/chainparams.cpp
     4@@ -412,7 +412,7 @@ public:
     5         consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
     6         consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
     7         consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
     8-        consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay
     9+        consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 576; // No activation delay
    10 
    11         consensus.nMinimumChainWork = uint256{};
    12         consensus.defaultAssumeValid = uint256{};
    
    $ make
    [...]
    
    $ src/bitcoind -regtest -daemon -vbparams=taproot:1:1932252724
    [...]
    
    $ src/bitcoin-cli -regtest getblockchaininfo | jq .blocks,.softforks.taproot
    0
    {
      "type": "bip9",
      "bip9": {
        "status": "defined",
        "start_time": 1,
        "timeout": 1932252724,
        "since": 0
      },
      "active": false
    }
    

    I’m expecting to see a min_activation_height field in the getblockchaininfo results, as my reading of the code says it should be there for cases where the min activation is greater than zero.

    It’d be nice if -vbparams here took a fourth argument for the minimum activation height, similar to in #21392 , so recompiling (and whatever mistake I’m making there) wouldn’t be necessary.

  69. ajtowns commented at 2:50 am on March 26, 2021: member
    @harding The -vbparams setting happens in UpdateVersionBitsParameters which sets it back to 0 – https://github.com/bitcoin/bitcoin/pull/21377/files#diff-ff53e63501a5e89fd650b378c9708274df8ad5d38fcffa6c64be417c4d438b6dR483
  70. ajtowns force-pushed on Mar 28, 2021
  71. ajtowns commented at 4:45 am on March 28, 2021: member

    Changed from “delayed activation” to “delayed lock in” – that is, transitions go from STARTED to DELAYED to LOCKED_IN to ACTIVE, with LOCKED_IN not being entered until MTP passes min_lock_in_time. This fixes the problem noted earlier while retaining the ability to know 2016 blocks in advance when activation will occur.

    Using MTP timestamps for activation retains compatible with deployments for testnet and signet, where block heights are unpredictable (for testnet) or inconsistent (different custom signets will not have common block heights).

    Extends the ComputeBlockVersion unit tests to test all deployments across all chains. Adds an optional parameter to -vbparams to allow testing, and uses that parameter in the unit tests.

    Note that this approach can also be used to make bip-148/bip-91 style mandatory signalling compatible with MTP-based activation; see https://github.com/ajtowns/bitcoin/commits/202103-bip9-uasf for a branch that adds a LAST_CHANCE signalling phased that will always be reached just prior to failure, so can be used for mandatory signalling to guarantee lock in.

  72. achow101 commented at 2:02 pm on March 28, 2021: member

    Approach NACK

    At this point, it seems like this PR has gotten more complicated due to MTP and reviewing this is going to be more complicated due to the edge cases around MTP. When this used a minimum activation height, I would have agreed that this would be easier and faster to review, but now that it has reverted to using a minimum (lock in) time, I am not comfortable with this approach anymore. While I agree that MTP is conceptually easier to work with, practically it has many edge cases and gotchas that I don’t think we should continue forward with using MTP. Heights are strictly easier to understand. The only edge cases to consider are off by one errors and not miners screwing with timestamps.

    Using MTP timestamps for activation retains compatible with deployments for testnet and signet, where block heights are unpredictable (for testnet)

    I agree that using MTP for testnet is better, but I don’t think that is important enough to block the use of heights for mainnet. I also think that testnet3 should be abandoned at this point, but that’s a discussion for another place and time.

    or inconsistent (different custom signets will not have common block heights).

    I think that either approach is incompatible with custom signets. I don’t think it makes sense that every signet would also have the same start time and timeout time, especially for signets that are created afterwards. It would make more sense to refactor the parameter setting and allow for the custom signets to set their own activation parameters, regardless of height or MTP.

  73. flack commented at 2:48 pm on March 28, 2021: contributor

    It would make more sense to refactor the parameter setting and allow for the custom signets to set their own activation parameters, regardless of height or MTP.

    wouldn’t that mean that you can never bury a deployment again because there could be some signet where it hasn’t activated yet?

  74. harding commented at 5:51 pm on March 28, 2021: contributor

    @ajtowns

    Transitions go from STARTED to DELAYED to LOCKED_IN to ACTIVE, with LOCKED_IN not being entered until MTP passes min_lock_in_time. This fixes the problem noted earlier while retaining] the ability to know 2016 blocks in advance when activation will occur.

    I like that. I think it does a better job of communicating to users what state the deployment is in and, by virtue of min_lock_in_time being a time, gives a more reliable and easier to understand estimate of when activation will happen (e.g. “min_lock_in_time + 10 - 24 days with high confidence” instead of using heights, where it could be as much as “140 to 220 days”).

    Adds an optional parameter to -vbparams to allow testing, and uses that parameter in the unit tests.

    Thank you! @achow101

    it seems like this PR has gotten more complicated due to MTP

    Having reviewed both (as of last week), I thought this was moderately simpler than yours. I skimmed the rebase diff and don’t think the last push increases complexity (indeed, I think it simplifies it from the earlier minimum_activation_height change.

    MTP […] practically has many edge cases and gotchas [and] I don’t think we should continue forward with using MTP. Heights are strictly easier to understand.

    What edge cases does MTP have for a deployment such as this where there’s no mandatory signaling or mandatory activation? MTP abuse generally assumes miner adversity (usually at least enough miners to execute a selfish mining attack, e.g. ~30%) but a mere 15% of miners opposed to Speedy Trial should be able to prevent its activation by following the allowed protocol.

    Heights may be easier to reason about in some ways, but they don’t directly provide the most important assurance we want in a Speedy Trial activation: that we will have a certain minimum amount of time to get many nodes upgraded to enforce taproot when it activates. As far as I’m aware, miners screwing with time stamps—at a level less than that necessary to produce a time warp attack[1]—can only delay a deployment of this PR as written, and only at cost to the miners in the form of needing to do more PoW. I think that’s fine; we can be a little bit more patient. But for a height-based activation, miners can accelerate the deployment either non-deliberately (e.g. because BTC price increases lead to increased mining) or deliberately (again at the cost of doing more PoW); I think that’s an acceptable risk, but to my mind it’s not as good as the assurance MTP can bring us.

    Again, if there’s an MTP edge case that applies to this PR that I’m not aware of, I’d certainly be interested in learning about it. Overall, I feel this latest update improves this PR to where I slightly prefer it over height-based activation (although I’m still fine with either approach).

    [1] Time warp attacks break important properties of both MTP-based and height-based activation mechanisms; it also breaks important fundamental properties of Bitcoin. Given that, I don’t think we need to consider it here: I think miners know that trying a time warp attack on mainnet could easily lead to a PoW algorithm change.

  75. achow101 commented at 9:27 pm on March 28, 2021: member

    wouldn’t that mean that you can never bury a deployment again because there could be some signet where it hasn’t activated yet?

    Not necessarily. A deployment can be buried in one chain and not in another. With #19438, I think it would also be trivial to have a deployment buried in one chain and not in another.

    What edge cases does MTP have for a deployment such as this where there’s no mandatory signaling or mandatory activation? MTP abuse generally assumes miner adversity (usually at least enough miners to execute a selfish mining attack, e.g. ~30%) but a mere 15% of miners opposed to Speedy Trial should be able to prevent its activation by following the allowed protocol.

    Sure, but my main concern is about analyzing the correctness of the code around these scenarios. I am sure that the code can handle those situations correctly, but when reviewing it, it is harder to analyze and conclude that all of the state transitions that are based around MTP are safe. It is easier to do this analysis for height based deployments.

  76. ajtowns commented at 1:49 am on March 29, 2021: member

    @flack

    wouldn’t that mean that you can never bury a deployment again because there could be some signet where it hasn’t activated yet?

    The only plausible way I’ve come up with for burying deployments on signet is via MTP – ie, declare that a soft-fork is unconditionally active on signet once the MTP passes some point, probably the MTP at which it activated on mainnet plus some delay; perhaps something like:

    0constexpr uint32_t ACTIVATION_MTP_THRESHOLD = 500000000;
    1bool DeploymentActiveAt(block, params, dep) {
    2    int h = params.DeploymentHeight(dep);
    3    return (h < ACTIVATION_MTP_THRESHOLD ? block.nHeight >= h : block.GetMedianTimePast() >= h);
    4}
    

    For custom signets that have chosen to activate the soft fork already, that’s fine, because it’s a soft fork the old blocks will still be valid even if they use rules that won’t be enforced on a -reindex or by a new node doing IBD, and because blocks are signed, there is no risk of a reorg stealing funds. For custom signets that have not already activated the soft fork, it’s fine as long as either miners upgrade to the new software before the signet flag day activation, or as long as the soft fork is safe and miners are not doing something like -acceptnonstdtxn so they won’t mine blocks that are invalid under the new rules. @achow101

    It would make more sense to refactor the parameter setting and allow for the custom signets to set their own activation parameters, regardless of height or MTP.

    I don’t think that’s feasible from a usability standpoint: to use a custom signet you would have to define all the consensus parameters for all post-taproot forks, and do so in a way that’s exactly compatible with all the other users of that custom signet.

    Using MTP-based signalling allows a signet to be upgraded to a new soft fork simply by the signet miners signalling, with no action required by users at all beyond running an updated binary, which I don’t think is achievable otherwise. (At minimum you’d need to update your config to set a “activate this deployment early” flag for each deployment on each custom signet)

    Not necessarily. A deployment can be buried in one chain and not in another. With #19438, I think it would also be trivial to have a deployment buried in one chain and not in another.

    The latter part of that is not true – #19438 differentiates buried and signalled deployments by data type, which is constant for all chains at compile time.

    But the point of burying deployments is so that we don’t need to keep dealing with conditional activation – eg, we don’t need to support the possibility that segwit is activated but p2sh is not. We don’t want to force ourselves to maintain those code paths to support random custom signets.

  77. in src/test/versionbits_tests.cpp:305 in f720a0b45a outdated
    301@@ -242,81 +302,123 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
    302     }
    303 }
    304 
    305-BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
    306+void check_computeblockversion(const Consensus::Params& params, Consensus::DeploymentPos dep)
    


    jnewbery commented at 1:50 pm on March 29, 2021:
    can be static
  78. in src/test/versionbits_tests.cpp:274 in 72ef629f1a outdated
    270     // In the first chain, test that the bit is set by CBV until it has failed.
    271     // In the second chain, test the bit is set by CBV while STARTED and
    272     // LOCKED-IN, and then no longer set while ACTIVE.
    273     VersionBitsTester firstChain, secondChain;
    274 
    275     // Start generating blocks before nStartTime
    


    sipa commented at 5:13 pm on March 29, 2021:

    In commit “tests: test ComputeBlockVersion for all deployments”

    Nit: comment outdated now

  79. in src/test/versionbits_tests.cpp:313 in f720a0b45a outdated
    315 
    316     // Use the TESTDUMMY deployment for testing purposes.
    317-    int64_t bit = mainnetParams.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit;
    318-    int64_t nStartTime = mainnetParams.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime;
    319-    int64_t nTimeout = mainnetParams.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout;
    320+    int64_t bit = params.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit;
    


    jnewbery commented at 5:20 pm on March 29, 2021:
    Is there a reason that these are vDeployments[Consensus::DEPLOYMENT_TESTDUMMY] and not vDeployments[dep]?

    sipa commented at 6:33 pm on March 29, 2021:
    In commit “tests: pull ComputeBlockVersion test into its own function” @jnewbery Indeed, looks like it’s only ever testing DEPLOYMENT_TESTDUMMY.

    ajtowns commented at 1:48 am on March 30, 2021:
    Nope, must have missed replacing all those in some rebase/redraft or another.
  80. jnewbery commented at 5:22 pm on March 29, 2021: member
    I’ve only started stepping through the test commits so far. I wonder if those first three commits should be pulled out into a separate PR?
  81. in src/validation.cpp:2467 in c7c8bfaf9e outdated
    2463@@ -2456,6 +2464,7 @@ static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const C
    2464             WarningBitsConditionChecker checker(bit);
    2465             ThresholdState state = checker.GetStateFor(pindex, chainParams.GetConsensus(), warningcache[bit]);
    2466             if (state == ThresholdState::ACTIVE || state == ThresholdState::LOCKED_IN) {
    2467+                // DELAYED unreachable since min_lock_in_time = 0
    


    sipa commented at 6:03 pm on March 29, 2021:

    In commit “versionbits: Add support for delayed lock in”

    Why do we know min_lock_in_time == 0?


    ajtowns commented at 1:58 am on March 30, 2021:
    Because WarningBitsConditionChecker doesn’t override MinLockInTime() so uses the default from AbstractThresholdConditionChecker which always returns 0. Will change the comment to refer to MinLockInTime instead of min_lock_in_time.
  82. in src/versionbits.cpp:82 in c7c8bfaf9e outdated
    73@@ -73,6 +74,12 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
    74                     pindexCount = pindexCount->pprev;
    75                 }
    76                 if (count >= nThreshold) {
    77+                    stateNext = (pindexPrev->GetMedianTimePast() < min_lock_in_time ? ThresholdState::DELAYED : ThresholdState::LOCKED_IN);
    78+                }
    79+                break;
    80+            }
    81+            case ThresholdState::DELAYED: {
    82+                if (min_lock_in_time <= pindexPrev->GetMedianTimePast()) {
    


    sipa commented at 6:06 pm on March 29, 2021:

    In commit “versionbits: Add support for delayed lock in”

    Very nitty: use the exact same expression as above here?

    0stateNext = (pindexPrev->GetMedianTimePast() < min_lock_in_time ? ThresholdState::DELAYED : ThresholdState::LOCKED_IN);
    

    You could even use switch case fallthrough, but I don’t think that contributes to readability…

  83. in src/versionbits.h:28 in c7c8bfaf9e outdated
    24@@ -25,7 +25,8 @@ static const int32_t VERSIONBITS_NUM_BITS = 29;
    25 enum class ThresholdState {
    26     DEFINED,   // First state that each softfork starts out as. The genesis block is by definition in this state for each deployment.
    27     STARTED,   // For blocks past the starttime.
    28-    LOCKED_IN, // For one retarget period after the first retarget period with STARTED blocks of which at least threshold have the associated bit set in nVersion.
    29+    DELAYED,   // Between the first STARTED period where threshold is reached, until min_lockin_time is reached
    


    sipa commented at 6:11 pm on March 29, 2021:

    In commit “versionbits: Add support for delayed lock in”

    Maybe mention that the DELAYED state may be skipped entirely if the threshold is reached after min_lockin_time?

  84. in src/test/fuzz/versionbits.cpp:311 in 97baea8ac3 outdated
    321+        if (exp_state == ThresholdState::STARTED) {
    322+            assert(blocks_sig >= threshold);
    323+            assert(current_block->GetMedianTimePast() < checker.m_end);
    324+        } else {
    325+            assert(exp_state == ThresholdState::DELAYED);
    326+        }
    


    sipa commented at 7:01 pm on March 29, 2021:

    In commit “fuzz: test versionbits delayed lock in”

    In case case ThresholdState::FAILED you want to check that exp_state isn’t ThresholdState::DELAYED either (I think the current fuzz code won’t prevent a transition DELAYED->FAILED).


    ajtowns commented at 2:25 am on March 30, 2021:
    Ah, I updated that for LAST_CHANCE in https://github.com/ajtowns/bitcoin/commits/202103-bip9-uasf but didn’t backfill to here.
  85. sipa commented at 7:22 pm on March 29, 2021: member

    I believe this is the intended state machine:

    states

    To generate:

     0$ dot -Tpng >states.png
     1digraph versionbits {
     2    defined [shape=box,label="DEFINED"];
     3    started [shape=box,label="STARTED"];
     4    failed [shape=box,label="FAILED"];
     5    locked [shape=box,label="LOCKED_IN"];
     6    delayed [shape=box,label="DELAYED"];
     7    active [shape=box,label="ACTIVE"];
     8
     9    defined -> defined [label="time < begin\ntime < end"];
    10    defined -> started [label="time >= begin\ntime < end"];
    11    defined -> failed [label="time >= end"];
    12    started -> started [label="time < end\nsig < thresh"];
    13    started -> failed [label="time >= end\n"];
    14    started -> delayed [label="time < end\nsig >= thresh\ntime < min_lock"];
    15    started -> locked [label="time < end\nsig >= thresh\ntime >= min_lock"];
    16    delayed -> delayed [label="time < min_lock"];
    17    delayed -> locked [label="time >= min_lock"];
    18    locked -> active [label="always"];
    19    active -> active [label="always"];
    20    failed -> failed [label="always"];
    21}
    
  86. JeremyRubin commented at 8:57 pm on March 29, 2021: contributor

    When this was updated I was originally a bit frustrated since the addition of the DELAYED state + switch to all MTP invalidates the ACKs on this PR.

    However, now that I understand @ajtowns reasoning around signet compatibility, I see the merit with this approach and retract my earlier preference for height based ST. With regards to review burden, I still think this is slightly easier to reason about & a smaller diff, but I think we’ve accepted the risks of delaying the proposed release schedule in favor of creating a more robust mechanism. As a Signet Operator*, a pure MTP design should make it easier for me to activate taproot for users of my network.

    My only critique of the modified design is that it might be nice to accommodate a min number of signal periods (e.g., 6) so that signets with 1 hour block rate would have sufficient time to activate. 1 hour isn’t crazy for a signet because such blocks are seen somewhat often on the network, so a long-delay signet might have utility for testing infrastructure robustness.

    I don’t think this is a show-stopping issue for merging this code as-is, because the min number of signal periods functionality can be added later relatively easily without affecting any ongoing release (just default set it to 0).

    * my signet is currently crashed, hope to bring it online soon when I can add disk space…

  87. sipa commented at 9:12 pm on March 29, 2021: member
    You may also want to rebase to fix the Android ARM build (#21541).
  88. ajtowns commented at 2:28 am on March 30, 2021: member

    a pure MTP design should make it easier for me to activate taproot for users of my network

    Note – taproot is already active on any signet; activation on signet is only a concern for any future soft forks (eg CTV).

  89. ajtowns force-pushed on Mar 30, 2021
  90. ajtowns commented at 2:33 am on March 30, 2021: member
    Bumped for 21541 and addressed review comments.
  91. ajtowns commented at 2:51 am on March 30, 2021: member

    @jnewbery

    I wonder if those first three commits should be pulled out into a separate PR?

    The could be, but they aren’t terribly compatible with #21392 which stops checking ComputeBlockVersion behaviour against mainnet, and has a bunch of other changes to the ComputeBlockVersion test in order to convert from mtp to heights.

  92. in src/test/versionbits_tests.cpp:334 in 754ab11f20 outdated
    329@@ -302,17 +330,29 @@ static void check_computeblockversion(const Consensus::Params& params, Consensus
    330         nHeight += 1;
    331     }
    332 
    333-    nTime = nTimeout;
    334-    // FAILED is only triggered at the end of a period, so CBV should be setting
    335-    // the bit until the period transition.
    336-    for (uint32_t i = 0; i < params.nMinerConfirmationWindow - 1; i++) {
    337+    if (nTimeout != Consensus::BIP9Deployment::NO_TIMEOUT) {
    338+        // cannot reach NO_TIMEOUT a uint32_t
    


    sipa commented at 4:14 am on March 30, 2021:
    µnit: grammar a weird

    ajtowns commented at 4:49 pm on March 30, 2021:
    I couldn’t even fit all the words I needed a uint32_t.

    ajtowns commented at 1:34 am on April 12, 2021:
    Replaced comment
  93. in src/test/versionbits_tests.cpp:182 in 8c63862471 outdated
    190@@ -151,6 +191,11 @@ class VersionBitsTester
    191     VersionBitsTester& TestActive() { return TestState(ThresholdState::ACTIVE); }
    192     VersionBitsTester& TestFailed() { return TestState(ThresholdState::FAILED); }
    193 
    194+    // non-delayed should be active; delayed should still be locked in
    


    sipa commented at 4:36 am on March 30, 2021:
    nit: this comment seems to apply to TestActiveLockedIn, not TestLockedInDelayed.
  94. sipa commented at 4:44 am on March 30, 2021: member
    Code review ACK e12a22a0fd9d18bde1cffc6f919b58f77cf83236
  95. michaelfolkson commented at 3:46 pm on March 30, 2021: contributor

    I’d like to rescind my previous Approach NACK

    It was given in the spirit of wanting all review to be focused on a single Speedy Trial PR (something I think should be top priority above all else) rather than two PRs, thinking an alternative PR (#21392) was better placed to be merged and personal preference too.

    However, I am less sure the alternative PR stands a better chance of being merged at this point. And my personal preference for the alternative PR is not strong enough to Approach NACK this PR otherwise.

    Hence, Approach ACK for this PR. My preference is #21392 but (to the extent I can) I won’t stand in the way of this PR being merged if it can be.

  96. ajtowns commented at 9:50 am on March 31, 2021: member

    My only critique of the modified design is that it might be nice to accommodate a min number of signal periods (e.g., 6) so that signets with 1 hour block rate would have sufficient time to activate. 1 hour isn’t crazy for a signet because […]

    I’m not sure that a 1 hour block rate isn’t crazy – nPowTargetSpacing is still 10min, so your difficulty would keep getting automatically lowered unless you had bursts of blocks to compensate so that the average rate hits 10min every 2 weeks. If you have bursts like that to maintain difficulty anyway, you can just use them to get signalling done in a normal timeframe.

    But ignoring that, I think an easy fix would be to lower signet’s nMinerConfirmationWindow from 2016 to 288; that way normal signets can activate changes in four days instead of four weeks; and even a 1-hour-per-block signet that wasn’t willing to do bursts wouldn’t need to to take much longer than a month either (<12 days to hit a 288-block boundary, ~12 days to signal, ~12 days to lock in, so <36 days total).

  97. JeremyRubin commented at 4:50 pm on March 31, 2021: contributor
    @ajtowns i thought you could set arbitrary block rates for signet?
  98. ajtowns commented at 12:44 pm on April 1, 2021: member

    @ajtowns i thought you could set arbitrary block rates for signet?

    The only signet consensus parameter you can change (without everyone recompiling) is the block challenge. The old miner script used to let you target a block rate, but that would have an impact on difficulty that might be annoying to manage, so the new one lets you target a difficulty (--nbits) and adjusts the block rate to match; when you hit your difficulty target, it’ll just average out at ~10min per block.

  99. ajtowns commented at 4:26 pm on April 1, 2021: member

    I believe this is the intended state machine:

    Yeah. It’s somewhat more complicted than it could be because it’s maintaining some of the complexities of the bip9 state machine. It could be simplified a bit by dropping the DEFINED->FAILED case and not ignoring signalling from the last STARTED period:

    image

    Excepting the addition of the DELAYED state, that pretty much makes it the same as the corresponding bip8 diagram, as bip8 has already made those changes compared to bip9.

     0digraph versionbits {
     1    defined [shape=box,label="DEFINED"];
     2    started [shape=box,label="STARTED"];
     3    failed [shape=box,label="FAILED"];
     4    locked [shape=box,label="LOCKED_IN"];
     5    delayed [shape=box,label="DELAYED"];
     6    active [shape=box,label="ACTIVE"];
     7
     8    delayorlock [shape=circle,label=""];
     9
    10    edge [weight=1];
    11    defined -> defined [label="time < begin"];
    12    started -> started [label="nsig < thresh\ntime < end"];
    13    active -> active [label="always"];
    14    failed -> failed [label="always"];
    15    delayed -> delayorlock [label="always"];
    16
    17    edge [weight=100];
    18    defined -> started [label="time >= begin"];
    19    started -> failed [label="nsig < thresh\ntime >= end\n"];
    20
    21    started -> delayorlock [label="nsig >= thresh"];
    22    delayorlock -> delayed [label="time < min_lock"];
    23    delayorlock -> locked [label="time >= min_lock"];
    24
    25    locked -> active [label="always"];
    26
    27    { rank=same; delayed delayorlock }
    28}
    
  100. in src/consensus/params.h:32 in e12a22a0fd outdated
    28@@ -29,6 +29,11 @@ struct BIP9Deployment {
    29     int64_t nStartTime;
    30     /** Timeout/expiry MedianTime for the deployment attempt. */
    31     int64_t nTimeout;
    32+    /** Delay lock in occuring until at least this timestamp.
    


    ariard commented at 6:50 pm on April 1, 2021:

    Should this say median time not a block timestamp (nTime) ?

    IIUC correctly the problem mentions here is solved for the following rational. If a reorg occurs across a retarget period with new block timestamps rolling back the median time, the LOCKED_IN period serves a safety buffer until ACTIVATION ? If so that would be great to document it somewhere.


    ajtowns commented at 1:34 am on April 12, 2021:
    No longer relevant, but fwiw I was using “timestamp” in the sense of “epoch timestamp, ie seconds since 1970”, not “block timestamp, ie nTime value”.
  101. ariard commented at 6:51 pm on April 1, 2021: member
    Approach ACK introducing min_lock_in_time
  102. Rspigler commented at 9:08 pm on April 2, 2021: contributor
    I was confused how it would be possible to go directly from DEFINED->FAILED without passing through STARTED in @sipa’s state machine here: #21377 (comment) Seems only possible if “end” is before “begin”? @ajtowns drops it here (mentions it is due to BIP9?) #21377 (comment) Should STARTED -> delayorlock be defined as “nsig >= thresh & time <= end” ?
  103. MarcoFalke commented at 12:03 pm on April 4, 2021: member

    If it is currently not possible to bury/flag-day activate/vb signal at different heights/times in signet, it seems that this should be made possible via command line options, not by preferring MTP over height based vb code (or the other way round).

    Also, I fail to see how MTP solves an issue that height based can’t. Let’s assume the signet default params for deployment mention a start and end parameter for a soft-fork, but someone wants to start a signet with the softfork always active. This can only be fixed by supplying modified source/binaries or by allowing the users/miners to pick the vbparams at runtime. It can’t be fixed by using MTP over height (or the other way round).

  104. JeremyRubin commented at 8:32 pm on April 4, 2021: contributor

    the benefit of MTP is not needing to disseminate additional parameters per-signet for activation. If you upgrade to the latest bitcoin-core with a new Soft fork deployment, your existing config file (with whatever signetchallenge) will be able to activate without further configuration.

    this is not possible with height.

  105. luke-jr commented at 9:08 pm on April 4, 2021: member
    If Signet is going to become a reason to choose inferior consensus code for mainnet, then we should revert Signet.
  106. JeremyRubin commented at 9:21 pm on April 4, 2021: contributor
    it’s also true for testnet
  107. Rspigler commented at 10:06 pm on April 4, 2021: contributor
    I don’t think signet should be a consideration for MTP vs height, since taproot is already activated on signet, and there’s no indication that ST will be used in the future.
  108. MarcoFalke commented at 6:06 am on April 5, 2021: member

    this is not possible with height.

    Is it really that much work to check the place where you copied the signetchallenge to also copy the vbparams? You’ll have to do that anyway, otherwise you won’t know if your signet picked always active, flag day, or signalling activation.

  109. instagibbs commented at 5:30 am on April 6, 2021: member

    apologies, I just am not going to be awake for a 3am IRC meeting my time, so dropping my feedback here:

    I’m unconvinced by the arguments set out here for MTP: the one thing you do get is testnet being an activation testing ground for the MTP-based implementation. Other than that it’s a wash, or it seems to be a net negative for clarity and testing.

    If signets require a little more config data to be passed around, so be it. We should not be tying mainnet safety to configs of these networks. Based on my years of experience any proper operation of a “non-official” signet-like chain will be carting around a config file anyways.

    fwiw I’m deferring proper review of a PR until I think we can approach consensus on this topic. I think this can be solved provided the review burden of both is similar in magnitude.

  110. ajtowns commented at 7:31 am on April 6, 2021: member

    @MarcoFalke

    Also, I fail to see how MTP solves an issue that height based can’t. Let’s assume the signet default params for deployment mention a start and end parameter for a soft-fork, but someone wants to start a signet with the softfork always active. This can only be fixed by supplying modified source/binaries or by allowing the users/miners to pick the vbparams at runtime. It can’t be fixed by using MTP over height (or the other way round).

    The issue for signet that MTP signalling solves that height based signalling doesn’t is allowing multiple signets to operate with the same fixed-at-compile-time parameters. That avoids having to have every user of every signet add custom command line parameters that will change over the lifetime of the signet (as additional soft forks are deployed/activated).

    With MTP signalling, if you want a non-buried soft fork “always active” on a new signet, you create the signet and mine 3 periods worth of blocks, signalling in the second period – no need for anyone to set vbparams. (For a soft fork that’s buried via MTP, you simply need to get MTP past the burial MTP, so likely only need to mine two or three blocks if you’re creating a new signet for an already buried fork)

    If you’re adding height-based params to signetchallenge, you’re making the challenge incompatible with existing nodes (that obviously don’t support parsing and removing the vbparams from the challenge), but you’re also requiring the challenge to change with each new soft fork: you’ll need to add ‘anyprevout=height’ one day, then ‘ctv=height’ some other day, then ‘consensuscleanup=height’ some other day, and can’t remove any of them, even after they’re buried on mainnet. And you’re also providing an extra way for people to make consensus-breaking mistakes, in hard to detect ways – currently if you get the signet challenge wrong you’ll just end up having a different network magic and not connect to the signet at all, but with optional parameters added you can’t do the same, since you want to be backwards compatible by allowing the optional values to be missing. @Rspigler

    I don’t think signet should be a consideration for MTP vs height, since taproot is already activated on signet, and there’s no indication that ST will be used in the future.

    If we’re not trying to use this as a stepping stone for future activations, then it would make sense to keep the changes for ST as simple as possible, and sticking with MTP is the smaller and more easily backportable set of changes…

    Even restricting the analysis purely to the effect on mainnet, and ignoring testing, implementation or backporting complexity, I think MTP is superior; if it were blocking the ability to reasonably do a UASF then that would still be a showstopper, but as per the LAST_CHANCE comments above, I don’t see any reason why that would be the case.

  111. achow101 commented at 11:20 pm on April 6, 2021: member

    I think that the min_lock_in_time should be reverted to min_activation_height. As noted on the mailing list, min_lock_in_time adds an additional 14 day uncertainty to the true activation time, whereas predicting the time for min_activation_height to be reached only gets better as that height approaches. A good enough prediction of the actual activation time can be known several days or weeks ahead. With min_lock_in_time, miners could also manipulate timestamps to introduce uncertainty in the activation time.

    Although I think that block heights is technically superior, many of the specific things that it guarantees are still provided by an MTP implementation probabilistically and some changes can be made to provide the same guarantees. With that in mind, I would like to see that the DEFINED->FAILED transition is removed and the unconditional STARTED->FAILED when the timeout is reached. This would make it more like BIP 8 where at least one signaling period is guaranteed, and if the timeout falls in the middle of a signaling period, then that period will still count towards the activation.

  112. JeremyRubin commented at 11:40 pm on April 6, 2021: contributor
    @achow101 what would you say to at the point of lock_in a height is projected out to estimate a date based on 9.5 minute blocks? Or something like that… it works for signets fine as well, and then you avoid the +/- issue at the actual turn on point?
  113. JeremyRubin commented at 11:41 pm on April 6, 2021: contributor
    Ah also noting that min_activation_height should be compatible with signet no matter what it is set to (edit: most simply, 0), unlike startheight/stopheight so it doesn’t matter that much!
  114. JeremyRubin commented at 0:36 am on April 7, 2021: contributor

    Although I think that block heights is technically superior, many of the specific things that it guarantees are still provided by an MTP implementation probabilistically and some changes can be made to provide the same guarantees. With that in mind, I would like to see that the DEFINED->FAILED transition is removed and the unconditional STARTED->FAILED when the timeout is reached. This would make it more like BIP 8 where at least one signaling period is guaranteed, and if the timeout falls in the middle of a signaling period, then that period will still count towards the activation. @achow101 would the LAST_CHANCE https://github.com/ajtowns/bitcoin/commits/202103-bip9-uasf solution work for the guaranteed at least 1 signal period?

  115. achow101 commented at 1:01 am on April 7, 2021: member

    @achow101 would the LAST_CHANCE https://github.com/ajtowns/bitcoin/commits/202103-bip9-uasf solution work for the guaranteed at least 1 signal period?

    Originally this paragraph suggested that, but @ajtowns pointed out that changing these state transitions achieves the same effect. I prefer changing these transitions rather than adding a new state as these were part of BIP 8 and are thus already (partially) understood. However I think it is important to add LAST_CHANCE later since it enables a UASF.

  116. ajtowns force-pushed on Apr 7, 2021
  117. ajtowns commented at 9:36 am on April 7, 2021: member

    Updates as per achow101’s suggestions – switches back from fully-mtp-based to using min_activation_height, and drops bip9’s “fail immediately when timeout passes” handling (also adds a NEVER_ACTIVE special case, since the lack of an immediate failure mode means you can’t just use a super-early timeout for that purpose anymore).

    I believe this should capture the updated state machine:

    image

    [EDIT: note that these all map attributes of the last block in a period to the activation state of all blocks in the next period. Replace “time” by “previous block’s time” and “height + 1” by “height” to map attributes of the first block in a period to the activation state of all blocks in the same period]

    I believe this backports to 0.21 cleanly (on top of #21614 anyway).

    (CI is currently timing out on the fuzzer run)

  118. in src/versionbits.cpp:84 in be7d1368d8 outdated
    81             }
    82             case ThresholdState::LOCKED_IN: {
    83-                // Always progresses into ACTIVE.
    84-                stateNext = ThresholdState::ACTIVE;
    85+                // Progresses into ACTIVE provided activation height will have been reached.
    86+                if (pindexPrev->nHeight + 1 >= min_activation_height) {
    


    benthecarman commented at 3:21 pm on April 7, 2021:
    nit: Might be worth adding a comment saying pindexPrev->nHeight + 1 is used so we measure the current block height, and not the previous block’s height

    instagibbs commented at 2:28 am on April 8, 2021:
    In the context of “will the next connected block have active X” the comment seems clear enough to me?

    JeremyRubin commented at 5:33 pm on April 8, 2021:

    I :+1: the sentiment that it’s confusing, but think it’s clear enough in this case.

    In #21392 there were a lot of examples of pindexPrev->nHeight + 1 < min_activation_height which I wanted documented better. Despite being the negation, is a lot harder to understand when the transition was happening because it was in states with multiple possible transitions, and the clause was guarding a state transition that becomes impossible rather than one that becomes possible. As such, even if this code contained an off-by-one error in the logic, it would only result in one additional period of LOCKED_IN as opposed to a failure to activate.

    That said, there’s no off-by-one error here AFAICT :)

  119. in src/chainparams.cpp:519 in aa6a95de12 outdated
    516         if (!ParseInt64(vDeploymentParams[2], &nTimeout)) {
    517             throw std::runtime_error(strprintf("Invalid nTimeout (%s)", vDeploymentParams[2]));
    518         }
    519+        if (vDeploymentParams.size() >= 4 && !ParseInt32(vDeploymentParams[3], &min_activation_height)) {
    520+            throw std::runtime_error(strprintf("Invalid min_activation_height (%s)", vDeploymentParams[3]));
    521+        }
    


    achow101 commented at 5:54 pm on April 7, 2021:

    In aa6a95de12b265cd3dc16b2d1303b89a20331a56 “versionbits: Add support for delayed activation after lockin”

    This could also check that all of the parameters are valid. That would be >=0 for all of them and nStartTime and nTimeout are either -1 (or -2 when it is introduced). And that min_activation_height falls on a retarget boundary.


    ajtowns commented at 1:30 am on April 12, 2021:
    Agree with the concept, but leaving this for now – setting weird values on regtest shouldn’t break anything given the fuzz tester already tests most of the weird cases.

    ProofOfKeags commented at 8:23 pm on April 14, 2021:
    nit: Is it possible for the deployment parameters to actually exceed 4? In the above checks it seems that we rule out any case where the deployment params size strictly exceeds 4. It seems that the only permissible cases here are sizes 3 and 4, which imo makes the >= 4 test misleading, implying it could be 5+. Obviously the semantics here are identical considering the entire block of code as a whole, but I wonder if the ambiguity could be reduced.
  120. in src/test/fuzz/versionbits.cpp:318 in c7008a1a07 outdated
    321@@ -323,7 +322,7 @@ FUZZ_TARGET_INIT(versionbits, initialize)
    322         assert(exp_state == ThresholdState::ACTIVE || exp_state == ThresholdState::LOCKED_IN);
    323         break;
    324     case ThresholdState::FAILED:
    325-        assert(current_block->GetMedianTimePast() >= checker.m_end);
    326+        assert(never_active_test || current_block->GetMedianTimePast() >= checker.m_end);
    


    achow101 commented at 5:56 pm on April 7, 2021:

    In c7008a1a07ae8bd8dfb9c2001a4fdef6e08eb395 “versionbits: Add explicit NEVER_ACTIVE deployments”

    Could add assert(!never_active_test) in case ThresholdState::STARTED.


    ajtowns commented at 2:21 am on April 8, 2021:

    The if (never_active_test) block at the end asserts both state and exp_state are always FAILED so that’s already covered (likewise for always_active_test).

    (This is just a probably-too-clever way of writing if (!never_active_test) { assert(MTP >= m_end); })

  121. in src/test/versionbits_tests.cpp:465 in e94fc819d8 outdated
    448+    {
    449+        // Use regtest/testdummy to ensure we always exercise the
    450+        // min_lock_in_time test, even if we're not using that in a
    451+        // live deployment
    452+        ArgsManager args;
    453+        args.ForceSetArg("-vbparams", "testdummy:1199145601:1230767999:403200"); // January 1, 2008 - December 31, 2008, min act height 403200
    


    achow101 commented at 6:09 pm on April 7, 2021:

    In e94fc819d8a996ce3690274c307c537329dfb95b “tests: test versionbits delayed activation”

    How come min_activation_height is set so high? By my calculation, it only needs to be at least 576.


    ajtowns commented at 0:31 am on April 8, 2021:
    To make sure that it doesn’t blow up when a mainnet deployment is configured with a similarly high min_activation_height and it’s tested.

    JeremyRubin commented at 6:21 pm on April 8, 2021:
    Should we not use 700000+? I can’t see what would change between the two values, but if the goal is realism (maybe it runs too slowly :) )

    ajtowns commented at 1:25 am on April 12, 2021:
    576 to 400k is a factor of 700x, 400k to 700k is only an extra 73% – if the test blows up from 2s to 1400s that’s a huge problem, if it goes from 2s to 3.5s, meh. :) So leaving it as is; though you’re right, 700k would have been a fine choice.
  122. achow101 commented at 6:11 pm on April 7, 2021: member

    ACK be7d1368d8e98b6f9f692289b7d25ac369bf2984

    All of the changes that I suggested have been implemented.

  123. in src/test/versionbits_tests.cpp:300 in 754ab11f20 outdated
    313+
    314+        // Mine more blocks (4 less than the adjustment period) at the old time, and check that CBV isn't setting the bit yet.
    315+        for (uint32_t i = 1; i < params.nMinerConfirmationWindow - 4; i++) {
    316+            lastBlock = firstChain.Mine(params.nMinerConfirmationWindow + i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
    317+            // This works because VERSIONBITS_LAST_OLD_BLOCK_VERSION happens
    318+            // to be 4, and the bit we're testing happens to be bit 28.
    


    harding commented at 8:52 pm on April 7, 2021:

    In commit “tests: test ComputeBlockVersion for all deployments”

    I don’t understand this comment. (1) in this generalized check for all deployments, the “bit we’re testing happens to be bit 28” isn’t the case; (2) the exact comparison on the next line is run several times prior to here without this comment. Was there maybe an additional check here that got deleted?


    ajtowns commented at 1:19 am on April 12, 2021:
    Removed this comment. See #21377 (review) for reasoning.
  124. in src/test/versionbits_tests.cpp:80 in e94fc819d8 outdated
    84@@ -77,14 +85,18 @@ class VersionBitsTester
    85     int num;
    86 
    87 public:
    88-    VersionBitsTester() : num(0) {}
    


    instagibbs commented at 2:32 am on April 8, 2021:
    was num never modified before?

    ajtowns commented at 3:06 am on April 8, 2021:
    There was (and is) a num++ at the end of TestState and TestStateSinceHeight
  125. instagibbs approved
  126. instagibbs commented at 2:48 am on April 8, 2021: member

    utACK https://github.com/bitcoin/bitcoin/pull/21377/commits/be7d1368d8e98b6f9f692289b7d25ac369bf2984

    logic appears to match the latest state machine image, read through all the various discussions on the design choices, agree that the current state machine in the PR is much better than previous iterations.

    Will do some follow-on testing soon.

  127. in src/test/versionbits_tests.cpp:283 in be7d1368d8 outdated
    279+static void check_computeblockversion(const Consensus::Params& params, Consensus::DeploymentPos dep)
    280 {
    281+    // This implicitly uses versionbitscache, so clear it every time
    282+    versionbitscache.Clear();
    283+
    284     // Check that ComputeBlockVersion will set the appropriate bit correctly
    


    jnewbery commented at 9:47 am on April 8, 2021:
    This comment is a bit stranded. It was previously a comment for the entire function. Perhaps it could be moved up to be a doxygen comment for the function.

    ajtowns commented at 1:21 am on April 12, 2021:
    Moved
  128. in src/test/versionbits_tests.cpp:314 in be7d1368d8 outdated
    304+    if (nStartTime == Consensus::BIP9Deployment::NEVER_ACTIVE) return;
    305+
    306+    BOOST_REQUIRE(nStartTime < nTimeout);
    307+    BOOST_REQUIRE(nStartTime >= 0);
    308+    BOOST_REQUIRE(nTimeout <= std::numeric_limits<uint32_t>::max() || nTimeout == Consensus::BIP9Deployment::NO_TIMEOUT);
    309+    BOOST_REQUIRE(0 <= bit && bit < 32);
    


    jnewbery commented at 9:51 am on April 8, 2021:

    Any reason not to make this more tightly bounded:

    0    BOOST_REQUIRE(0 <= bit && bit < 29);
    

    or even:

    0    BOOST_REQUIRE(0 <= bit && bit < VERSIONBITS_NUM_BITS);
    

    ajtowns commented at 1:39 pm on April 8, 2021:

    It’s already checked via BOOST_REQUIRE(((1 << bit) & VERSIONBITS_TOP_MASK) == 0); – the “requires” are there because the later checks will break if these aren’t true; if it were possible for bit to be NUM_BITS or greater, but not clash with TOP_MASK the other tests would be fine.

    (I had also thought versionbits_sanity was checking the bit was in [0, NUM_BITS) so didn’t “duplicate” the test here, but it’s not actually doing that – perhaps I saw that in some patch implementing BIP320)


    JeremyRubin commented at 5:52 pm on April 8, 2021:

    Noting that this check is required before computing on the bit (https://en.cppreference.com/w/cpp/language/operator_arithmetic):

    In any case, if the value of the right operand is negative or is greater or equal to the number of bits in the promoted left operand, the behavior is undefined.

    This also impacts where, if taken, https://github.com/bitcoin/bitcoin/pull/21377/commits/754ab11f2085a0c5e417cfbba80cd024fc8ea67a#r609525131 should be created.

  129. in src/test/versionbits_tests.cpp:315 in be7d1368d8 outdated
    305+
    306+    BOOST_REQUIRE(nStartTime < nTimeout);
    307+    BOOST_REQUIRE(nStartTime >= 0);
    308+    BOOST_REQUIRE(nTimeout <= std::numeric_limits<uint32_t>::max() || nTimeout == Consensus::BIP9Deployment::NO_TIMEOUT);
    309+    BOOST_REQUIRE(0 <= bit && bit < 32);
    310+    BOOST_REQUIRE(((1 << bit) & VERSIONBITS_TOP_MASK) == 0);
    


    jnewbery commented at 9:55 am on April 8, 2021:

    1 << bit (or 1<<bit) is used all over this test. Consider making an alias for readability:

    0    uint32_t deployment_mask{1 << bit};
    
  130. in src/test/versionbits_tests.cpp:326 in be7d1368d8 outdated
    318 
    319-    // Start generating blocks before nStartTime
    320-    int64_t nTime = nStartTime - 1;
    321+    int64_t nTime = nStartTime;
    322+
    323+    const CBlockIndex *lastBlock = nullptr;
    


    jnewbery commented at 9:55 am on April 8, 2021:

    If you’re touching this:

    0    const CBlockIndex* lastBlock = nullptr;
    
  131. in src/test/versionbits_tests.cpp:337 in be7d1368d8 outdated
    360+
    361+        // Mine more blocks (4 less than the adjustment period) at the old time, and check that CBV isn't setting the bit yet.
    362+        for (uint32_t i = 1; i < params.nMinerConfirmationWindow - 4; i++) {
    363+            lastBlock = firstChain.Mine(params.nMinerConfirmationWindow + i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
    364+            // This works because VERSIONBITS_LAST_OLD_BLOCK_VERSION happens
    365+            // to be 4, and the bit we're testing happens to be bit 28.
    


    jnewbery commented at 9:58 am on April 8, 2021:

    “the bit we’re testing happens to be bit 28.”

    Not true!


    ajtowns commented at 11:22 pm on April 11, 2021:
    I think that justification is only relevant if ComputeBlockVersion would return LAST_OLD_BLOCK_VERSION when not setting a bit, but that logic was removed before 7575 was even merged; #7575#discussion-diff-54153581 and #7575#discussion-diff-55125020 are the relevant discussion points, I think. So the comment’s never been relevant.

    ajtowns commented at 1:21 am on April 12, 2021:
    Removed this comment.
  132. in src/test/versionbits_tests.cpp:359 in be7d1368d8 outdated
    370+        nTime = nStartTime;
    371+        for (uint32_t i = params.nMinerConfirmationWindow - 4; i <= params.nMinerConfirmationWindow; i++) {
    372+            lastBlock = firstChain.Mine(params.nMinerConfirmationWindow + i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
    373+            BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
    374+        }
    375+        // Next we will advance to the next period and transition to STARTED,
    


    jnewbery commented at 10:01 am on April 8, 2021:
    Maybe mine up to params.nMinerConfirmationWindow * 3 - 1 here and verify that we stay in DEFINED, and then the next block (below) transitions us to STARTED.

    ajtowns commented at 1:50 am on April 12, 2021:
    This test is mostly just treating the underlying state as implied rather than matching it explicitly; I think that’s something to reconsider more broadly as part of later refactoring (which ideally would also let ComputeBlockVersion be checked via fuzzing)
  133. in src/test/versionbits_tests.cpp:322 in be7d1368d8 outdated
    312+    BOOST_REQUIRE_EQUAL(min_activation_height % params.nMinerConfirmationWindow, 0);
    313 
    314     // In the first chain, test that the bit is set by CBV until it has failed.
    315     // In the second chain, test the bit is set by CBV while STARTED and
    316     // LOCKED-IN, and then no longer set while ACTIVE.
    317     VersionBitsTester firstChain, secondChain;
    


    jnewbery commented at 10:09 am on April 8, 2021:
    It’s pretty confusing that this test is using these VersionBitsTester objects, which have their own activation parameters, but the thing that’s actually being tested is the global versionbitscache state. In fact, these VersionBitsTester objects are only being used for their Mine() method. I think this code would be a lot clearer if that was pulled out into a utility function.

    ajtowns commented at 1:46 pm on April 8, 2021:
    I think there are plenty of things that could be a lot clearer in versionbits in general, and would like to start doing those refactors later, in PRs that don’t have a need to be backported. So intending to leave these more general improvements for then – please do remind when I forget!

    jnewbery commented at 2:08 pm on April 8, 2021:
    I’ll commit to reviewing those PRs – please remind me if I forget to do that :)
  134. in src/test/versionbits_tests.cpp:335 in be7d1368d8 outdated
    344+    if (nTime == 0) {
    345+        // since CBlockIndex::nTime is uint32_t we can't represent any
    346+        // earlier time, so will transition from DEFINED to STARTED at the
    347+        // end of the first period by mining blocks at nTime == 0
    348+        lastBlock = firstChain.Mine(params.nMinerConfirmationWindow - 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
    349+        BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
    


    jnewbery commented at 10:37 am on April 8, 2021:

    Perhaps out of scope here, but I find that the bit masking in these checks is a little difficult to read and detracts from the intent of the test by emphasizing the syntax rather than the meaning. May be worth pulling out the checks into a utility:

      0diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
      1index 5f53da8aff..f952935e58 100644
      2--- a/src/test/versionbits_tests.cpp
      3+++ b/src/test/versionbits_tests.cpp
      4@@ -275,6 +275,31 @@ BOOST_AUTO_TEST_CASE(versionbits_sanity)
      5     }
      6 }
      7 
      8+static bool check_bit_set(const CBlockIndex* lastBlock, const Consensus::Params& params, int64_t bit)
      9+{
     10+    if ((ComputeBlockVersion(lastBlock, params) & (1 << bit)) == 0) {
     11+        BOOST_ERROR("version bit not set!");
     12+        return false;
     13+    }
     14+    if ((ComputeBlockVersion(lastBlock, params) & VERSIONBITS_TOP_MASK) != VERSIONBITS_TOP_BITS) {
     15+        BOOST_ERROR("versionbits top mask not set!");
     16+        return false;
     17+    }
     18+    return true;
     19+}
     20+
     21+static bool check_bit_unset(const CBlockIndex* lastBlock, const Consensus::Params& params, int64_t bit)
     22+{
     23+    if ((ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0) {
     24+        BOOST_ERROR("version bit set!");
     25+        return false;
     26+    }
     27+    if ((ComputeBlockVersion(lastBlock, params) & VERSIONBITS_TOP_MASK) != VERSIONBITS_TOP_BITS) {
     28+        BOOST_ERROR("versionbits top mask not set!");
     29+        return false;
     30+    }
     31+    return true;
     32+}
     33 static void check_computeblockversion(const Consensus::Params& params, Consensus::DeploymentPos dep)
     34 {
     35     // This implicitly uses versionbitscache, so clear it every time
     36@@ -318,9 +343,10 @@ static void check_computeblockversion(const Consensus::Params& params, Consensus
     37         // earlier time, so will transition from DEFINED to STARTED at the
     38         // end of the first period by mining blocks at nTime == 0
     39         lastBlock = firstChain.Mine(params.nMinerConfirmationWindow - 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
     40-        BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
     41+        BOOST_CHECK(check_bit_unset(lastBlock, params, bit));
     42+
     43         lastBlock = firstChain.Mine(params.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
     44-        BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
     45+        BOOST_CHECK(check_bit_set(lastBlock, params, bit));
     46         // then we'll keep mining at nStartTime...
     47     } else {
     48         // use a time 1s earlier than start time to check we stay DEFINED
     49@@ -328,30 +354,28 @@ static void check_computeblockversion(const Consensus::Params& params, Consensus
     50 
     51         // Start generating blocks before nStartTime
     52         lastBlock = firstChain.Mine(params.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
     53-        BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
     54+        BOOST_CHECK(check_bit_unset(lastBlock, params, bit));
     55 
     56         // Mine more blocks (4 less than the adjustment period) at the old time, and check that CBV isn't setting the bit yet.
     57         for (uint32_t i = 1; i < params.nMinerConfirmationWindow - 4; i++) {
     58             lastBlock = firstChain.Mine(params.nMinerConfirmationWindow + i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
     59             // This works because VERSIONBITS_LAST_OLD_BLOCK_VERSION happens
     60             // to be 4, and the bit we're testing happens to be bit 28.
     61-            BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
     62+            BOOST_CHECK(check_bit_unset(lastBlock, params, bit));
     63         }
     64         // Now mine 5 more blocks at the start time -- MTP should not have passed yet, so
     65         // CBV should still not yet set the bit.
     66         nTime = nStartTime;
     67         for (uint32_t i = params.nMinerConfirmationWindow - 4; i <= params.nMinerConfirmationWindow; i++) {
     68             lastBlock = firstChain.Mine(params.nMinerConfirmationWindow + i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
     69-            BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
     70+            BOOST_CHECK(check_bit_unset(lastBlock, params, bit));
     71         }
     72         // Next we will advance to the next period and transition to STARTED,
     73     }
     74 
     75     lastBlock = firstChain.Mine(params.nMinerConfirmationWindow * 3, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
     76     // so ComputeBlockVersion should now set the bit,
     77-    BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
     78-    // and should also be using the VERSIONBITS_TOP_BITS.
     79-    BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
     80+    BOOST_CHECK(check_bit_set(lastBlock, params, bit));
     81 
     82     // Check that ComputeBlockVersion will set the bit until nTimeout
     83     nTime += 600;
     84@@ -360,8 +384,7 @@ static void check_computeblockversion(const Consensus::Params& params, Consensus
     85     // These blocks are all before nTimeout is reached.
     86     while (nTime < nTimeout && blocksToMine > 0) {
     87         lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
     88-        BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
     89-        BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
     90+        BOOST_CHECK(check_bit_set(lastBlock, params, bit));
     91         blocksToMine--;
     92         nTime += 600;
     93         nHeight += 1;
     94@@ -375,7 +398,7 @@ static void check_computeblockversion(const Consensus::Params& params, Consensus
     95         // finish the last period before we start timing out
     96         while (nHeight % params.nMinerConfirmationWindow != 0) {
     97             lastBlock = firstChain.Mine(nHeight+1, nTime - 1, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
     98-            BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
     99+            BOOST_CHECK(check_bit_set(lastBlock, params, bit));
    100             nHeight += 1;
    101         }
    102 
    103@@ -383,12 +406,12 @@ static void check_computeblockversion(const Consensus::Params& params, Consensus
    104         // the bit until the period transition.
    105         for (uint32_t i = 0; i < params.nMinerConfirmationWindow - 1; i++) {
    106             lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
    107-            BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
    108+            BOOST_CHECK(check_bit_set(lastBlock, params, bit));
    109             nHeight += 1;
    110         }
    111         // The next block should trigger no longer setting the bit.
    112         lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
    113-        BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
    114+        BOOST_CHECK(check_bit_unset(lastBlock, params, bit));
    115     }
    116 
    117     // On a new chain:
    118@@ -399,25 +422,26 @@ static void check_computeblockversion(const Consensus::Params& params, Consensus
    119     // Mine one period worth of blocks, and check that the bit will be on for the
    120     // next period.
    121     lastBlock = secondChain.Mine(params.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
    122-    BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
    123+    BOOST_CHECK(check_bit_set(lastBlock, params, bit));
    124 
    125     // Mine another period worth of blocks, signaling the new bit.
    126     lastBlock = secondChain.Mine(params.nMinerConfirmationWindow * 2, nTime, VERSIONBITS_TOP_BITS | (1<<bit)).Tip();
    127     // After one period of setting the bit on each block, it should have locked in.
    128     // We keep setting the bit for one more period though, until activation.
    129-    BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
    130+    BOOST_CHECK(check_bit_set(lastBlock, params, bit));
    131 
    132     // Now check that we keep mining the block until the end of this period, and
    133     // then stop at the beginning of the next period.
    134     lastBlock = secondChain.Mine((params.nMinerConfirmationWindow * 3) - 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
    135-    BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
    136+    BOOST_CHECK(check_bit_set(lastBlock, params, bit));
    137+
    138     lastBlock = secondChain.Mine(params.nMinerConfirmationWindow * 3, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
    139 
    140     if (lastBlock->nHeight + 1 < min_activation_height) {
    141         lastBlock = secondChain.Mine(min_activation_height, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
    142     }
    143 
    144-    BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
    145+    BOOST_CHECK(check_bit_unset(lastBlock, params, bit));
    146 }
    
  135. in src/test/versionbits_tests.cpp:406 in be7d1368d8 outdated
    434     }
    435-    // The next block should trigger no longer setting the bit.
    436-    lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
    437-    BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
    438 
    439     // On a new chain:
    


    jnewbery commented at 10:39 am on April 8, 2021:
    Maybe worth splitting this function in two?
  136. in src/test/versionbits_tests.cpp:445 in be7d1368d8 outdated
    452+        check_computeblockversion(chainParams->GetConsensus(), Consensus::DEPLOYMENT_TESTDUMMY);
    453+    }
    454+
    455+    {
    456+        // Use regtest/testdummy to ensure we always exercise the
    457+        // min_lock_in_time test, even if we're not using that in a
    


    jnewbery commented at 10:45 am on April 8, 2021:
    0        // min_activation_height test, even if we're not using that in a
    

    ajtowns commented at 1:21 am on April 12, 2021:
    Done
  137. in src/test/versionbits_tests.cpp:50 in be7d1368d8 outdated
    43@@ -44,6 +44,12 @@ class TestConditionChecker : public AbstractThresholdConditionChecker
    44     int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, paramsDummy, cache); }
    45 };
    46 
    47+class TestDelayedActivationConditionChecker : public TestConditionChecker
    48+{
    49+public:
    50+    int MinActivationHeight(const Consensus::Params& params) const override { return 15000; }
    


    jnewbery commented at 11:14 am on April 8, 2021:
    Perhaps explicitly define MinActivationHeight() in the base TestConditionChecker class?

    JeremyRubin commented at 6:17 pm on April 8, 2021:
    Could be done, personally indifferent – the main difference between why the others are explicit in TestConditionChecker and MinActivationHeight is not is that MinActivationHeight is defined to have a sane default function, whereas the others are not.

    ajtowns commented at 1:48 am on April 12, 2021:
    I think it would be better for the ConditionChecker to not be abstract and just decode the Consensus::Params structure directly, so that tests just pass in different data, but are running the same chunks of code. Leaving this as-is until later refactoring either way though.
  138. in src/test/versionbits_tests.cpp:91 in be7d1368d8 outdated
    83@@ -77,14 +84,18 @@ class VersionBitsTester
    84     int num;
    85 
    86 public:
    87-    VersionBitsTester() : num(0) {}
    88+    VersionBitsTester() : num(1000) {}
    89 
    90     VersionBitsTester& Reset() {
    91+        // Have each group of tests be counted by the 1000s part, starting at 1000
    92+        num = num - (num % 1000) + 1000;
    


    jnewbery commented at 11:15 am on April 8, 2021:
    Maybe clearer to split this into test_group and test_index rather than have some magic 1000 value.
  139. in src/test/versionbits_tests.cpp:87 in be7d1368d8 outdated
    83@@ -77,14 +84,18 @@ class VersionBitsTester
    84     int num;
    85 
    86 public:
    87-    VersionBitsTester() : num(0) {}
    88+    VersionBitsTester() : num(1000) {}
    


    jnewbery commented at 11:16 am on April 8, 2021:
    If you’re touching this, might as well set num to have default initialization and remove the empty ctor.

    MarcoFalke commented at 7:58 am on April 15, 2021:
    Fixed in #21691
  140. in src/test/versionbits_tests.cpp:433 in be7d1368d8 outdated
    424+    lastBlock = secondChain.Mine((params.nMinerConfirmationWindow * 3) - 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
    425+    BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
    426+    lastBlock = secondChain.Mine(params.nMinerConfirmationWindow * 3, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
    427+
    428+    if (lastBlock->nHeight + 1 < min_activation_height) {
    429+        lastBlock = secondChain.Mine(min_activation_height, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
    


    jnewbery commented at 11:25 am on April 8, 2021:
    There could be a test here that the bit continues to be signaled for multiple LOCKED_IN periods.

    ajtowns commented at 1:20 am on April 12, 2021:
    Added a test that min_act_height - 1 still signals.
  141. in src/test/versionbits_tests.cpp:240 in be7d1368d8 outdated
    234@@ -214,10 +235,15 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
    235                            .Mine(3000, TestTime(10000), 0).TestStarted().TestStateSinceHeight(3000)
    236                            .Mine(4000, TestTime(10000), 0).TestStarted().TestStateSinceHeight(3000)
    237                            .Mine(5000, TestTime(10000), 0).TestStarted().TestStateSinceHeight(3000)
    238+                           .Mine(5999, TestTime(20000), 0).TestStarted().TestStateSinceHeight(3000)
    239                            .Mine(6000, TestTime(20000), 0).TestFailed().TestStateSinceHeight(6000)
    240-                           .Mine(7000, TestTime(20000), 0x100).TestFailed().TestStateSinceHeight(6000);
    241+                           .Mine(8000, TestTime(20000), 0x100).TestFailed().TestStateSinceHeight(6000)
    


    jnewbery commented at 11:53 am on April 8, 2021:
    What’s the reason for this change (7000->8000)?

    instagibbs commented at 1:29 pm on April 8, 2021:
    tests pass without this change included fwiw

    ajtowns commented at 1:44 pm on April 8, 2021:
    It gives it two periods in FAILED that are signalling instead of one to give it plenty of time to decide to switch to some other state if there’s some bug that allows transitions out of FAILED. (If it were to go out of FAILED and back again, the StateSinceHeight check should catch that)

    jnewbery commented at 2:13 pm on April 8, 2021:

    Seems reasonable, although it may be worth explicitly commenting that:

     0diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
     1index 5f53da8aff..19bf3f948f 100644
     2--- a/src/test/versionbits_tests.cpp
     3+++ b/src/test/versionbits_tests.cpp
     4@@ -237,7 +237,8 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
     5                            .Mine(5000, TestTime(10000), 0).TestStarted().TestStateSinceHeight(3000)
     6                            .Mine(5999, TestTime(20000), 0).TestStarted().TestStateSinceHeight(3000)
     7                            .Mine(6000, TestTime(20000), 0).TestFailed().TestStateSinceHeight(6000)
     8-                           .Mine(8000, TestTime(20000), 0x100).TestFailed().TestStateSinceHeight(6000)
     9+                           .Mine(7000, TestTime(20000), 0x100).TestFailed().TestStateSinceHeight(6000)
    10+                           .Mine(8000, TestTime(20000), 0x100).TestFailed().TestStateSinceHeight(6000) // Make sure we don't transition out of FAILED
    11         ;
    12     }
    13 }
    

    ajtowns commented at 1:20 am on April 12, 2021:
    Added 7000 back, bumped 8000 to 24000 as in some of the other cases and added a comment.
  142. jnewbery commented at 12:18 pm on April 8, 2021: member

    Reviewed everything. All the changes look good to me. utACK be7d1368d8e98b6f9f692289b7d25ac369bf2984.

    I’ve left a bunch of style suggestions inline. Nothing critical, so feel free to ignore.

    Thank you for persevering with this, @ajtowns, and for all of the prerequisite work that you’ve done over the last couple of years to improve testing and develop activation proposals. Thanks also to @achow101 and all other reviewers whose suggestions have improved and strengthened this code.

  143. michaelfolkson commented at 12:30 pm on April 8, 2021: contributor

    From @maaku on using block height versus a mix of block height and MTP. Understandably he doesn’t want to be involved.

    “Segwit activation exposed a number of problems with the BIP9 activation state machine which was based on MTP windows. These concerns include:

    Difficulty of interfacing with in-parallel activation mechanisms like BIP148, BIP91, or the presumed future LOT=true UASF for taproot. (You may not like UASF. You may be against UASF and wish is never happened. Well reality doesn’t care. There will be a UASF client, that is simply a fact. That cat is out of the bag. Our job is to make sure that UASF risks are minimized for the sake of the network, which height-based activation mechanisms do better. I’m not saying MTP can’t be UASF compatible, I’m just saying it’s way easier to avoid foot-gunning a UASF deployment with height-based windows. And while your inner schadenfreude might laugh at a UASF foot-gun, such a chain-splitting failure affects everyone.)

    Perverse timestamp-manipulation incentives for miners, who can use timestamp manipulation to slow or speed up MTP to delay or speed up the closing of an activation window. People rely on accurate MTP timestamps for things like lock times, and of course it affects difficulty adjustments, and so it is very bad for there to be incentives for miners to manipulate timestamps.

    Two-week uncertainty over activation window start or end times based on alignment of 2016-block window with calendar dates. Makes it very difficult for people to schedule travel or events around a single likely activation date (+/- a day or two). This affects not just activation parties (which is a silly reason IMHO, even more so in the midst of a pandemic), but also mining and infrastructure organizations which will want staff at hand in case there are activation issues.

    BIP8 was specifically created to codify our lessons learned from activating segwit into a new activation protocol to be used in the next soft-fork. It got significant review 4 years ago and in the time since, and was strengthened by that review. There is no way that some new state machine for MTP-based activation could receive the same amount of review in the speedy trial timeline. We should use the reviewed mechanisms that are already in place (or very nearly so) and which incorporate lessons learned. If you think there are flaws in BIP8(LOT=false), then start working now on an alternative activation mechanism for the next soft-fork.

    Every single one of the above points has been made on the mailing list or in relevant Github discussions. I don’t think that any of them have been adequately addressed by MTP advocates. For example, pointing out that “timewarp could cause more uncertainty than +/- 2 weeks” ignores a few facts: (1) timewarp is NOT a normal condition and would itself be seen as an attack on the network; (2) timewarp would require the coordination of more miners than it would take to block taproot anyway; and (3) having monotonically decreasing estimation error without a two-week floor is critical to scheduling needs.

    I think many of us who were involved in that prior work that went into segwit lessons learned and BIP8 review are sitting around dumbfounded and speechless at what has happened. What is the point of doing all that advanced planning if it’s immediately thrown away precisely when it is needed? I’m not surprised if Luke-Jr and Jorge seem obstinate on this. They’ve invested literally years on this problem so that we’d have a solution when it is needed, as it is now, and getting buy-in from the developers at the time… only to have all that casually thrown away. (Because of a freaking coin flip?)”

  144. in src/versionbits.cpp:55 in ccfe854884 outdated
    56@@ -57,18 +57,12 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
    57 
    58         switch (state) {
    59             case ThresholdState::DEFINED: {
    60-                if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) {
    61-                    stateNext = ThresholdState::FAILED;
    


    harding commented at 1:55 pm on April 8, 2021:

    In commit ccfe854884276fbdd2ddbcc65689dbd3306f6332 “versionbits: simplify state transitions”

    If I restore the logic to go straight to FAILED here, the unit tests don’t complain. I didn’t try with the fuzz tester. Maybe a chance to expand the tests here?


    instagibbs commented at 2:35 pm on April 8, 2021:
    Yes this needs coverage. I was unable to coax any other phony successful tests by modifying relevant changes in the code.

    jnewbery commented at 3:14 pm on April 8, 2021:

    Good catch! I think this oughta do it:

     0diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
     1index 5f53da8aff..57d9f1bedf 100644
     2--- a/src/test/versionbits_tests.cpp
     3+++ b/src/test/versionbits_tests.cpp
     4@@ -200,6 +200,15 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
     5                            .Mine(3000, TestTime(20000), 0).TestFailed().TestStateSinceHeight(3000) // 50 old blocks (so 899 out of the past 1000)
     6                            .Mine(4000, TestTime(20010), 0x100).TestFailed().TestStateSinceHeight(3000)
     7 
     8+        // DEFINED -> STARTED after timeout reached -> FAILED
     9+                           .Reset().TestDefined().TestStateSinceHeight(0)
    10+                           .Mine(1, TestTime(1), 0).TestDefined().TestStateSinceHeight(0)
    11+                           .Mine(1000, TestTime(10000) - 1, 0x100).TestDefined().TestStateSinceHeight(0) // One second more and it would be started
    12+                           .Mine(1999, TestTime(20000), 0).TestDefined().TestStateSinceHeight(0) // Still not started
    13+                           .Mine(2000, TestTime(20000), 0).TestStarted().TestStateSinceHeight(2000) // Skip straight to the timeout MTP. We guarantee one period of started
    14+                           .Mine(2999, TestTime(30000), 0).TestStarted().TestStateSinceHeight(2000) // 999 new blocks
    15+                           .Mine(3000, TestTime(30000), 0).TestFailed().TestStateSinceHeight(3000) // ... and fail
    16+
    17         // DEFINED -> STARTED -> LOCKEDIN after timeout reached -> ACTIVE
    18                            .Reset().TestDefined().TestStateSinceHeight(0)
    19                            .Mine(1, TestTime(1), 0).TestDefined().TestStateSinceHeight(0)
    

    ajtowns commented at 10:43 pm on April 11, 2021:

    One way to manually run the fuzz tests without running fuzzing is something like this:

    0ls ../../../qa-assets/fuzz_seed_corpus/versionbits/* --sort=s -r | 
    1while read a; do 
    2  if ! FUZZ=versionbits test/fuzz/fuzz < $a; then
    3    echo $a;
    4    echo 'base64:' $(base64 < $a);
    5    break;
    6  fi;
    7done
    

    (that runs the versionbits fuzzer using the qa-assets seed corpus, in order of smallest seed to largest, and prints out the filename of the first failing seed; assumes you’re in src/ already)

    When I run that with the DEFINED condition changed to if (GetMedianTimePast >= nTimeTimeout) stateNext = FAILED; else if (GetMedianTimePast >= nTimeStart) stateNext = STARTED;, I get:

    0fuzz: test/fuzz/versionbits.cpp:322: void (anonymous namespace)::versionbits_fuzz_target(FuzzBufferType): Assertion `exp_state == ThresholdState::FAILED' failed.
    1../../../qa-assets/fuzz_seed_corpus/versionbits/ed29b8233d29abb3e0398be2221b9bdb8623daf6
    2base64: /5SUlL29vb29va0Axr3y
    

    If you don’t want to download the qa-assets repo, you should be able to duplicate that specific error with echo '/5SUlL29vb29va0Axr3y' | base64 -d | FUZZ=versionbits test/fuzz/fuzz


    harding commented at 11:11 pm on April 11, 2021:
    Oh, I later setup fuzz testing but I didn’t come back to check this. I’ve done that just now using libFuzz and I can confirm it quickly finds this deliberately-introduced problem.

    ajtowns commented at 1:18 am on April 12, 2021:
    I adapted the old DEFINED -> FAILED test case to become DEFINED -> STARTED after timeout reached -> FAILED rather than taking the case above.
  145. harding commented at 2:18 pm on April 8, 2021: contributor

    Reviewed the code of each commit individually and tested its tests. Found one comment that confuses me and one scenario where there could maybe be a better test. See line comments below; complete review and testing journal here: https://gist.github.com/harding/e622323eaf80d620826a7cb74ab3fb40

    Tomorrow I’ll try to do a more holistic review of the final mechanism to convince myself all the state transitions are well tested. I’d also like to play around a bit more with the fuzz testing to get a better handle on it.

    Overall I’m pleased with the code, especially it’s simplicity (which is emphasized when going commit-by-commit with the PR’s excellent commit organization).

  146. JeremyRubin commented at 6:32 pm on April 8, 2021: contributor

    CR-ACK be7d1368d8e98b6f9f692289b7d25ac369bf2984

    Will put some additional effort into futzing with the fuzzers and regtests and stuff before a tAck. Seems like there are some fuzzing edge cases that need better coverage, but overall looks great (and those can be added as follow on commits).

  147. Sjors commented at 7:02 pm on April 8, 2021: member
    It might be useful to adapt the functional tests from #21392 to BIP9. I made a very rudimentary start here: https://github.com/Sjors/bitcoin/commits/2021/04/bip9_speedy_test
  148. laanwj added this to the "Blockers" column in a project

  149. tests: pull ComputeBlockVersion test into its own function
    The intent here is to allow checking ComputeBlockVersion behaviour with
    each deployment, rather than only testdummy on mainnet. This commit does
    the trivial refactoring component of that change.
    63879f0a47
  150. tests: test ComputeBlockVersion for all deployments
    This generalises the ComputeBlockVersion test so that it can apply to
    any activation parameters we might set, and checks all the parameters
    set for each deployment on each chain, to simultaneously ensure that the
    deployments we have configured work sensibly, and that the test code
    does not suffer bitrot in the event that all interesting deployments
    are buried.
    5932744450
  151. tests: clean up versionbits test
    Simplify the versionbits unit test slightly to make the next set of
    changes a little easier to follow.
    9e6b65f6fa
  152. versionbits: Add support for delayed activation 73d4a70639
  153. tests: test versionbits delayed activation dd85d5411c
  154. fuzz: test versionbits delayed activation dd07e6da48
  155. versionbits: Add explicit NEVER_ACTIVE deployments
    Previously we used deployments that would timeout prior to Bitcoin's
    invention, which allowed the deployment to still be activated in unit
    tests. This switches those deployments to be truly never active.
    55ac5f568a
  156. versionbits: simplify state transitions
    This removes the DEFINED->FAILED transition and changes the
    STARTED->FAILED transition to only occur if signalling didn't pass the
    threshold. This ensures that it is always possible for activation to
    occur, no matter what settings are chosen, or the speed at which blocks
    are found.
    f054f6bcd2
  157. ajtowns force-pushed on Apr 12, 2021
  158. ajtowns commented at 1:57 am on April 12, 2021: member

    Updated for feedback, git diff be7d136..ffe33df should be easy to follow if you’d reviewed the previous head.

    [EDIT: bumped the final commit with no changes and re-pushed so as to retrigger CI since the incident has apparently finished]

  159. rxgrant approved
  160. rxgrant commented at 2:18 am on April 12, 2021: none
    utACK. For me this was a learning experience in bitcoind’s test architecture.
  161. ajtowns commented at 2:41 am on April 12, 2021: member
    Note that the comments from maaku copied above have been responded to in the PR where they were originally posted.
  162. chainparams: drop versionbits threshold to 90% for mainnnet and signet ffe33dfbd4
  163. ajtowns force-pushed on Apr 12, 2021
  164. benthecarman approved
  165. benthecarman commented at 3:19 am on April 12, 2021: contributor
    ACK ffe33dfbd4c3b11e3475b022b6c1dd077613de79
  166. instagibbs commented at 6:54 am on April 12, 2021: member

    re-utACK https://github.com/bitcoin/bitcoin/pull/21377/commits/ffe33dfbd4c3b11e3475b022b6c1dd077613de79

    API changed to return min_height_activation regardless of value, other assorted comments. I’ll verify the new test case triggers when mutating logic later today to change to a tACK

  167. instagibbs commented at 7:10 am on April 12, 2021: member

    tACK https://github.com/bitcoin/bitcoin/pull/21377/commits/ffe33dfbd4c3b11e3475b022b6c1dd077613de79

    I can’t trigger any more mutation false-successful runs.

  168. jnewbery commented at 7:24 am on April 12, 2021: member

    utACK ffe33dfbd4c3b11e3475b022b6c1dd077613de79

    Verified range-diff from be7d1368d8

  169. MarcoFalke commented at 9:49 am on April 12, 2021: member

    review ACK ffe33dfbd4c3b11e3475b022b6c1dd077613de79 💈

    Also checked that it addresses all my style feedback I left on the other pull ( #21392#pullrequestreview-621891984 )

    Signature:

     0-----BEGIN PGP SIGNED MESSAGE-----
     1Hash: SHA512
     2
     3review ACK ffe33dfbd4c3b11e3475b022b6c1dd077613de79 💈
     4
     5Also checked that it addresses all my style feedback I left on the other pull ( [#21392](/bitcoin-bitcoin/21392/)#pullrequestreview-621891984 )
     6-----BEGIN PGP SIGNATURE-----
     7
     8iQGzBAEBCgAdFiEE+rVPoUahrI9sLGYTzit1aX5ppUgFAlwqrYAACgkQzit1aX5p
     9pUjIiwv/elbuMaynxLCVMxZAuduzypZgl4ZM/6IysmHZSw9FxhYgXWih3UrIVlHG
    10jQpEqUorssfPE0dUeVNG7ToVCs9w5t+DSzJpKJUDzHsESnoXkxvZm/PqWc9q3+vr
    11fVv3azJ6LOnyUmxBUy8vZf+FMck66cJq6VPhfG24kL57MgX24JRk8NT0swiuWLS9
    12Ekz1N2bSKB7iGeTyhLbyjrNiSNK29BUeT0dqKNTx2jqsnrDAyzD4skZLT8ZubWdY
    13kW+njZwz2ZNvvs+ffHcG/EJpCeRAmlDBw+F6TD1bm+WilcmXY2bp9uLiUE1Uo2eR
    14kPk2RlQxJtVDGwFpUfGYX7847eePYrMgUsDVsrSCkdqhHh+2UAwlyzqQrr0Z8lAS
    15NF02RYBFcFmwbuJ2Z7+Fu5pvlqcZ4q9YHepj8R3BTHKJURCj6ss9FNby0mEUYdfJ
    16jF63r+KwMljW3jbox10SEe1T32MZHuOyu4VaBv+hs1Rjna9JgKbZQaHPo1pOAcid
    17AlhYcuHs
    18=pYnx
    19-----END PGP SIGNATURE-----
    

    Timestamp of file with hash 9edb49a6fd6bab8f19ff268a5fde21eb741d236d3f07ee453643334bfb0a3cac -

  170. in src/test/versionbits_tests.cpp:446 in ffe33dfbd4
    453+{
    454+    // check that any deployment on any chain can conceivably reach both
    455+    // ACTIVE and FAILED states in roughly the way we expect
    456+    for (const auto& chain_name : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::SIGNET, CBaseChainParams::REGTEST}) {
    457+        const auto chainParams = CreateChainParams(*m_node.args, chain_name);
    458+        for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++i) {
    


    MarcoFalke commented at 10:00 am on April 12, 2021:

    in commit f054f6bcd2c2ce5fea84cf8681013f85a444e7ea:

    It would be nice to check that no bits are re-used (within the same chain). I guess previously there was no test for this because BIP 9 allowed (though discouraged) this. With the changes here this should be disallowed because the transition to FAILED (on timeout) no longer takes precedence over STARTED/LOCKED_IN. So with the changes here all softforks on the same bit might overlap, even when non-overlapping start-end times are picked.


    jonatack commented at 3:11 pm on April 12, 2021:

    593274445004506c921d5d851361aefb3434d744 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es49-if-you-must-use-a-cast-use-a-named-cast

    0        for (int i = 0; i < static_cast<int>(Consensus::MAX_VERSION_BITS_DEPLOYMENTS); ++i) {
    

    MarcoFalke commented at 3:23 pm on April 14, 2021:
    If overlapping deployments are an issue (I don’t think they are as long as bits are not re-used for to-be-deployed changes), we could switch to the version of this pull requests that avoids them: #21377#pullrequestreview-619480897. (That version also has the min_activation based on height). I liked that version better because it really was a minimal change from BIP 9, but I am not sure what others think about picking that version.

    MarcoFalke commented at 7:57 am on April 15, 2021:
    Added test in #21691
  171. in src/chainparams.cpp:81 in ffe33dfbd4
    77@@ -78,7 +78,7 @@ class CMainParams : public CChainParams {
    78         consensus.nPowTargetSpacing = 10 * 60;
    79         consensus.fPowAllowMinDifficultyBlocks = false;
    80         consensus.fPowNoRetargeting = false;
    81-        consensus.nRuleChangeActivationThreshold = 1916; // 95% of 2016
    82+        consensus.nRuleChangeActivationThreshold = 1815; // 90% of 2016
    


    MarcoFalke commented at 10:02 am on April 12, 2021:
    commit ffe33dfbd4c3b11e3475b022b6c1dd077613de79: While touching this line, maybe also add a link somewhere to the section “Modified thresholds” in https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#support-for-future-changes ?
  172. in src/test/versionbits_tests.cpp:301 in ffe33dfbd4
    301+    // This implicitly uses versionbitscache, so clear it every time
    302+    versionbitscache.Clear();
    303+
    304+    int64_t bit = params.vDeployments[dep].bit;
    305+    int64_t nStartTime = params.vDeployments[dep].nStartTime;
    306+    int64_t nTimeout = params.vDeployments[dep].nTimeout;
    


    jonatack commented at 2:53 pm on April 12, 2021:

    63879f0a4760c0c0f784029849cb5d21ee088abb these can all be const (and eventually braced initialization to check for narrowing)

    0-    int64_t bit = params.vDeployments[dep].bit;
    1-    int64_t nStartTime = params.vDeployments[dep].nStartTime;
    2-    int64_t nTimeout = params.vDeployments[dep].nTimeout;
    3+    const int64_t bit{params.vDeployments[dep].bit};
    4+    const int64_t nStartTime{params.vDeployments[dep].nStartTime};
    5+    const int64_t nTimeout{params.vDeployments[dep].nTimeout};
    

    sipa commented at 10:29 pm on April 13, 2021:

    I think this is mostly personal style choice, so more as a general suggestion: if you really just want to get shorter aliases for other expressions, this may be even better (guaranteed to have the same type, and will avoid a copy in case it’s a nontrivial object).

    0const auto& bit = params.vDeployments[dep].bit;
    1...
    

    Even if the right hand is a nontrivial expression that computes a temporary, lifetime extension makes this formula safe to use.

  173. in src/test/versionbits_tests.cpp:348 in ffe33dfbd4
    372+        // Start generating blocks before nStartTime
    373+        lastBlock = firstChain.Mine(params.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
    374+        BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
    375+
    376+        // Mine more blocks (4 less than the adjustment period) at the old time, and check that CBV isn't setting the bit yet.
    377+        for (uint32_t i = 1; i < params.nMinerConfirmationWindow - 4; i++) {
    


    jonatack commented at 2:56 pm on April 12, 2021:

    63879f0a4760c0c0f784029849cb5d21ee088abb here and 593274445004506c921d5d851361aefb3434d744 lines 355 and 396, prefix iterator

    0        for (uint32_t i = 1; i < params.nMinerConfirmationWindow - 4; ++i) {
    
  174. in src/test/versionbits_tests.cpp:451 in 55ac5f568a outdated
    443@@ -447,6 +444,15 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
    444         }
    445     }
    446 
    447+    {
    448+        // Use regtest/testdummy to ensure we always exercise some
    449+        // deployment that's not always/never active
    450+        ArgsManager args;
    451+        args.ForceSetArg("-vbparams", "testdummy:1199145601:1230767999"); // January 1, 2008 - December 31, 2008
    


    Sjors commented at 3:14 pm on April 12, 2021:
    55ac5f568a3b73d6f1ef4654617fb76e8bcbccdf: why not just use -2 here?

    ajtowns commented at 11:57 pm on April 12, 2021:
    -2 would be never active, this is to ensure we execute the test code even if all the actual deployments are always active or never active, to avoid the test code bit rotting

    MarcoFalke commented at 4:12 am on April 13, 2021:
    Maybe add an explicit test for -1/-2 as well?

    ajtowns commented at 6:06 am on April 13, 2021:
    Currently taproot on regtest/signet tests the always active (-1) case, and testdummy on mainnet should always test the never active (-2) case, so think this can be left for later cleanup/refactoring.

    MarcoFalke commented at 6:12 am on April 13, 2021:
    Sure, no need to force push. I am just dumping ideas.

    Sjors commented at 12:34 pm on April 13, 2021:

    @ajtowns maybe I’m missing something, but January 1, 2008 - December 31, 2008 is also never active. Or do we use earlier fake dates in the test?

    Ah I see: #21377 (review) . The casual reader - and me - might understand the test better when using a realistic range, but no need to change things here.


    ajtowns commented at 2:09 pm on April 13, 2021:
    @Sjors Note that with the updated state machine, it’s possible for a real chain (not just a fake one in the unit tests) to become ACTIVE even if starttime/timeout are prior to the genesis block’s timestamp: in that case, the first period will be DEFINED, the second STARTED, and the third either LOCKED_IN or FAILED depending on signalling in the second period. Not an issue for chains that are already well past genesis though, so real is limited to regtest, brand new signets, or someone trying to reorg mainnet/testnet3 all the way to genesis.
  175. jonatack commented at 3:19 pm on April 12, 2021: member
    First pass checkpoint, reviewed/tested the first 3 commits rebased to current master. A few nits if you retouch. Continuing forward.
  176. in src/test/versionbits_tests.cpp:455 in dd85d5411c outdated
    450+    {
    451+        // Use regtest/testdummy to ensure we always exercise the
    452+        // min_activation_height test, even if we're not using that in a
    453+        // live deployment
    454+        ArgsManager args;
    455+        args.ForceSetArg("-vbparams", "testdummy:1199145601:1230767999:403200"); // January 1, 2008 - December 31, 2008, min act height 403200
    


    Sjors commented at 3:20 pm on April 12, 2021:

    dd85d5411c1702c8ae259610fe55050ba212e21e: why is the timeout set to 2008 here? In 55ac5f568a3b73d6f1ef4654617fb76e8bcbccdf we don’t the start height to -2 here, so the test won’t early-exit, but I think it’s more clear to just set a far future timeout date:

    0args.ForceSetArg("-vbparams", "testdummy:1199145601:4102441200:403200"); // January 1, 2008 - January 1, 2100, min act height 403200
    

    ajtowns commented at 0:00 am on April 13, 2021:
    It’s duplicating the old testdummy parameters. It’s a fake chain so the exact times don’t matter. The difference between the times can make a difference, but only if the period’s short enough that two periods of 10 minute blocks will cover the entire difference.
  177. Sjors approved
  178. Sjors commented at 3:39 pm on April 12, 2021: member

    ACK ffe33dfbd4c3b11e3475b022b6c1dd077613de79

    Concept ACK on the state transition simplification for BIP 9. I made a PR to the BIP repo to reflect these changes: https://github.com/bitcoin/bips/pull/1101

  179. in src/chainparamsbase.cpp:25 in ffe33dfbd4
    21@@ -22,7 +22,7 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman)
    22                  "This is intended for regression testing tools and app development. Equivalent to -chain=regtest.", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
    23     argsman.AddArg("-segwitheight=<n>", "Set the activation height of segwit. -1 to disable. (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
    24     argsman.AddArg("-testnet", "Use the test chain. Equivalent to -chain=test.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
    25-    argsman.AddArg("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
    26+    argsman.AddArg("-vbparams=deployment:start:end[:min_activation_height]", "Use given start/end times and min_activation_height for specified version bits deployment (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
    


    jonatack commented at 3:42 pm on April 12, 2021:

    73d4a706393e6dbd6b6d6b6428f8d3233ac0a2d8 maybe mention “optional” in addition to the brackets, as done for e.g, -rpcbind

    0    argsman.AddArg("-vbparams=deployment:start:end[:min_activation_height]", "Use given start/end times and optional min_activation_height for specified version bits deployment (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
    
  180. in src/test/versionbits_tests.cpp:242 in ffe33dfbd4
    240-                           .Mine(14333, TestTime(30003), 0).TestActive().TestStateSinceHeight(4000)
    241-                           .Mine(24000, TestTime(40000), 0).TestActive().TestStateSinceHeight(4000)
    242+                           .Mine(4000, TestTime(30002), 0).TestActiveDelayed().TestStateSinceHeight(4000, 3000) // delayed will not become active until height=15000
    243+                           .Mine(14333, TestTime(30003), 0).TestActiveDelayed().TestStateSinceHeight(4000, 3000)
    244+                           .Mine(15000, TestTime(40000), 0).TestActive().TestStateSinceHeight(4000, 15000)
    245+                           .Mine(24000, TestTime(40000), 0).TestActive().TestStateSinceHeight(4000, 15000)
    


    jonatack commented at 5:00 pm on April 12, 2021:

    dd85d5411c1702c8ae259610fe55050ba212e21e for these four calls to TestStateSinceHeight(int height, int height_delayed)

    0                           .Mine(24000, TestTime(40000), 0).TestActive().TestStateSinceHeight(4000, /* height_delayed */ 15000)
    
  181. in src/test/fuzz/versionbits.cpp:168 in ffe33dfbd4
    172+            start_time = Consensus::BIP9Deployment::NEVER_ACTIVE;
    173             never_active_test = true;
    174         }
    175+        timeout = fuzzed_data_provider.ConsumeBool() ? Consensus::BIP9Deployment::NO_TIMEOUT : fuzzed_data_provider.ConsumeIntegral<int64_t>();
    176     }
    177+    int min_activation = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * max_periods);
    


    jonatack commented at 5:20 pm on April 12, 2021:

    dd07e6da48040dc7eae46bc7941db48d98a669fd can be const

    0    const int min_activation{fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * max_periods)};
    
  182. jonatack commented at 5:59 pm on April 12, 2021: member
    Initial approach ACK ffe33dfbd4c3b11e3475b022b6c1dd077613de79 after a first pass of review, building and testing each commit, mostly looking at the changes and diffs. Will do a more high-level review iteration. A few minor comments follow to pick/choose/ignore.
  183. earonesty approved
  184. MaxHillebrand approved
  185. MaxHillebrand commented at 8:17 pm on April 12, 2021: none
    concept ACK
  186. JeremyRubin commented at 1:25 am on April 13, 2021: contributor
    re CR-ack based on git diff be7d136..ffe33df
  187. michaelfolkson commented at 1:58 pm on April 13, 2021: contributor

    I’m going to try to make this my last comment on this PR because I’m wary of continuously adding noise and disrupting review. I just want to make sure we are all happy with a mix of block height and MTP rather than a consistent use of block height. Personally I just about am but my preference would definitely be a consistent use of block height.

    A minor preference for consistent use of block height from: @achow101 (link) @Sjors (link) @roconnor-blockstream (link) @benthecarman (link) @michaelfolkson @MarcoFalke (link) @instagibbs (link)

    A minor preference for mix of block height and MTP from: No one

    Entirely neutral: @harding @jonatack

    A literal NACK for consistent use of block height from: @ajtowns (link)

    A literal NACK for mix of block height and MTP from: @luke-jr (link) @maaku (link)

    I’m personally not sure where to put them: @jeremyrubin @ariard

    If I have mischaracterized anyone’s view please message me on IRC or on ##taproot-activation channel and I will update this comment. Please don’t comment on the PR as the focus should be on code and testing. However, if people like AJ’s PR but they’d prefer consistent block height than mix of block height and MTP (essentially they just don’t like BIP 8) then we should be going with this PR, a consistent use of block height and either BIP 9 or a new BIP.

  188. ajtowns commented at 5:13 pm on April 13, 2021: member
    Corresponding spec text at https://github.com/bitcoin/bips/pull/1104
  189. in src/versionbits.cpp:75 in ffe33dfbd4
    73@@ -74,12 +74,16 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
    74                 }
    75                 if (count >= nThreshold) {
    


    ariard commented at 8:16 pm on April 13, 2021:

    I think this is behavior is an explicit deviation from bip9.

    If the last block of the retarget period is fulfilling the threshold condition but with a MTP past with the timeout, the locked_in takes precedence over the failure transition. This sounds contrary to the bip 9 : “MTP < timeout AND (threshold reached)” ?

    If you retouch the branch or as a follow-up, I think it would be worthy to document.


    JeremyRubin commented at 10:11 pm on April 13, 2021:

    JeremyRubin commented at 10:13 pm on April 13, 2021:
    Maybe that doesn’t clarify the change – the point is to ensure at least one signalling period

    JeremyRubin commented at 10:15 pm on April 13, 2021:

    https://github.com/bitcoin/bips/pull/1104#issuecomment-819068299

    That’s probably what you want to look at.

  190. achow101 commented at 8:27 pm on April 13, 2021: member
    re-ACK ffe33dfbd4c3b11e3475b022b6c1dd077613de79
  191. ariard commented at 9:02 pm on April 13, 2021: member

    Code Review ACK ffe33df

    I did exercise the unit test coverage by manually mutating I think all the transition logics of the versionbits state machine. I didn’t find holes.

    I’ll try to have a look on fuzz testing coverage.


    @michaelfolkson I’m neutral on this debate and more preoccupied by the size of the changeset and thoroughness of its review than other concerns raised so far in the MTP vs consistent height use debate.

    Thanks to share maaku arguments, especially about malicious timestamp-manipulation by miners that I also raised in this PR previously. Honestly, if we see a mining cartel starting timewarp attacks even with the auxiliary intent of blocking taproot activation, I would be more worried of fixing the timewarp vuln rather than taproot not activating.

    If those perverse miners behaviors do happen and ST doesn’t succeed, we’ll at least have done a glaring step forward in the taproot activation debate. Assuming this hypothetical scenario, I think we can discuss again safe UASF mechanisms built-in in Core.

  192. luke-jr changes_requested
  193. luke-jr commented at 5:08 pm on April 14, 2021: member
    This remains Concept NACK. It is contrary to the community consensus around BIP 8, and a technically inferior solution. Developers do not get to strong-arm protocol changes any more than miners do.
  194. jamesob commented at 5:14 pm on April 14, 2021: member
    Concept ACK. I’m in the process of reviewing the code.
  195. molxyz commented at 5:23 pm on April 14, 2021: none
    Concept ACK. Approach ACK.
  196. ProofOfKeags commented at 5:58 pm on April 14, 2021: none
    Concept ACK. Reviewing the code now.
  197. zndtoshi commented at 6:04 pm on April 14, 2021: none
    Concept ACK
  198. kastravec commented at 6:13 pm on April 14, 2021: none
    Concept ACK Approach ACK
  199. Sjors commented at 6:59 pm on April 14, 2021: member
    @luke-jr you made the same comment on the BIP. As others have asked, can you elaborate on it? The BIP or mailinglist is probably the best place for that. In particular, when you say “BIP 8” are you arguing to switch back to #21392 or do you want a full on LOT=true BIP 8 implementation?
  200. guerillaV2 commented at 7:13 pm on April 14, 2021: none
    Concept ACK. Approach ACK.
  201. cnavigato commented at 7:41 pm on April 14, 2021: none
    Concept ACK. Approach ACK.
  202. gmaxwell commented at 7:56 pm on April 14, 2021: contributor

    ACK ffe33dfbd4c3b11e3475b022b6c1dd077613de79

    I haven’t been a Bitcoin developer for several years now but I am a user and investor with a solid technical background, having materially contributed to I believe every intentional consensus change since Satoshi’s last. I still follow along with some technical developments, including taproot activation related PRs.

    I’ve reviewed the PR and (AFAIK) all the publicly available conversation and spoken to many users I know in the ecosystem. From my research and discussion it is unambiguous that this is not just perfectly acceptable but a good path forward and also extremely broadly supported. Generally I’ve found users are of the perspective that whatever activation gets taproot activated quickly without causing relevant problems is a good thing, along with a lot of frustration over what they perceive to be arguing over pointless things.

  203. in src/chainparams.cpp:506 in ffe33dfbd4
    502@@ -492,22 +503,26 @@ void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args)
    503     for (const std::string& strDeployment : args.GetArgs("-vbparams")) {
    504         std::vector<std::string> vDeploymentParams;
    505         boost::split(vDeploymentParams, strDeployment, boost::is_any_of(":"));
    506-        if (vDeploymentParams.size() != 3) {
    507-            throw std::runtime_error("Version bits parameters malformed, expecting deployment:start:end");
    508+        if (vDeploymentParams.size() < 3 || 4 < vDeploymentParams.size()) {
    


    ProofOfKeags commented at 8:18 pm on April 14, 2021:

    nit: Apologies if this is bikeshedding, but the asymmetry of < and > causes more mental load than the code it replaces since inequality is commutative. It took me a sec to wrap my head around it and I wonder if something like

    0        if (vDeploymentParams.size() != 3 && vDeploymentParams.size() != 4) {
    

    would be more readable. Obviously this is not strictly necessary, and if more sizes become permissible than just the two acceptable here, the code you wrote would be more future proof.


    katesalazar commented at 7:50 pm on September 30, 2021:
    The proposed inequality reads naturally. Your suggested change is common code, so it’s easy to read as well. Your suggested change is not more future proof, I’d say it’s slightly less future proof.
  204. luke-jr commented at 8:47 pm on April 14, 2021: member

    In particular, when you say “BIP 8” are you arguing to switch back to #21392 or do you want a full on LOT=true BIP 8 implementation?

    A full BIP8 with LOT=True would be ideal, but #21392 was at least an acceptable compromise considering the lack of consensus around LOT.

  205. ghost commented at 9:01 pm on April 14, 2021: none

    I have few questions and issues:

    1. Is this strictly based on BIP 9 or modified version of BIP 9? If modified, what are the changes and reasons for it?

    2. What are the trade-offs involved in using ST|BIP9|MTP vs ST|BIP8|Block Height?

    3. Use of BIP 9:

    Quoting few things from https://medium.com/@elombrozo/forks-signaling-and-activation-d60b6abda49a since I had followed all the things in 2017 and Eric Lombrozo had been a part of lot of discussions I read or watched in different podcasts.

    In hindsight, it’s become clear that miner activation of soft forks cannot be relied upon when there exists a divergence of interests between miners and users. In the cooperative case, it is a tried and tested mechanism which if done correctly is known to work smoothly. However, in the adversarial case it simply does not work. BIP9 effectively gives a small amount of hashpower veto power over any soft fork activation no matter how widely popular it is among users and industry.

    So given these lessons of the past, it seems that a combination of MASF and UASF will be required for future deployments to properly address these adversarial scenarios. It is unlikely BIP9 will be ever used again.

    Quoting few things from https://medium.com/hackernoon/peter-todd-on-the-essence-of-bitcoin-b8d0c6d16f43

    No, definitely not. No. Most programmers in general, do you not have the capability to work on this stuff, because they don’t think it adversarially. It’s a tough thing for people to imagine. Most people are just not used to imagining, “All right, what are the bad things that could happen?”

    Lot of people think its okay to ignore few things in this activation because we all want taproot and some think being prepared is PTSD from 2017. I don’t agree with it because its Bitcoin and we need to be prepared for every bad scenario. We don’t trust anyone if using full nodes for Bitcoin, why should we trust the miners who got incentives to mine blocks and in some case incentives to attack the network?

    I had mentioned few things in this email: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2021-March/018613.html

    There was one line written on the wall of a school I still remember: The more we sweat in peace, the less we bleed in war

    1. Use of MTP:

    Although SE answer and few other posts mentioned few trade-offs, I am still not sure if using MTP has no risk in a soft fork activation mechanism. Coinflip even if not considered for deciding MTP vs block height was wrong way to decide something involved in soft fork activation mechanism which can change lot of risk/rewards for miners to attack.

    https://bitcoin.stackexchange.com/questions/103854/should-block-height-or-mtp-or-a-mixture-of-both-be-used-in-a-soft-fork-activatio

    1. There are several occasions when something was preferred because it will take less time to review or less time to code and make changes. I understand its a “Speedy” Trial and things need to be done in less time with less people who go the right skills and experience, however this approach for development in Bitcoin Core (90% nodes use Bitcoin Core implementation) will not be good in long term. Maybe we need more people who can help and more people who can donate for such efforts. I appreciate everything done by @ajtowns and @achow101, also everyone who reviewed the PR but things can be improved. Bitcoin Core is 4th most critical C++ OSS project and most of the users prefer using it when comparing usage of different implementations.

    http://gnusha.org/taproot-activation/

    1. Its not just @luke-jr who has different opinion about the approach and even if he is alone with a different idea that may work better, we should always respect his opinion as well. I have seen lot of comments in IRC logs which doesn’t look good.
  206. gmaxwell commented at 9:19 pm on April 14, 2021: contributor

    @prayank23 The activation mechanism is not the same as BIP9. The spec is at https://github.com/bitcoin/bips/pull/1104 because this is “speedy trial” and not BIP9 it has shorter duration, a lower threshold, and a minimum activation point (to give people adequate time to update).

    In terms of preparation– it is not feasible to prepare when you don’t know what you’re preparing for and the preparations come at great cost. The idea with speedy trial is that is an inexpensive way to learn the lay of the land such that further moves (if required at all) can be informed by actual objective information, rather than just having community members set their subjective speculations, hopes, and fears in a battle of rhetoric against each other. Preparing for the wrong thing can easily be worse than not preparing at all.

    It sounds like you may misunderstand what the “speedy” part refers to here– it isn’t about implementation complexity. It’s about learning how activation might activate or fail, in detail, with objective facts quickly. The implementation here is also designed to leverage the existing stored testing and experience because that is just prudent engineering and no amount of additional time or added developers can fully replace that. Even with all the resources in the world, you’re better off not introducing changes without good cause– those resources can better be used some other way.

    As such the most valuable thing for ST to accomplish is to be over with soon (activated or expired) and to do so without continuing to burn up goodwill among community members fighting over opinions rather than facts. If it activates, GREAT. If it doesn’t activate– that’s great too– because we will have learned about how it didn’t activate and that will inform the required preparations and tradeoffs for further efforts. The veto comment is directly addressed by ST (and also really was partially a criticism not of BIP9 but of the fact that the NODE_WITNESS changes made segwit difficult to reissue, leading to a mistaken belief that if it didn’t activate it couldn’t be activated later.)

    ST is in some sense the opposite of trusting. Instead of “trusting” participants to behave in particular ways, we just set things up so that no matter what happens things will be okay and then we see what actually happens. This is much better than speculating, which is really just trusting our guesses and trusting others to behave in particular ways (perhaps you trust them to behave badly, but that is still an expectation). ST is all about minimizing subjective assumptions in favor of learning objective facts.

    In fact, if anything, I’d characterize allowing a broadly supported improvement to get jammed over the fine details of activation is a perfect example of failing to think adversarially. Even to someone who is of the view that the fine details of the activation here aren’t the best possible under their preferred weighing of the tradeoffs should recognize that after ST is over (via activation or expiration) these activation criteria will not be relevant at all anymore. This puts a strict upper bound on how much these details matter.

    Coinflip even if not considered for deciding MTP vs block height

    Since it appears you know it wasn’t actually used bringing it up here is not really appropriate. Please don’t do that. I’d like no more to discuss with you at length why I am confident you are mistaken, but it would be entirely off-topic here.

  207. ProofOfKeags approved
  208. ProofOfKeags commented at 9:51 pm on April 14, 2021: none
    Had some code readability nits. Semantically a code ACK, though.
  209. harding commented at 10:42 pm on April 14, 2021: contributor

    @prayank23

    1. Is this strictly based on BIP 9 or modified version of BIP 9? If modified, what are the changes and reasons for it?

    It’s modified it two ways. First, the timeout is only evaluated at the end of a STARTED period after all signaling blocks in that period have been counted. This prevents miners from skipping over the signaling period altogether; that’s mainly a concern for attempts to require mandatory signaling (e.g. a UASF) but it also simplifies things here a bit.

    Second, it contains a minimum_activation_height parameter that delays activation of a locked_in fork until the specified height, making it compatible with the ST proposal.

    1. What are the trade-offs involved in using ST|BIP9|MTP vs ST|BIP8|Block Height?

    ST|BIP9|MTP is compatible with activating future soft forks on signet. ST|BIP9|Height is slightly simpler to review due to heights generally being monotonicly increasing and always being impossible to skip (although, again, this PR eliminates the skipping problem for MTP).

    3. Use of BIP 9:
    

    [Quoting from Eric Lombrozo] it’s become clear that miner activation of soft forks cannot be relied upon when there exists a divergence of interests between miners and users.

    That remains clear. In the case of taproot, miners have said that there’s a convergence of interests and Speedy Trial is an attempt to see if that’s actually the case. It’s designed either quickly succeed or quickly fail so that we can, correspondingly, quickly move on to the next thing or get everyone in agreement that we need some sort of UASF.

    it seems that a combination of MASF and UASF will be required for future deployments to properly address these adversarial scenarios.

    ST is exactly that. It’s a MASF which, if it fails, is almost certain to lead to a UASF. The insight here is that we don’t need to bundle a MASF and a UASF together; we can do them independently to allow us to focus on one piece of the puzzle at a time.

    It is unlikely BIP9 will be ever used again._

    If you’d like, I believe I can do some digging to find other developers who disagreed with Lombrozo at the time he said that. Moreover, it’s also worth noting that what’s proposed here is a modified version of BIP9—it has much the same relationship with BIP9 that BIP8 itself has with BIP9: an re-use of the same basic mechanism but tweaked for slightly different goals.

    1. Use of MTP:
    

    Although SE answer and few other posts mentioned few trade-offs, I am still not sure if using MTP has no risk in a soft fork activation mechanism.

    No change to consensus code is without risk. MTP is slightly more complex to think about than height, but IMO not to the degree height should automatically outweigh any advantage of MTP.

    I understand its a “Speedy” Trial and things need to be done in less time with less people who go the right skills and experience

    This PR has so far been reviewed in depth by most of the experts I would expect to review any other activation mechanism. I don’t think we’ve short circuited the review process; indeed, perhaps because of the controversy, I think we’ve all been more careful here than we might’ve been had there been no controversy at all.

    1. Its not just [@luke-jr](/bitcoin-bitcoin/contributor/luke-jr/) who has different opinion about the approach
    

    To my knowledge, the technical objectors to this approach are:

    1. @luke-jr, on the basis that height is better than MTP and that he believes there’s consensus to use BIP8. IMO, the differences between height and MTP aren’t significant enough to warrant a NACK and the previous widespread discussion about BIP8 doesn’t represent consensus any more than widespread discussion of a block size increase in early 2015 represented consensus to hard fork.

    2. @maaku, on the basis that using MTP encourages miners to manipulate their block times and that BIP8/heights are easier and safer to use for non-devs creating their own UASFs. IMO, significant time manipulation problems are extremely unlikely in the first place, super duper extremely unlikely for a ST, and are easier to mitigate than equivalent miner attempts to delay BIP8. Also IMO, with a little bit of documentation, we can make it just as easy and safe for non-devs to create UASFs using this PR and MTP as for using BIP8.

    3. @michaelfolkson who I think has only echoed the arguments above.

    Several other people have expressed a preference for heights over MTP, but I personally don’t think that’s a reason to prevent merging this PR. We will hopefully have many additional opportunities to activate feature-adding soft forks in the future, so we can iterate on the mechanism as we go—just as we’ve done from BIP30 flag day to BIP34 ISM to BIP9 to BIP148 to this PR. It’s better to do that work in parallel with waiting to see if miners use ST to lock in taproot than it is to delay taproot further while we argue over the subjective value of different people’s preferences.

    (Note: I didn’t see @gmaxwell’s reply to your comment until I was previewing my draft of this comment; sorry if there’s any redundancy.)

  210. in src/test/versionbits_tests.cpp:271 in ffe33dfbd4
    266@@ -226,6 +267,13 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
    267         // Make sure that no deployment tries to set an invalid bit.
    268         BOOST_CHECK_EQUAL(bitmask & ~(uint32_t)VERSIONBITS_TOP_MASK, bitmask);
    269 
    270+        // Check min_activation_height is on a retarget boundary
    271+        BOOST_CHECK_EQUAL(mainnetParams.vDeployments[i].min_activation_height % mainnetParams.nMinerConfirmationWindow, 0);
    


    fanquake commented at 1:49 am on April 15, 2021:

    This can be addressed in a followup:

    0warning: comparison of integers of different signs: 'const unsigned int' and 'const int' [-Wsign-compare]
    1...
    2test/versionbits_tests.cpp:271:9: note: in instantiation of function template specialization 'boost::test_tools::tt_detail::check_frwd<boost::test_tools::tt_detail::equal_impl_frwd, unsigned int, int>' requested here
    3        BOOST_CHECK_EQUAL(mainnetParams.vDeployments[i].min_activation_height % mainnetParams.nMinerConfirmationWindow, 0);
    

    MarcoFalke commented at 7:31 am on April 15, 2021:

    I can’t reproduce (#19123), but the warning seems correct:

     0#include <iostream>
     1#include <type_traits>
     2#include <cstdint>
     3 
     4int main()
     5{
     6  std::cout << std::boolalpha;
     7
     8  std::cout << std::is_same_v<int     , decltype(int{1} % unsigned{2})> << '\n'; // false
     9  std::cout << std::is_same_v<unsigned, decltype(int{1} % unsigned{2})> << '\n'; // true
    10}
    

    MarcoFalke commented at 7:58 am on April 15, 2021:
    Fixed in #21691
  211. fanquake merged this on Apr 15, 2021
  212. fanquake closed this on Apr 15, 2021

  213. fanquake removed this from the "Blockers" column in a project

  214. fanquake added the label Needs backport (0.21) on Apr 15, 2021
  215. sidhujag referenced this in commit acd1e9892c on Apr 15, 2021
  216. MarcoFalke referenced this in commit faac2e4100 on Apr 15, 2021
  217. hsjoberg commented at 10:04 am on April 15, 2021: contributor
    Concept ACK Code review ACK ffe33df
  218. Ziya-Sadr commented at 10:44 am on April 15, 2021: none
    wasn’t this supposed to be a BIP8 based ST?
  219. rodasmith commented at 10:57 am on April 15, 2021: contributor
    Concept NACK. BIP9 is a step backward from lessons learned during segwit activation. The user community that I am in wants BIP8 so we can UASF if miners don’t cooperate.
  220. viaj3ro commented at 11:18 am on April 15, 2021: none

    Concept NACK. BIP9 is a step backward from lessons learned during segwit activation. The user community that I am in wants BIP8 so we can UASF if miners don’t cooperate. @rodasmith Just NACKing this, without even bothering to read the discussion in this very thread is only going to hurt your reputation. Your argument doesn’t even make any sense since ST with MTP is absolutely compatible with a UASF.

    If you had read at least the last few comments, you’d know this. Please educate yourself before wasting precious developer time.

  221. harding commented at 11:46 am on April 15, 2021: contributor

    Post-merge tested ACK ffe33dfbd4c3b11e3475b022b6c1dd077613de79

    • Code reviewed diff since my previous review
    • Tested changes to getblockchaininfo and the tests since previous review; also performed additional testing on the earlier vbparams changes and ensured getblocktemplate returned reasonable-looking results for a regtest deployment
    • Performed extensive testing on the fuzz testing. Every problem I deliberately introduced that was reasonable to catch was caught within a minute of testing on a 48 core machine. Ran fuzz testing for about 200 CPU days total (yeah, yeah, I know, gmaxwell can do that in like an hour :-)
    • Spent some time staring at the state machine trying to think of every way it could go wrong by accident or by manipulation
    • Re-read every comment on the PR to date to ensure nothing important was missed; this is the only comment I’d personally like to see addressed in a follow up PR (no rush; it doesn’t need to be backported).

    My updated detailed review notes here: https://gist.github.com/harding/e622323eaf80d620826a7cb74ab3fb40

    My thanks to AJ, Andrew, and all the other test writers and reviewers. This seems to me to be extraordinarily well considered code.

  222. achow101 referenced this in commit 2e9e7f4329 on Apr 15, 2021
  223. achow101 referenced this in commit 1c0164544c on Apr 15, 2021
  224. achow101 referenced this in commit f9517e6014 on Apr 15, 2021
  225. achow101 referenced this in commit 4cab84cfdf on Apr 15, 2021
  226. achow101 referenced this in commit 0bcbc0b7f7 on Apr 15, 2021
  227. achow101 referenced this in commit 31862e0527 on Apr 15, 2021
  228. achow101 referenced this in commit e287d80d6d on Apr 15, 2021
  229. achow101 referenced this in commit 2bdcbd3ad4 on Apr 15, 2021
  230. achow101 referenced this in commit 563d3494e6 on Apr 15, 2021
  231. achow101 referenced this in commit 032ef333db on Apr 15, 2021
  232. achow101 referenced this in commit 46fb713137 on Apr 15, 2021
  233. achow101 referenced this in commit 43cfe4d52a on Apr 15, 2021
  234. achow101 referenced this in commit 74f8b2cca0 on Apr 15, 2021
  235. achow101 referenced this in commit c409ad7449 on Apr 15, 2021
  236. fanquake removed the label Needs backport (0.21) on Apr 16, 2021
  237. fanquake commented at 0:28 am on April 16, 2021: member
    Being backported in #21701.
  238. achow101 referenced this in commit 71917e01eb on Apr 16, 2021
  239. achow101 referenced this in commit b529222ad1 on Apr 16, 2021
  240. achow101 referenced this in commit 3acf0379e0 on Apr 16, 2021
  241. achow101 referenced this in commit 600357306e on Apr 16, 2021
  242. achow101 referenced this in commit ec7824396b on Apr 16, 2021
  243. jaybny commented at 3:47 am on April 16, 2021: none
    ACK - no need for BIP8 at this time
  244. MarcoFalke referenced this in commit e3b76b6c13 on Apr 16, 2021
  245. MarcoFalke referenced this in commit 6666213af6 on Apr 17, 2021
  246. MarcoFalke referenced this in commit fa772dc5f9 on Apr 17, 2021
  247. MarcoFalke referenced this in commit fad4167871 on Apr 17, 2021
  248. MarcoFalke referenced this in commit faec1e9ee1 on Apr 17, 2021
  249. fjahr commented at 6:21 pm on April 17, 2021: member

    Post merge Code Review ACK ffe33dfbd4c3b11e3475b022b6c1dd077613de79

    Also built and ran unit tests locally.

  250. in src/chainparams.cpp:92 in ffe33dfbd4
    92         consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
    93-        consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = 1199145601; // January 1, 2008
    94-        consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1230767999; // December 31, 2008
    95+        consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE;
    96+        consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
    97+        consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay
    


    Kixunil commented at 4:54 pm on April 18, 2021:
    Hmm? BIP341 says min_activation_height should be 709632 but this code contains 0.

    harding commented at 5:15 pm on April 18, 2021:
    @kixunil The activation parameters were add in a separate PR, #21686
  251. Kixunil commented at 4:55 pm on April 18, 2021: none
    Not reviewed, just confused, sorry for the noise.
  252. windsok referenced this in commit a2dddfde30 on Apr 23, 2021
  253. windsok referenced this in commit 3d5212538f on Apr 23, 2021
  254. DrahtBot locked this on Oct 30, 2022

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-12-26 21:12 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me