More on Taproot
Tags: bitcoinjs, bitcoin, taproot
Published: 2023-03-26 05:49:06

In a [previous article](https://dev.to/eunovo/a-guide-to-creating-taproot-scripts-with-bitcoinjs-lib-4oph), I gave a brief intro Taproot and shared some examples utilizing Taproot addresses.
In this article, I intend to share more information about taproot addresses and share one more example which I neglected in my [previous article](https://dev.to/eunovo/a-guide-to-creating-taproot-scripts-with-bitcoinjs-lib-4oph). I'll share:
- Public Key Tweaking, what it is, why we do it and how we do it
- How Taproot addresses are created
- How to generate the private key required to sign a spending transaction from a Taproot address.
- How to spend a Taproot output that has a script-tree without using any leaf scripts with bitcoinjs

## Public Key Tweaking
### What is Public key Tweaking
According to a [workshop on taproot by Bitcoin Optech](https://github.com/bitcoinops/taproot-workshop/blob/master/2.2-taptweak.ipynb), you can tweak a public key by adding a value (the tweak) to it. This way, you can still spend the tweaked public key if you know the original private key and the tweak. It is used in taproot to commit data or scripts.

### Why do we tweak keys
We tweak keys in taproot to commit to data or scripts that can be used for different spending conditions. This way, we can make complex transactions look like simple ones and save space and fees.

### Why didn't we tweak before Taproot
Before taproot, we used a different type of digital signatures called ECDSA. ECDSA did not allow for easy tweaking. Taproot uses a new type of signatures called Schnorr that enable tweaking and other benefits.

### How does a tweak work under the hood
A tweak is positive scalar value `t` where:

`0 < t < SECP256K1_ORDER`

_In bitcoin cryptography, we use the elliptic curve secp256k1 and `SECP256K1_ORDER` is the number of points on the curve_

A point, the Tweak point, is obtained from this tweak scalar value by multiplying it by the generator point:

`T = t * G`

_The generator point is a special point that can produce any other point in its subgroup by multiplying it by some integer_

This tweak point is added to the x-coordinate of the original public key to get the tweaked public key. Prepend a parity bit to this tweaked public key to indicate if it is positive or negative.

In taproot, we get the tweak value by concatenating the x-coordinate of the public key and a commitment to the script path to spend and hashing it. To spend a taproot output, you can sign with the tweaked private key (private key + tweak value) or provide a proof of the script path spend.

## Creating a Taproot address
Taproot allows you to create addresses that can be spent by a single public key or by scripts. It does this by allowing you to add data to a public key by tweaking it.

To create a P2TR (Pay-to-Taproot) address for a single public key, you need to:

1. Check that the y-coordinate of your public key is even, if not, negate the public key. Taproot requires that the y coordinate of the public key is even.
2. extract the x-coordinate (bytes 1 to 33) of your public key (which is a point on an Elliptic Curve)
3. tweak the public key with this x-only part of the public key.
4. hash the x-coordinate of the tweaked public key with SHA256 and RIPEMD to get a 20-byte address.
5. Prepend the version byte 0x51(indicates that the taproot address is a native SegWit output with version 1) to the address to get a 21-byte witness program.
6. Encode the witness program with Bech32m encoding.

To create an address with multiple spending conditions (in the form of scripts), we arrange our scripts into a Merkle Tree, where each node is a hash of its children. The root of this tree is then used to tweak a public key and an address can be generated from this tweaked public key in the same steps (from step 4) defined above.

_Even if you don't need multiple spending conditions for your address, BIP341 requires that you tweak the original public key with an unspendable script path which is what we do in step 3_

## Tweaking private keys for signing
To sign a transaction input spending a Taproot output, you need to tweak the private key for the original public key using the same tweak value that generated the address. If the public key was negated (see step 1 above) you must also negate the private key. The tweaked private key will create a valid Schnorr signature for the tweaked public key. This works because of the mathematical properties of Schnorr signatures and elliptic curves.

## Spending a Taproot output with a script tree via the key path using bitcoinjs
In the Taptree example from my [previous article](https://dev.to/eunovo/a-guide-to-creating-taproot-scripts-with-bitcoinjs-lib-4oph). It is possible to spend the Taproot output without using the script tree.

```typescript
// ...

const script_p2tr = payments.p2tr({
    internalPubkey: toXOnly(keypair.publicKey),
    scriptTree,
    network
});

// ...

const key_spend_psbt = new Psbt({ network });
key_spend_psbt.addInput({
    hash: utxos[0].txid,
    index: utxos[0].vout,
    witnessUtxo: { value: utxos[0].value, script: script_p2tr.output! },
    tapInternalKey: toXOnly(keypair.publicKey),
    tapMerkleRoot: script_p2tr.hash
});
key_spend_psbt.addOutput({
    address: "mohjSavDdQYHRYXcS3uS6ttaHP8amyvX78", // faucet address
    value: utxos[0].value - 150
});
// We need to create a signer tweaked by script tree's merkle root
const tweakedSigner = tweakSigner(keypair, { tweakHash: script_p2tr.hash });
key_spend_psbt.signInput(0, tweakedSigner);
key_spend_psbt.finalizeAllInputs();

tx = key_spend_psbt.extractTransaction();
console.log(`Broadcasting Transaction Hex: ${tx.toHex()}`);
txid = await broadcast(tx.toHex());
console.log(`Success! Txid is ${txid}`);
```

Thank you for reading!