Copies of all published parts of our weekly series on preparing for taproot activation at block 709,632.

  1. Bech32m sending support
  2. Is taproot even worth it for single-sig?
  3. Taproot descriptors
  4. From P2WPKH to single-sig P2TR
  5. Why are we waiting?
  6. Learn taproot by using it

Bech32m sending support

Originally published in Newsletter #154

Starting at block 709,632, expected in November, Bitcoin users will be able to safely receive payments to taproot addresses. Given the user enthusiasm for taproot and the five months that wallet developers have to implement support for it, Optech expects there to be several popular wallets that will allow their users to generate taproot addresses at the earliest possible moment.

That means any other wallet or service that sends bitcoins to user-provided addresses needs to be able to send to taproot addresses by block 709,632 or risk confusing and disappointing its users. Pay to TapRoot (P2TR) addresses use bech32m as specified in BIP350, which is slightly different than BIP173’s bech32 algorithm used for segwit v0 P2WPKH and P2WSH addresses. Bech32m uses the constant 0x2bc830a3 instead of bech32’s 0x01 in the checksum function.

Changing that single constant provides the ability to verify bech32m checksums, but the code still needs to use the original constant for existing P2WPKH and P2WSH addresses. The code needs to decode the address without verifying the checksum, determine whether it uses v0 segwit (bech32) or v1+ segwit (bech32m), and then validate the checksum with the appropriate constant. For examples, see the PR that updated the bech32 reference implementations for C, C++, JS, and Python. If the code already uses the reference libraries, they can be updated to the latest code from that repository, although note that some of the APIs have slight changes. BIP350 and the reference implementations provide test vectors that all bech32m implementations should use.

Although receiving payments to taproot addresses won’t be safe until block 709,632, sending payments should not cause any problems for the sender. Bitcoin Core has supported relaying and mining transactions with taproot-paying outputs since version 0.19 (released November 2019). Optech encourages developers of wallets and services to implement support for paying bech32m taproot addresses now rather than waiting until after taproot activates.

Is taproot even worth it for single-sig?

Originally published in Newsletter #155

Using Optech’s transaction size calculator, we can compare the sizes of different types of single-sig transactions. As expected, transactions using P2WPKH inputs and outputs are much smaller than those using P2PKH inputs and outputs—but, perhaps surprisingly, P2TR transactions are slightly larger than equivalent P2WPKH transactions.

  P2PKH (legacy) P2WPKH (segwit v0) P2TR (taproot/segwit v1)
Output 34 31 43
Input 148 68 57.5
2-in, 2-out tx 374 208.5 211.5

That may make it seem counterproductive for single-sig wallets to implement taproot spending in preparation for block 709,632, but a closer look reveals that there are a number of advantages to using P2TR for single-sigs, both for wallet users and for the network as a whole.

  • Cheaper to spend: it costs about 15% less at the input level to spend a single-sig P2TR UTXO than it does to spend a P2WPKH UTXO. An overly simple analysis like the table above hides the detail that the spender can’t choose which addresses they’re asked to pay, so if you stay on P2WPKH and everyone else upgrades to P2TR, the actual typical size of your 2-in-2-out transactions will be 232.5 vbytes—while all-P2TR transactions will still only be 211.5 vbytes.

  • Privacy: although some privacy is lost when early adopters change to a new script format, users switching to taproot also immediately receive a privacy boost. Your transactions will be able to look indistinguishable from people working on new LN channels, more efficient DLCs, secure multisignatures, various clever wallet backup recovery schemes, or a hundred other pioneering developments.

    Using P2TR for single-sig now also allows your wallet to upgrade to multisignatures, tapscripts, LN support, or other features later on without affecting the privacy of your existing users. It won’t matter whether a UTXO was received to an old version or a new version of your software—both UTXOs will look the same onchain.

  • More convenient for hardware signing devices: since the rediscovery of the fee overpayment attack, several hardware signing devices have refused to sign a transaction unless each UTXO spent in that transaction is accompanied by metadata containing a copy of significant parts of the entire transaction which created that UTXO. This greatly increases the worst-case processing that hardware signers need to perform and is especially problematic for hardware signers using limited-size QR codes as their primary communication medium. Taproot eliminates the vulnerability underlying the fee overpayment attack and so can significantly improve the performance of hardware signers.

  • More predictable feerates: ECDSA signatures for P2PKH and P2WPKH UTXOs can vary in size (see Newsletter #3). Since wallets need to choose a transaction’s feerate before creating the signature, most wallets just assume the worst case signature size and accept that they’ll slightly overpay the feerate when a smaller signature is generated. For P2TR, the exact size of the signature is known in advance, allowing the wallet to reliably choose a precise feerate.

  • Help full nodes: the overall security of the Bitcoin system depends on a significant percentage of Bitcoin users verifying every confirmed transaction with their own nodes. That includes verifying the transactions your wallet creates. Taproot’s schnorr signatures can be efficiently batch verified, reducing by about 1/2 the number of CPU cycles nodes need to expend when verifying signatures during the process of catching up on previous blocks. Even if you rejected every other point on this list, consider preparing to use taproot for the benefit of people running full nodes.

Taproot descriptors

Originally published in Newsletter #156

Output script descriptors provide a generic way for wallets to store the information needed to create addresses, efficiently scan for outputs paying those addresses, and later spend from those addresses. In addition, descriptors are reasonably compact and contain a basic checksum, making them convenient for backing up address information, copying it between different wallets, or sharing it between wallets that are cooperating to provide multiple signatures.

Although descriptors are currently only used by a few projects, they and the related miniscript project have the potential to significantly improve interoperability between different wallets and tools. This will become increasingly important as more users take advantage of the benefits of taproot to improve their security through multisignatures and their resiliency through backup spending conditions.

Before that can happen, descriptors need to be updated to work with taproot. That was the subject of the recently merged Bitcoin Core #22051 pull request. The syntax has been designed to allow a single descriptor template to provide all the information necessary to use both P2TR keypath spends and script path spends. For simple single-sig, the following descriptor is sufficient:

tr(<key>)

The same syntax may also be used for multisignatures and threshold signatures. For example, Alice, Bob, and Carol aggregate their keys using MuSig and then pay to tr(<combined_key>).

Unintuitively, the key specified by tr(<key>) won’t be the key encoded in the address. The tr() descriptor follows a safety recommendation from BIP341 to use an internal key that commits to an unspendable script tree. This eliminates an attack against users of naive key aggregation schemes (more advanced schemes such as MuSig and MuSig2 are not affected).

For scriptpath spending, a new syntax is added that allows specifying the contents of a binary tree. For example, { {B,C} , {D,E} } specifies the following tree:

Internal key
    / \
   /   \
  / \ / \
  B C D E

A tree can be specified as an optional second parameter to the descriptor template we used before. For example if Alice wants to be able to spend via keypath, but she also wants to allow Bob, Carol, Dan, and Edmond spend via a scriptpath that generates an audit trail for her (but not for third-party chain surveillance), Alice can use the following descriptor:

tr( <a_key> , { {pk(<b_key>),pk(<c_key>)} , {pk(<d_key>),pk(<e_key>)} )

The above features are all that’s required to use descriptors for taproot, but PR #22051 notes that there are still some things missing that could be added to make descriptors better at completely describing expected policies:

  • Voided keypaths: some users may want to prevent usage of keypath spending in order to force scriptpath spending. That can be done now by using an unspendable key as the first parameter to tr(), but it’d be nice to allow wallets to store this preference in the descriptor itself and have it compute a privacy-preserving unspendable keypath.

  • Tapscript multisig: for legacy and v0 segwit, the multi() and sortedmulti() descriptors support the OP_CHECKMULTISIG opcode. To allow batch verification in taproot, script-based multisig is handled a bit differently in tapscript, so tr() descriptors currently need to specify any necessary multisig opcodes via a raw() script. Updated versions of multi() and sortedmulti() for tapscripts would be nice to have.

  • MuSig-based multisignatures: earlier in this article, we described Alice, Bob, and Carol manually aggregating their keys in order to use a tr() descriptor. Ideally, there would be a function that allows them to specify something like tr(musig(<a_key>, <b_key>, <c_key>)) so that they could retain all the original key information and use it to populate fields in the PSBTs they use to coordinate signing.

  • Timelocks, hashlocks, and pointlocks: these powerful constructions used in LN, DLCs, coinswaps, and many other protocols can currently only be described via the raw() function. Adding support for them directly to descriptors is possible, but it may be the case that support will be added instead through descriptor’s sibling project, miniscript. Integration of miniscript into Bitcoin Core is still an ongoing project, but we expect its innovations to spread to other wallets in the same way that tools like PSBTs and descriptors already have.

Wallets don’t need to implement descriptors in order to start using taproot, but those that do will give themselves a better foundation for using more advanced taproot features later.

From P2WPKH to single-sig P2TR

Originally published in Newsletter #157

For wallets that already support receiving and spending v0 segwit P2WPKH outputs, upgrading to v1 segwit P2TR for single-sig should be easy. Here are the main steps:

  • Use a new BIP32 key derivation path: you don’t need to change your BIP32 Hierarchical Deterministic (HD) code and your users don’t need to change their seeds.1 However, you are strongly encouraged to use a new derivation path for your P2TR public keys (such as defined by BIP86); if you don’t do this, there’s a possible attack that can occur if you use the same keys with both ECDSA and schnorr signatures.

  • Tweak your public key by its hash: although technically not required for single-sig, especially when all your keys are derived from a randomly-chosen BIP32 seed, BIP341 recommends having your key commit to an unspendable scripthash tree. This is as simple as using an Elliptic Curve addition operation that sums your public key with the curve point of that key’s hash. Advantages of complying with this recommendation are that you’ll be able to use the same code if you later add scriptless multisignature support or if you add support for tr() descriptors.

  • Create your addresses and monitor for them: use bech32m to create your addresses. Payments will be sent to the scriptPubKey OP_1 <tweaked_pubkey>. You can scan for transactions paying the script using whatever method you use to scan for v0 segwit addresses like P2WPKH.

  • Creating a spending transaction: all the non-witness fields for taproot are the same as for P2WPKH, so you don’t need to worry about changes to the transaction serialization.

  • Create a signature message: this is a commitment to the data from the spending transaction. Most of the data is the same as what you sign for a P2WPKH transaction, but the order of the fields is changed and a few extra things are signed. Implementing this is just a matter of serializing and hashing various data, so writing the code should be easy.

  • Sign a hash of the signature message: there are different ways to create schnorr signatures. The best is not to “roll your own crypto” but instead to use the function from a well-reviewed library you trust. But if you can’t do that for some reason, BIP340 provides an algorithm that should be straightforward to implement if you already have available the primitives for making ECDSA signatures. When you have your signature, put it in the witness data for your input and send your spending transaction.

Even before taproot activates at block 709,632, you can test your code using testnet, the public default signet, or Bitcoin Core’s private regtest mode. If you add taproot support to your open source wallet, we encourage you to add a link to the PR(s) implementing it on the taproot uses and bech32m adoption pages of the Bitcoin Wiki so other developers can learn from your code.

Why are we waiting?

Originally published in Newsletter #158

Earlier entries in this series saw us encouraging developers working on wallets and services to begin implementing taproot upgrades now so that they’re ready when taproot activates. But we’ve also warned against generating any addresses for P2TR before block 709,632 as this could cause your service or your users to lose money.

The reason not to generate addresses in advance is that any payment to a P2TR-style output can be spent by anyone prior to block 709,632. The money would be completely unsecured. But starting with that block, thousands of full nodes will begin enforcing the rules of BIP341 and BIP342 (and, by association, BIP340).

If it was guaranteed that there wouldn’t be a reorganization of the block chain, it would be safe to start generating addresses for P2TR as soon as the final pre-taproot block was seen (block 709,631). But there’s reason to be concerned about block chain reorgs—not just accidental reorgs but also those deliberately created to take money from early P2TR payments.

Imagine a large number of people all wanting to be one of the first to receive a P2TR payment. They naively send themselves some money as soon as they see block 709,631.2 Those payments will be secure in block 709,632, but they can be stolen by any miner who creates an alternative to block 709,631. If the value of the money sent to P2TR outputs is large enough, it could easily become more profitable to attempt to mine two blocks instead of just one (see our fee sniping topic for more details).

For this reason, we don’t recommend your software or service generate addresses for P2TR until you think the reorg risk has been effectively eliminated. We think waiting 144 blocks (approximately one day) after activation is a reasonably conservative margin that minimizes risk without significantly delaying you or your users from taking advantage of the benefits of taproot.

In short:

  • 709,631: last block where anyone can spend money sent to a P2TR-style output
  • 709,632: first block where P2TR outputs can only be spent if they satisfy the BIP341 and BIP342 rules.
  • 709,776: a reasonable block at which wallets can start giving their users bech32m receiving addresses for P2TR outputs

None of the above changes the advice given in the first part of this series to enable paying to bech32m addresses as soon as possible. If someone requests payment to an address for P2TR before you think it’s safe, that’s their risk to take.

Learn taproot by using it

Originally published in Newsletter #159

Almost two years ago, James Chiang and Elichai Turkel produced an open source repository of Jupyter notebooks for a series of Optech workshops to train developers on taproot technology. Workshops held in San Francisco, New York City, and London received positive reviews, but travel restrictions prevented subsequent in-person workshops.

Since the publication of the Jupyter notebooks, taproot underwent several changes. However, taproot support was also merged into Bitcoin Core, allowing the notebooks to drop their dependency on a custom branch of Bitcoin Core. Developer Elle Mouton has kindly updated the notebooks for all those changes, making them again a great way to quickly build hands-on experience working with taproot’s algorithms and data types.

The notebooks are divided into four sections:

  • Section 0 contains a notebook that helps you set up your environment, covers the basics of elliptic curve cryptography, and teaches you about the tagged hashes used throughout BIPs 340, 341, and 342.

  • Section 1 walks you through creating schnorr signatures. Once you’ve mastered them, you learn how to create multisignatures with the MuSig protocol.

  • Section 2 gives you experience with every aspect of taproot. It starts with a review of the principles of segwit v0 transactions and then helps you create and send segwit v1 (taproot) transactions. Applying the knowledge from section 1, you then create and spend a taproot output using MuSig. The concept of key tweaking is introduced and you learn how taproot allows you to use its public key to commit to data. Now that you can create commitments, you learn about tapscripts—how they differ from legacy and segwit v0 script, and how to commit to a tree of tapscripts. Finally, a short notebook introduces huffman encoding for creating optimal script trees.

  • Section 3 provides an optional exercise in creating a taproot output that changes which signatures are required the longer the output goes unspent—allowing the output to be efficiently spent under normal circumstances but also providing for a robust backup in case of problems.

The notebooks include numerous programming exercises that are relatively easy but which will ensure that you actually learned the material presented. The author of this column, who is no great coder, was able to complete the notebooks in six hours and only regretted that he had not taken the time to learn from them earlier.

Footnotes

  1. When Electrum upgraded to segwit v0, it required anyone who wanted to receive to bech32 addresses generate new seeds. This was not technically required but it allowed the authors of Electrum to introduce some new features into their custom seed derivation method. One of those features was ability for a seed version number to specify which scripts a seed is meant to be used with. This allows safe deprecation of old scripts (e.g., a future a version of Electrum may be released that no longer supports receiving to legacy P2PKH addresses).

    Around the same time the Electrum developers were deploying their versioned seeds, Bitcoin Core developers began using output script descriptors to solve the same problem of allowing script deprecation (in addition to solving other problems). The following table compares Electrum’s versioned seeds and Bitcoin Core’s descriptors to the implicit scripts method previously used by both wallets and still in common use among most other wallets.

    Script management Initial backup Introducing new scripts Scanning (bandwidth/CPU cost) Deprecating scripts
    Implicit scripts (e.g. BIP44) Seed words Automatic (no user action required) Must scan for all supported scripts, O(n) No way to warn users that they're using unsupported scripts
    Explicit scripts (versioned seeds) Seed words (includes version bits) User must backup new seed; funds are either partitioned into two separate wallets or user must send funds from the old wallet to the new Only scans for a single script template, O(1) Users warned about unsupported scripts
    Explicit scripts (descriptors) Seed words and descriptor User must back up the new descriptor Only scans for the script templates that were actually used, O(n); n=1 for a new wallet Users warned about unsupported scripts

  2. Users who want to receive a P2TR payment in the first taproot block should generate an address they don’t share with anyone and then create a transaction to that address with nLockTime set to 709,631. That transaction can be broadcast at as soon as block 709,631 has been received. The nLockTime will ensure the transaction can’t be included into any block before 709,632, where taproot rules are enforced. Messing about with new script types and custom locktimes can be dangerous if you don’t know what you’re doing, so please take care.