-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chaincfg+blockchain: abstract/refactor BIP 9 version bits implementation to work w/ BIP 8 block heights #1700
Conversation
The alternative to this PR is to just go with the old: "lower than this value is a block height" approach which is used for lock time related logic elsewhere. However, I figured it's better to just further abstract things and hide more of the implementation details from the call sits within the |
blockchain/versionbits.go
Outdated
|
||
// Can't fail as we make sure to set the clock above. | ||
header := blkNode.Header() | ||
started, _ := deploymentStarter.HasStarted(&header) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since deploymentStarter
is an interface, and you don't know how the the actual implementation looks like, wouldn't if be better to check for the error?
So for this to potentially be "taproot ready", assuming this "speed trial" thing happens, then we'll need to also add a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really like this change, even though Taproot didn't activate through BIP 8, it paves the way forward for any future soft forks that do.
As you mentioned, this PR is still missing the state transition changes as part of Speedy Trial.
Pull Request Test Coverage Report for Build 1748228076
💛 - Coveralls |
ed86f68
to
a50756b
Compare
Initial comments addressed, next commit with add the possibility for a custom threshold per deployment and also the min activation height. Will then add a new dummy deployment to test against that has both of these new values specified. |
Ok, this should be good to go now. Reviews should ensure they've internalized the commit body in this commit: c1842e2 |
From c1842e2:
Just to make sure I understand what you're saying here. Are you referring to the transition from BIP 341 states: case LOCKED_IN:
if (block.nHeight < min_activation_height) {
return LOCKED_IN;
}
return ACTIVE; The returned value ( So the current block is Bitcoin Core does the following: case ThresholdState::LOCKED_IN: {
// Progresses into ACTIVE provided activation height will have been reached.
if (pindexPrev->nHeight + 1 >= min_activation_height) {
stateNext = ThresholdState::ACTIVE; This is part of the function: /** Returns the state for pindex A based on parent pindexPrev B. Applies any state transition if conditions are present.
* Caches state from first block of period. */
ThresholdState GetStateFor(const CBlockIndex* pindexPrev, When this function is called with the Tip block, it returns the state for the next (unmined!) block. So So I don't think there's an off by one error in the BIP, but both the BIP and the Bitcoin Core implementation are confusing enough to be likely to cause off-by-one errors (and in fact they did with SegWit2x).
|
Decided ultimately to special case things a bit just so it matches 1:1, once this base PR is in, I want to follow up with some additional tests for the hybrid state machine. |
Hey @Sjors, yeh I explained this on #btcd IRC. I was just initially tripped up by the fact that the BIP and the bitcoind implementation don't match up (one uses the current block (to be mined/accepted) while the other uses the previous. Our BIP 9 implementation always uses the previous block. |
Implemented the BIP 9 modifications, and added a new unit test here: 75d3489. This PR should be g2g now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have limited understanding of activation mechanisms in general, but from what I see in this diff the changes make sense to me. Always nice to see how a well-defined interface can make things very straightforward to understand!
In this commit, we create a series of new interfaces that'll allow us to abstract "when" exactly a deployment starts and ends. As is, all deployments start/end based on a unix timestamp, which is compared against the MTP of a given block to determine if a new deployment has started or ended. This works fine for BIP 9 which uses time based timeouts, but not so much for BIP 8. In order to prep a future refactor that allows our version bits implementation to support both time and block based start/end times, this new abstraction has been introduced.
…times In this commit, we utilize the recently added ConsensusDeploymentStarter and ConsensusDeploymentEnder interfaces. Concrete implementations of this interface based on the median time past comparison are used now in the ConsensusDeployment struct instead of hard coded start/end times. Along the way, we had to switch to using the "zero time": time.Time{}, in place of 0 and math.MaxInt64 as comparison (After/Before) seems to be broken in the Go stdlib for times very far in the future. It appears Go isn't ready to handle the heat death of the universe.
…deployments In this commit, we update our version bits logic to use the newly added HasStarted and HasEnded methods for consensus deployments. Along the way, wee modify the thresholdConditionChecker` interface to be based off the new chaincfg interfaces. In addition, we add a new method `PastMedianTime`, in order to allow the chain itself to be used as a `chaincfg.BlockClock`. This serves to make the logic more generic in order to support both block height and time based soft fork timeouts.
In this commit, we add a new "dummy" deployment that adds the new params used to activate taproot. We chose to add a new deployment as unlike the bitcoind codebase, we don't currently "bury" soft forks that have happened in the past (hard code an activation height). The old taproot deployment has been removed as with the way the array works, a deployment needs to be defined for _all_ networks.
…tom thresholds In this commit, we extend the existing version bits state machine to add support for the new minimum activation height and custom block threshold for activation. We then extend the existing BIP 9 tests (tho this isn't really BIP 9 anymore...) to exercise the new min activation height logic.
In this commit, we extract the BIP 9 state transition logic from the thresholdState method into a new thresholdStateTransition function that allows us to test all the defined state transitions, including the modified "speedy trial" logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 🎉
Post-merge ACK 👍 |
In this PR, we further abstract our Version Bits implementation to be able to support both BIP 9 time based timeouts, as well as BIP 8 block based timeouts. In order to bridge the two mechanisms, we introduces a series of new light interfaces that allow us to abstract away from when exactly a soft fork deployment "starts" and "ends". This PR doesn't yet add any new BIP 8 compatible deployments, as it's mean to be a pure refactoring that doesn't introduce any new behavior. It's also the case that BIP 8 has some extra Version Bits states that need to be added to our Version Bits implementation.
The only thing missing from this PR is a few additional tests to confirm that no change in behavior has occurred. Reviewers should examine the change to how we define deployments that are "always started" and "never ended", as well as the usage of
time
methods to determine if a deployment has started or ended.