Sturm und drang und taproot activation

The Shipwreck – Joseph Mallord William Turner 1775-1851

Back at the end of 2019, I said on Stephan Livera’s podcast that activation of taproot is “something a lot of people in the community have very strong opinions of; so it’s probably going to be a Twitter flamefest or whatever about it.” It’s turned out both better and worse than I expected — better in that we got decent agreement on an activation method, merged it into core, and so far appear to be getting uptake by miner more rapidly than I was expecting; worse in that the the “UASF” side of the debate seems to have gone weirdly off the rails.

(I’ve written in the past about activating soft forks in Bitcoin so I’ll just leave that link there if you want some general context for what the heck I’m talking about and otherwise dive right in)

Speedy Trial

The activation method included in the Bitcoin Core 0.21.1 is called “Speedy Trial” and it’s implemented as a variant of BIP 9 (which was used for activating segwit and CSV/relative timelocks) modified in a few ways:

  • rather than having signalling not start for a month after the release of the new version, signalling was scheduled for just a few weeks after the merge of the activation parameters, and ended up starting on the same day the software was actually released
  • rather than having signalling continue for a year, it only continues for a bit over three months (ending sometime after August 11)
  • rather than having activation occur two weeks after lock in occurs, it is delayed until block 709632 (expected around mid November)
  • rather than requring 95% of blocks to signal to lock in activation, only 90% of blocks are required to signal

The main idea is that we don’t have any reason to expect problems with taproot activation, so let’s just do something quickly: if we’re right, and there are no problems, the quick approach will work fine, and if we’re wrong and there are problems then we can do something else.

Shortening the timeframe (starting and ending signalling sooner than BIP 9’s recommendations) means that we can move on to dealing with problems much more quickly and delaying activation helps ensure that there’s still time for users to upgrade and ensure miners play fair, despite signalling starting so quickly. Finally the reduced threshold recognises that the BIP 9 mechanism doesn’t need as high a threshold as the old IsSuperMajority approach, so lowers it somewhat while remaining fairly conservative.

The broader rationale behind this approach was documented by Matt in his Modern Soft Fork Activation email early last year (point 4): adoption by the vast majority of hashpower reduces the risk to the network of activation while the rest of the network upgrades — users who don’t upgrade will follow the chain with the new rules because any chain that doesn’t follow the new rules will quickly become much shorter (the 90% figure means transactions in an chain invalid under the new rules only have ~1% chance of getting three confirms before being reorged to a chain that’s valid under the new rules).

That strategy fits well with voluntary signalling by miners: if miners upgrade quickly, it’s safe to activate the new rules fairly quickly. If they don’t upgrade quickly, well then we need to wait for users to upgrade to have a UASF-style activation — but if users have all upgraded, it doesn’t much matter what miners do: if they mine blocks invalid under the new rules, they’ll just be ignored, the same as BCH or BSV blocks are ignored by Bitcoin nodes. So “Speedy Trial” just deals with the easy case — let’s see if things will work well, and get it over with if it does. If it doesn’t work well, it’ll all be over quickly, and we can move on to an activation method that doesn’t rely on miners, knowing that it’s needed.


While most people were happy to release Bitcoin Core with the Speedy Trial method, that’s not true of everyone, and a few people are instead encouraging use of an alternative implementation, forked from the Bitcoin Core codebase, and using a different activation methodology, that requires taproot activation by signalling by a particular block height that is expected to arrive around November 2022.

I don’t recommend running that client under any circumstances.

The simplest reason is that it’s poorly maintained: for example, the change to set the activation parameters is PR#9, which was merged without any (public) review comments, about nine hours after it was filed, and about 29 hours before the meeting that was going to make the decision on those parameters. It also has a red-cross “failed CI tests” marker, mainly because various software-as-a-service CI systems have limits on how many jobs they’ll run for free, and in order to run all the CI tests for bitcoin, you either have to pay to get them run quickly, or you have to wait for a long time. Having been forked from Bitcoin Core 0.21.0, none of the backports targeted to Bitcoin Core 0.21.1 have been merged, such as #20901, #21469, or #21640 — the lack of #21469 in particular means the UASF client won’t correctly parse bech32m addresses when attempting to pay to taproot addresses following BIP 350, for instance, rendering it incompatible with any taproot wallets following the recommended address format. Are these serious bugs that will lose you money today? No, probably not. But is this the best software to secure your BTC so you won’t lose money tomorrow? Also, no.

Beyond poor quality, it’s also marketed deceptively — eg, announcing it as “the Bitcoin Core 0.21.0 build w/ Taproot added in” and naming it “Bitcoin Core version 0.21.0-based Taproot Client 0.1” rather than making it clear that it’s an entirely separate group of people working on it to the Bitcoin Core developers. Perhaps if you look carefully at their site, after skipping past the big, bold “Bitcoin Core” heading in the middle of your screen when you first load it up, you might see the “Who maintains the Taproot Client software?” and click through to see “Bitcoin Mechanic, Shinobi and stortz”, though you’ll likely have no way of figuring out who those people are.

“lot=false to lot=true”

It was (in my opinion) always likely we’d end up with one activation method in Bitcoin Core and a different UASF client published by Luke and others — when I did the survey of some devs two-thirds didn’t want to go straight to a “flag day” style activation, but the remaining one-third did, and the BIP 148 experience already demonstrated that releasing a forked client was a realistic gambit if things weren’t going your way.

The original pseudonymous author of BIP 8 approved Luke’s patch adding himself as a co-author of that document in June last year, and in the same patch set, Luke introduced the lockinontimeout parameter, which seemed (to me at least) like a way that the same codebase could satisfy both goals. So for the eight or so months following that, I spent a bunch of time (see #950, #1019, #1020, #1021, #1023, #1063) trying to refine that so it would work as smoothly as possible, even if miners or others were deliberately trying to game the system.

The advantage of that approach over where we are today is that when a few opinionated people decide that a UASF is the only reasonable approach it would be much easier to maintain a high quality fork of Bitcoin Core that has that feature — you’re only changing a single “false” to “true” and otherwise just updating documentation and the name; so there’s no particular difficulty in porting over other patches. (In contrast, when you’re using an entirely different mechanism, you have to touch code in lots of different places, and each of those has a chance of conflicting with any other patches you might also want to include)

By mid February it was looking (to me at least) pretty much like that was how things would play out, and we would merge BIP 8 into core with taproot set as lot=false, but with the code ready for a switch to lot=true. There was still work to be done to make lot=true safe in the adversarial conditions where it might have any value, but it seemed plausible that we could make progress on that over the next few months — pulling in some of the fixes that were already done for the BIP 148 code in 2017, and adding improvements on top of that.

But that was about the point any chance of consensus collapsed: the suggestions of “core should just release both clients and let the people choose” and the consensus risks that implies (which is complicated enough it’d take a whole article in itself to cover) concerned Suhas seriously enough to setup a blog and concerned Matt enough to go back to square one on activation methods. Meanwhile on the other side, Luke declared “LOT=False is dangerous and shouldn’t be used”.

The “UASF (LOT=true) kick off meeting” was then announced as happening a couple of days later (though work on the UASF website had already begun a week earlier, prior to “LOT=false is dangerous” post), which ended up including some gloating about the confusion, along with promises to make it hard to come to consensus (“<luke-jr> personally I plan to NACK any LOT=False; but I doubt I would need to at this point (devs pushing against LOT=True seem to be off shed-painting other bad ideas now)“).

Perhaps someone with more ranks in the Diplomacy skill could’ve done better and we could have stayed on that track, but, at least for me, those were pretty clear “Dead End” signs.

Speedy Trial was proposed a few days later, providing a new track. It took under six hours to get a first draft PR implementing that proposal up, but then an additional 57 days to actually get it included in a release. Some of that was due to problems with the original draft, some was due to improving test coverage, some was due to the regular release candidate process, and some was certainly due to an unexpected certificate revocation, but a lot of time was effectively wasted: eg, there was a port of Speedy Trial on top of the BIP 8 patches proposed a few days after the initial PR above, effectively doubling the review load and splitting the development effort for most of that time, and then there were the promised NACKs, along with long delays with getting BIP updates merged.

I say “effectively wasted”, but perhaps that’s not fair: exploring alternative ways of writing code helps you understand what’s going on, even if you throw it away (certainly, I learned something from it: notably that height based signalling isn’t compatible with testnet behaviour). Given the sudden failure of the previous lot=false approach, I don’t think there was ever any realistic hope of achieving a better degree of consensus, but of course, I could be wrong, and some things are worth trying even when it seems hopeless. So, obviously, draw your own conclusions on whether the time spent there was worthwhile or not.

Speedy Trial vs UASF

I think there’s fundamentally three reasons why some people are still sticking to the UASF approach rather than being happy with the Speedy Trial approach — whether in practice, despite the poor implementation, or more in principle, eg by suggesting that Bitcoin Core should be deploying some sort of a UASF backstop now, rather than solely doing Speedy Trial.

I think the simplest of these reasons is something along the lines of “BIP 8 was proposed in 2017, why go to all this hassle instead of just doing it?” (eg adam3us or michaelfolkson) or more assertively something like “We already agreed to do BIP 8, why are you violating community consensus?” (eg luke-jr or MarkFriedenbach) The problem with that is that the BIP 8 we have today is not the BIP 8 that was proposed in 2017 or even the one we had in January this year — over time it’s had the lot=true/false parameter added, had a compulsory signalling phase added, had numerous tweaks to the state behaviour added, and most recently had a lock-in delay added. It’s never been used outside of the regression test environment, and bugs were being found and fixed as recently as February and throughout March. That’s not unexpected — what we had in 2017 was an idea, but as with most ideas, things get more complicated when you try to actually make them a reality. And sometimes it turns out that your original idea wasn’t so great in the first place.

Another reason is something along the lines of “If we think a UASF might be necessary in a few months in the event Speedy Trial doesn’t hit 90%, why not do it now?” There’s two answers to that: the first is that the approach that we were working towards had collapsed, and it would likely take months to get agreement on an alternative one — by which time Speedy Trial would have finished anyway, whether it succeeds or fails. (That it took two months to even get Speedy Trial out suggests that might even be an underestimate). So why not get started with the easy part while we rethink the hard part? The second is that we’re likely to learn things from Speedy Trial and that can inform our decisions on how to deploy the UASF. From my perspective we’ve already learnt some things:

  • miners/pools didn’t start signalling prior to the activation logic reaching the STARTED state — that probably means there’s less “false” signalling than some us feared/expected
  • pools have been upgrading to signal fairly quickly, adding credence to their prior statements as recorded on
  • poolin have reported that some ASIC firmware doesn’t support signalling via version bits — maybe that’s an easy fix, or maybe we should move signalling to a different mechanism; as a result they’ve only enabled signalling for some of their servers, and maybe 1THash has done the same
  • maybe we should be expecting teething problems like this and in the future encourage signalling in advance of it actually mattering

An obvious thing we’ll learn if Speedy Trial fails is that we can’t reach 90% of miners signalling in three months — if we discover that’s for practical reasons (like the issue poolin described), then making signalling mandatory is probably a bad idea if a significant amount of hashrate can’t actually do it — so perhaps lowering the threshold further would be a good idea, or changing the way we signal to something that more mining hardware is compatible with would be worthwhile, or perhaps changing the approach entirely might be a better bet. We’ll also likely learn how enthusiastic businesses and node operators are to upgrade to support taproot — the faster everyone does that, the less time we need to wait before triggering a flag day. But there’s a chicken and egg problem — you can’t pick a flag day without knowing how fast people will upgrade, people can’t upgrade until there’s a client, you can’t release a flag day/UASF client until you pick a date for the flag day, you can’t pick a flag day without knowing how fast people will upgrade… rinse, repeat. So being able to release a client that doesn’t set a flag day lets you break that cycle and get a somewhat informed answer. And beyond all that, perhaps there are unknown unknowns that we’ll find out about.

The third reason for advocating for a UASF now, in my opinion, is just that a bunch of people enjoyed the BIP 148/no2x drama and want to play it out again in much the same. Looked at from the right viewpoint it was a really straightforward heroic saga: a small band of misfits get together to fight the big bad, against the advice of the establishment, build up a popular movement, and win the battle without a shot being fired. Total feel-good Hollywood blockbuster plot line.

You can see little demonstrations of this sentiment every now and then, eg when the BIP 148 big bad started signalling, adam3us’s reponse was “don’t thank them too much – last time they were among the ring leaders in tactical veto games. 148 lurking. never forget.” or zndtoshi’s take “Dude I wish that ST would fail so that miners get it that we can enforce via uasf. But I have a feeling they did learn from the segwit saga and will just signal now.”

And I mean, that’s fine — if you’ve got an empowering narrative for why you’re doing what you’re doing, good for you! But it becomes a problem if remembering the past blinds you to the present and your plotline doesn’t actually match reality — just because you won the last battle with a cavalry charge, doesn’t mean it’s necessarily a good idea for this battle, and just because it was fun fighting an enemy in the past doesn’t mean it’s a smart idea to find more enemies now.

Anyway, that’s my collection of thoughts on where we’re at. No particular conclusion from them — I guess I have a whole other set of thoughts on what to do next — but I wanted to get these written down somewhere before moving on.

Leave a Reply