Fully Annotated P2TR Key Path Execution
From TeachMeBitcoin, the free encyclopedia
Reading time: 3 min
6. Fully Annotated P2TR Key Path Execution
Pay-to-Taproot (P2TR) key path spending is the simplest and most private way to spend a Taproot output. It uses a single Schnorr signature against the tweaked public key embedded in the output.
Creating the P2TR Output
def create_p2tr_output(internal_pubkey, script_tree=None):
"""
Create a P2TR output committing to an internal key
and optionally a script tree.
internal_pubkey: 32-byte x-only public key
script_tree: optional MAST of spending scripts
"""
if script_tree is None:
# No scripts: tweak with empty tree
merkle_root = b''
tweak_data = internal_pubkey # Just the key
else:
merkle_root = compute_merkle_root(script_tree)
tweak_data = internal_pubkey + merkle_root
# Compute the tweak
tweak = tagged_hash("TapTweak", tweak_data)
tweak_int = int.from_bytes(tweak, 'big')
# Compute tweaked public key
# P' = P + tweak*G
internal_point = lift_x(int.from_bytes(internal_pubkey, 'big'))
tweak_point = point_mul(G, tweak_int)
tweaked_point = point_add(internal_point, tweak_point)
# Get x-only representation
tweaked_pubkey_x = tweaked_point.x.to_bytes(32, 'big')
# scriptPubKey: OP_1 <32-byte-tweaked-pubkey>
return bytes([0x51, 0x20]) + tweaked_pubkey_x
Key Path Spending Transaction
Input being spent:
scriptPubKey: 5120 <32-byte-tweaked-pubkey>
(OP_1 = 0x51, push 32 bytes = 0x20, then the key)
Spending transaction input:
scriptSig: (empty — key path uses witness only)
witness: [<64-or-65-byte-schnorr-signature>]
Schnorr Signature Format
Key path Schnorr signature formats:
64 bytes (SIGHASH_DEFAULT = 0x00):
<32-byte R_x> <32-byte s>
No sighash type byte appended — SIGHASH_DEFAULT is implied
65 bytes (any other sighash type):
<32-byte R_x> <32-byte s> <1-byte sighash_type>
sighash_type can be: 0x01 (ALL), 0x02 (NONE), 0x03 (SINGLE)
or with ANYONECANPAY: 0x81, 0x82, 0x83
Signing and Verification
def sign_taproot_key_path(private_key, tx, input_index, utxos, sighash_type=0x00):
"""
Create a Schnorr signature for a Taproot key path spend.
Implements BIP 340 signing with BIP 341 sighash.
"""
# Compute the tweaked private key
# d = private_key + tweak (mod curve order)
tweak = compute_tap_tweak(private_key_to_pubkey(private_key))
tweaked_private_key = (private_key + int.from_bytes(tweak, 'big')) % SECP256K1_ORDER
# Compute sighash using BIP 341 algorithm
sighash = compute_taproot_sighash(tx, input_index, utxos, sighash_type)
# BIP 340 Schnorr signing:
# 1. Generate deterministic nonce k
rand = os.urandom(32) # Optional auxiliary randomness
t = xor(tweaked_private_key_bytes, tagged_hash("BIP0340/aux", rand))
k = int.from_bytes(tagged_hash("BIP0340/nonce", t + pubkey + sighash), 'big') % ORDER
# 2. R = k*G
R = point_mul(G, k)
# 3. If R.y is odd, negate k
if R.y % 2 != 0:
k = ORDER - k
# 4. e = H("BIP0340/challenge", R.x || P.x || msg)
e = int.from_bytes(
tagged_hash("BIP0340/challenge",
R.x.to_bytes(32,'big') +
tweaked_pubkey_x +
sighash), 'big'
) % ORDER
# 5. s = k + e*d (mod n)
s = (k + e * tweaked_private_key) % ORDER
# 6. Signature = (R.x, s)
sig = R.x.to_bytes(32, 'big') + s.to_bytes(32, 'big')
if sighash_type != 0x00:
sig += bytes([sighash_type])
return sig
Technical Insight
This topic covers essential mechanics for Chapter 12. Understanding these details is key to mastering advanced Bitcoin script constructions like Taproot and specialized covenants.
☕ Help support TeachMeBitcoin
TeachMeBitcoin is an ad-free, open-source educational repository curated by a passionate team of Bitcoin researchers and educators for public benefit. If you found our articles helpful, please consider supporting our hosting and ongoing content updates with a clean donation:
Ethereum:
0x578417C51783663D8A6A811B3544E1f779D39A85
Bitcoin:
bc1q77k9e95rn669kpzyjr8ke9w95zhk7pa5s63qzz
Solana:
4ycT2ayqeMucixj3wS8Ay8Tq9NRDYRPKYbj3UGESyQ4J