Taproot Demonstration

Abstract: In this piece we take a second look at Taproot. We explain the mechanics of Taproot by walking through two example Taproot transactions on the Bitcoin mainnet, including spending Bitcoin sent to the same address in two different ways: i. Using the Schnorr signature and ii. Using the Tapleaf and the so-called un-tweaked public key.


Taproot is a Bitcoin protocol upgrade (via a softfork), which we first explained in May 2019. Taproot finally activated on Bitcoin on 14th November 2021, at block height 709,632. Since activation, adoption of Taproot has been minimal. According to data from TXStats.com, a joint BitMEX Research and CoinMetrics website, only around 0.05% of Bitcoin is stored in Taproot outputs. Despite the low levels of adoption in absolute terms, usage has been growing steadily. The characteristic of gradual adoption is not too dissimilar to previous softfork upgrades (like SegWit) and is what many in the development community hope to achieve with new features; slow, smooth, gradual and non-disruptive adoption.

Source: TXStats.com

Taproot Usage Example

We decided to attempt to use Taproot in the latest version of Bitcoin Core (23.0). We didn’t only want to use Taproot in the most basic way (using a Schnorr signature), but we also wanted to demonstrate the main novel functionality, redeeming Bitcoin using the Tapleaf method. Conducting this process was more difficult than we expected and took several hours, however, we managed it in the end. A step by step guide of the process is provided below.

Generating a Taproot Address & Taproot Spending Conditions

The first step involves generating a new wallet, which can be done using the GUI in Bitcoin Core. Then we went to the console, where one can type RPC commands. We ran the following:


This then displayed a list of 8 xpubs. An xpub can be thought of as a master public key for a hierar­chical deter­min­istic wallet, which can be used to generate a series of public keys. The list contained 8 xpubs and the derivation paths and other details used to indicate how each series of public keys would be generated. The reason the list contains 8 xpubs, is because Bitcoin Core supports 4 hierar­chical deter­min­istic (HD) wallet schemes and each has a version for receiving and a version for change. For instance public key hash, witness public key hash or the one we are interested in today, Taproot. The xpub for is Taproot can be found by looking for tr(). In our case the xpub was as follows:


We also need the master private keys to conduct this exercise, the following command can be used to display the private keys:

listdescriptors true

The corresponding master private key for our Taproot xpub is shown below:


In order to use the “advanced” Taproot functionality of spending using the Tapleaf, we had to also get another pair of master HD keys. The ones we used are shown below:



With the above information, we can then use the xpubs to generate Taproot spending conditions and a Taproot address. Below we have shown how to conduct this using the two xpubs:

deriveaddresses "tr([becb3bf1/86'/0'/0']xpub6DSxK6Vxk8tidDT6rLuGXNShzV9SS7HruXif56dU23WoSvejTdTN2UNY6gRhXreRYpRrwLN2mduyGqBLfxMyGFMDPdNJGmkjPik56qVSArA/0/*,pk([becb3bf1/44'/0'/0']xpub6DQq1Noe9suZnf6jaaqT8EQp2HeETEXDy972dkcpFyDBCigsECSd3vaQynmGfPo4xz9WE9SXhFkTbkQVd61HKYfJopRNBgfEcESKJWpBxCL/0/*))#8gn2q5qp" "[0,1]"

This generated the below Bech32m address:


Spending Bitcoin From The Taproot Address

Remember, we have set it up so that there are two ways to redeem funds from the Taproot address.

  1. The “normal” spend scenario, where all we need to provide is a Schnorr signature
  2. The so-called “abnormal” scenario, where we need to provide the untweaked public key, the redeem script and of course still a signature

In our example we only have one branch on our tree, to avoid making things too complicated. In order to test the redemption of funds we sent 0.001 BTC to our Taproot address. Then, so that we can control the redemption of the funds, we created a brand new Bitcoin Core wallet. We then imported the private key for our first Taproot xpub, followed by the xpub from the 2nd pair of master HD keys as follows:

Spend scenario 1 – The “normal” spend – Importing Keys

importdescriptors '[{ "desc": "tr(xprv9s21ZrQH143K2VuTba4fXmpA6yjZjaTGh5q2oaGerWMeCozTKAQXRQAkMEAYnKL8Bw6geeFW4EsLNaEYacbVWe6zB4MtnXSFmoAEMh6nSk8/86h/0h/0h/0/*,pk([becb3bf1/44h/0h/0h]xpub6DQq1Noe9suZnf6jaaqT8EQp2HeETEXDy972dkcpFyDBCigsECSd3vaQynmGfPo4xz9WE9SXhFkTbkQVd61HKYfJopRNBgfEcESKJWpBxCL/0/*))#z6cqpzlk", "timestamp":1655925204, "internal": false, "active": true, "range": [0,2] }]'

Importing these details into Bitcoin Core’s wallet was challenging and confusing and it may get more difficult the more complex the spending conditions are. Miniscript is designed to potentially solve this problem, as it should help compile these scripts into the required format for you.

After we imported the above keys and Taproot scheme, we then saw the incoming Bitcoin transaction of 0.001 BTC in the Bitcoin Core GUI (this required a restart). We were then able to spend the funds using the GUI and the transactions details are shown below.

Normal Spend Scenario

Source: https://mempool.space/tx/d8d482f4f6416dc58afc8e1ab7681cffa9fe47c1083c716a28b2063c73315ffb

The transaction witness is around 64 bytes and is simply the Schnorr signature. No other details are required to conduct the spend. One can also see the “tweaked” public key, which starts 6807....

After the funds were sent away, we then sent another 0.001 BTC to the same address. Then we created another brand new and empty Bitcoin Core wallet. Now we imported a different set of keys using the same RPC command. We switched it around to try the alternative redemption methadology. We first imported the xpub for the first Taproot master key pair, followed by the private key for the second master key pair, as follows:

Spend scenario 2 – The “abnormal” spend

importdescriptors '[{ "desc": "tr([becb3bf1/86h/0h/0h]xpub6DSxK6Vxk8tidDT6rLuGXNShzV9SS7HruXif56dU23WoSvejTdTN2UNY6gRhXreRYpRrwLN2mduyGqBLfxMyGFMDPdNJGmkjPik56qVSArA/0/*,pk(xprv9s21ZrQH143K2VuTba4fXmpA6yjZjaTGh5q2oaGerWMeCozTKAQXRQAkMEAYnKL8Bw6geeFW4EsLNaEYacbVWe6zB4MtnXSFmoAEMh6nSk8/44h/0h/0h/0/*))#4erxc8tf", "timestamp":1655925204, "internal": false, "active": true, "range": [0,2] }]'

Now, as before, we saw an incoming transaction of 0.001 BTC and we were able to spend it, but this time using the Tapleaf method.

Source: https://mempool.space/tx/977b937e7ab7ca52d87ec25a50e36ff164dacb00165ef30dbb13f63c0e7bf7eb

This transaction is larger and more complicated than our first attempt. The witness now contains three elements:





Un-tweaked public key


We can verify the tweaked and un-tweaked public keys using the getaddressinfo call in Bitcoin Core, in a wallet which has the two relevant xpubs available. We have shown this below. The tweaked public key is called “scriptPubKey”, while the un-tweaked public key is in the “desc” field.

Source: Bitcoin Core

Summary of Spending Conditions

An overview of our two example Taproot spending transactions is shown in the diagram below. On the left hand side is the “normal” spend, while on the right hand side is the spend via Tapleaf and the un-tweaked public key.


Given how difficult the above process was and the potential risks involved, we would not recommend using the Tapleaf method for now, unless you are a professional wallet developer. However, reading through our example may help improve one’s understanding of Taproot and help one envisage how this technology may be useful in the future, either with Lightinng or other wallet systems.

We would like to thank Sjors Provoost for contributing to this article.