Private key → P2TR address (Taproot)
The BIP86 key-path-only Taproot bc1p… address, with every step exposed: the point P = k·G, the x-only internal key and its parity rule, the TapTweak tagged hash, the tweaked point Q = P + t·G, the output key x(Q), and the bech32m encoding with witness version 1.
Enter a value above and press Convert. Not sure what to type? Press “Use example” to load the sample from the placeholder.
What this page computes
Input: one 256-bit private key. Output: the BIP86 key-path Taproot address, the string
that starts with bc1p. Taproot is not "SegWit with a bigger program" — the public key is first
adjusted by a hash-derived scalar, and that adjustment is what the address commits to:
Every intermediate value is printed in the result panel, including the parity decision, the two 32-byte hashes inside the tagged hash, and the tweaked point itself.
1. What Taproot set out to solve
Taproot is three proposals shipped together, activated on 14 November 2021:
| BIP | What it defines |
|---|---|
| BIP340 | Schnorr signatures for secp256k1, defined over 32-byte x-only public keys |
| BIP341 | Taproot outputs and spending rules: the output key, the TapTweak adjustment, key-path and script-path spends |
| BIP342 | Tapscript — the script language used inside script-path spends |
| BIP350 | bech32m, the checksum variant that witness v1+ addresses must use |
Privacy. Before Taproot, a payment to a contract looked different on-chain: the output
committed to a script hash, so an observer could see that something complicated was happening. Taproot makes
the two indistinguishable. Every output looks like
OP_1 <32 bytes>. If everyone in a multisig or a channel cooperates, they spend it with a
single signature on the key path, and the blockchain shows exactly what an ordinary single-signature payment
shows. Only if cooperation fails does a script path get revealed — and even then, only the one branch that
was used.
Schnorr instead of ECDSA. Schnorr signatures (BIP340) are a fixed 64 bytes; ECDSA signatures are DER-encoded, vary between 70 and 72 bytes, and have a malleable encoding SegWit had to work around. More importantly, Schnorr is linear: a sum of signatures is a valid signature for the sum of keys. That property is what makes MuSig-style key aggregation, threshold signatures and batch verification possible without hacks.
2. x-only public keys: why BIP340 throws away y
An secp256k1 point has two coordinates, but for any x there are only two candidate points: P
and −P = (x, p − y). One of them always has an even y. BIP340 defines the canonical
interpretation of a 32-byte public key as "the point with that x and even y" — a function called
lift_x. Dropping y saves a byte in every signature and removes an ambiguity that ECDSA carried
around for years.
Two consequences matter in practice:
- If your private key
kproduces a point with odd y, the BIP340 signer usesn − kinstead, since(n − k)·G = −P, the even-y representative of the same x. Fork = 6the point's y isae12777a…6b075f297, which is odd, so the signer negates — yet the address is stillbc1p4rsld9ryjhte00drc0r23r8ngd63xrzh5s4fvmy6q5yt70xzlsdqcuvtzv. Get this wrong and signatures fail to verify. - Because the tweak below depends only on x,
Pand−Pproduce the same Taproot address — this page computes both and shows they match — while their legacy P2PKH addresses differ. x-only keys are not a compression trick; they are a different signing model.
3. The TapTweak construction, and why it is a tagged hash
An output key is never the raw public key you derived. It is that key plus a scalar multiple of the generator:
For a key-path-only address (BIP86) the merkle root is omitted entirely, so the input is just the 32-byte
x-only internal key. The result Q is the output key, and only its x coordinate goes into
the address. Nobody can tell from the address whether a script tree exists — the internal key absorbs that
commitment.
| Offset | Bytes | Content |
|---|---|---|
| 0 | 32 | x(P) — the x-only internal key |
| 32 | 0 or 32 | the Taproot Merkle root of the script tree: absent for BIP86 key-path-only, 32 bytes when a script tree exists |
A tagged hash is the BIP340 pattern for deriving specialised hashes from SHA-256:
The tag hash is prepended twice. That costs one extra compression block and buys
domain separation: a hash computed for one purpose can never collide with a hash computed
for another. Without it, an attacker could construct a value meaningful both as a signature nonce and as a
TapTweak input — a cross-protocol attack surface that costs nothing to close. For
the tag "TapTweak", SHA256("TapTweak") is
e80fe1639c9ca050e3af1b39c143c63e429cbceb15d940fbb5c5a1f4af57c5e9.
Adding t·G is what makes a script tree optional: the tree's Merkle root is folded
into the key, so the key itself is the commitment. It also means the key you publish (the internal key) is
not the key that verifies signatures (the output key): substituting the internal key yields a different
output key and therefore a different address. If the computed tweak
is ≥ n or Q is the point at infinity, the construction fails and must be retried (BIP341);
the odds are about 2−128.
4. Key path vs script path
There are two ways to spend a Taproot output.
- Key path. Provide one signature by the tweaked private key. The witness contains a single 64-byte (occasionally 65-byte) item. Nothing about scripts, branches or participants is revealed. This is the cheapest and most private option, and it is the only one this BIP86 page builds.
- Script path. Provide a leaf script, the data it needs, and a control block: the internal key plus the 32-byte sibling hashes that lead from that leaf to the Merkle root. The verifier recomputes the root, redoes the tweak and checks that the output key matches. Only the used leaf is revealed; every unused branch stays hidden — which a P2SH multisig cannot do, since it publishes the whole script.
This is why the page is key-path-only: it is what a single-key wallet (BIP86) does, and it is the only
form where the address depends on nothing but the internal key. If you wanted a script path, the tweak would
need the tree's Merkle root, giving a completely different tweak, a different output key and a different
address. For scale: substituting a dummy 32-byte Merkle root of 1111…11 turns the demo key's
tweak into 254264f6410f1b3890db1ee95ea9990b28875f4b3e06cf35dfd0d0a6dda6d04b instead of the real
bae91685…85e8fa4d, so the address changes too.
An address is a commitment to one specific output key. If you generate a BIP86 address and later try to spend it with a script path, the scripts are not in the commitment and the spend is invalid. Conversely, if you build an address with a script tree and then only ever use the key path, your coins are fine — but the address will not match what a "plain" wallet shows, which is exactly why BIP86 exists: it pins one deterministic, script-free construction so that wallets agree.
5. bech32m, and why witness v1+ needs it
Taproot addresses use bech32m (BIP350): the same alphabet and structure as bech32, with a
different checksum constant, 0x2bc830a3 (734539939) instead of 1. Witness v0 must use
bech32; witness v1 and above must use bech32m.
The reason for the second variant is an insertion weakness in the original checksum, stated plainly in
BIP350: whenever the final character of a bech32 string is p, inserting or deleting any
number of q characters immediately before it does not invalidate the checksum. The
checksum characters shift along, so the same six characters can validate strings whose data parts have
different lengths — an ambiguity between "this address" and "that address" produced from one string. The
server behind this page verified it: the valid address
bc1qgey9e3xtk8fpdsgchch25saytdp6nlhdt9gm0p keeps a valid checksum after inserting one, two or
three q characters before its final p (…gm0qp, …gm0qqp,
…gm0qqqp). Across 78,624 single-character insertions attempted on bc-prefixed
strings, every checksum-preserving insertion had exactly that shape, and all of them were bech32
strings — not one bech32m string accepted an insertion. The same experiment on a bech32m address
ending in p fails the checksum outright, which is the whole point of the new constant.
Witness version 0 escaped the problem: its programs are restricted to exactly 20 or 32 bytes, so a decoder that enforces the structural rules rejects the mutated string. Witness v1+ has no such restriction, so BIP350 mandated the fixed constant for it.
Re-encode this page's Taproot address with the bech32 constant and you get
bc1pmsrhxnvmj8tjxxu9wj69ntvwvhvr28qufyc26tshz3rjj777tx3szg9sln, which decodes to
"Witness v1+ must use bech32m, not bech32". Re-encode the demo P2WPKH address with the bech32m
constant and you get bc1qy86hkm0telzmvuvzm76279jp7z5p3yqjxpf0nc, which decodes to
"Witness v0 must use bech32, not bech32m". Only the last six characters differ — a checksum variant
is a security property, not a cosmetic detail.
6. The witness program and the address, byte by byte
A Taproot output script is 34 bytes: a version opcode, a 32-byte push, and the output key.
| Offset | Bytes | Content | Meaning |
|---|---|---|---|
| 0 | 1 | 51 | OP_1 — pushes the value 1, which doubles as witness version 1 |
| 1 | 1 | 20 | push the next 32 bytes (0x20 = 32) |
| 2 | 32 | x(Q) | the x-only tweaked output key — the witness program |
And the address that encodes it:
| Part | Demo value | Rule |
|---|---|---|
hrp | bc | bc mainnet, tb testnet |
| Separator | 1 | split at the last 1; 1 is not in the data charset |
| Version character | p | p = value 1 = witness v1 (q is value 0, used by P2WPKH) |
| Program characters | msrhxnvm…777tx3s (52) | 256 bits ÷ 5 = 51.2, so 52 words: 51 full words plus 1 bit padded with four zeros |
| Checksum | h54u63 | 6 characters, bech32m constant 0x2bc830a3 |
| Total | 62 characters | 2 + 1 + 53 + 6 |
Decoding the demo address returns witver = 1, variant bech32m, and a 32-byte
program identical to the output key in the results — a round trip, not an assumption.
7. Worked example — the real demo key
| Step | Value |
|---|---|
Private key k | 0824c314772b2c2859c889471390a36bf66e53a1ca1642f96f0f2c9307a171ca |
| P = k·G — x | ff812e26116a9aa140abe629e7f8a38401653caf925119def59ed3758c67ee80 |
| P = k·G — y | 8f49cde13c7ffd64f1d730bf87a743a578eda4971ddac4a08118160732b424ae |
| Parity | last hex digit e → even, so no negation is needed and the internal key equals x(P) |
| Internal key (x-only) | ff812e26116a9aa140abe629e7f8a38401653caf925119def59ed3758c67ee80 |
SHA256("TapTweak") | e80fe1639c9ca050e3af1b39c143c63e429cbceb15d940fbb5c5a1f4af57c5e9 |
Tweak t | bae9168523281e7f9e91d897a8d23f7ae6fe66d61854019dcb8abb5185e8fa4d |
t·G — x | baa35582aa3a12f4c5fb2cace80671a765dfc95cee356d28b31eb757c52b94a8 |
t·G — y | be8ddfba65063b45db3fb0c2c14d909e0981d1f04cdcde251d5fb18eeda211ff |
| Q = P + t·G — x (output key) | dc07734d9b91d7231b8574b459ad8e65d8351c1c4930ad2e171447297bde59a3 |
| Q = P + t·G — y | f54d5ae5bfeade97f7148ebd308fa916f268bde1bea6c64594e5baf904831284 |
| Output script | 5120dc07734d9b91d7231b8574b459ad8e65d8351c1c4930ad2e171447297bde59a3 |
| Data part | pmsrhxnvmj8tjxxu9wj69ntvwvhvr28qufyc26tshz3rjj777tx3s |
| Checksum | h54u63 (values 23 20 21 28 26 17) |
| P2TR address | bc1pmsrhxnvmj8tjxxu9wj69ntvwvhvr28qufyc26tshz3rjj777tx3sh54u63 |
| Same key, testnet | tb1pmsrhxnvmj8tjxxu9wj69ntvwvhvr28qufyc26tshz3rjj777tx3squrnq7 |
8. Taproot next to P2WPKH
| P2WPKH (witness v0) | P2TR key path (witness v1) | |
|---|---|---|
| Address | bc1q…, 42 characters | bc1p…, 62 characters |
| Encoding | bech32, constant 1 | bech32m, constant 0x2bc830a3 |
| Witness program | 20 bytes: hash160(compressed public key) | 32 bytes: x-only tweaked output key |
| Output script | 0014… (22 bytes) | 5120… (34 bytes) |
| Signature | ECDSA, DER, ~71–72 bytes | Schnorr (BIP340), 64 bytes |
| Weight per input | 272 WU (68 vB) | 230 WU (57.5 vB) — 15.4% lighter |
| What a spend reveals | the public key, every time | nothing, if the key path is used |
| Script flexibility | fixed: one key, one signature | key path or any tapscript leaf, invisible until used |
| Activated | 24 August 2017 | 14 November 2021 |
9. Where this fits in the chain
- Before it: private key → compressed public key and P2WPKH, which uses the same key with a hashed 20-byte program.
- Related: secp256k1 point arithmetic for what
P + t·Greally means, and the bech32/bech32m decoder to take this address apart again. - Wallets: BIP39 mnemonic → BIP32 child key — a BIP86 wallet derives
m/86'/0'/0'/0/iand builds exactly this address.
10. Security notes
- The address commits to the tweaked key, so you cannot recognise your own Taproot address by looking at your public key alone. Always derive, never compare by eye.
- x-only means the y parity of your point may have to be negated before signing. If a wallet reports "signature verification failed" for a key that is definitely correct, check the parity handling.
- A script-path spend needs the internal key, the leaf script and the control block. Lose the control block and the branch is permanently unspendable; the key path is your backup, which is a good reason to keep a key path in any Taproot wallet.
- Reusing one Taproot address links payments exactly like any other address type does. Taproot hides scripts, not the fact that the same key was paid twice.
11. Common mistakes
- Tweaking with a tagged hash of the wrong tag.
"TapTweak"is case-sensitive, and the tag is hashed twice. A single wrong character yields a valid-looking, useless address. - Using the internal key as the address. The address commits to
x(Q), not tox(P). Funds sent to an "address" built from the untweaked key are unspendable. - Encoding v1 with bech32. Rejected by every compliant decoder; see the verified error messages above.
- Assuming Taproot hides the amount or the sender. It hides scripts, not amounts; the transaction graph stays public.
- Forgetting the version character.
bc1pandbc1qdiffer by one character and describe different scripts; copy addresses, never retype them.
12. Quick reference
| Item | Value |
|---|---|
| Address type | P2TR, witness version 1, key-path-only (BIP86 style) |
| Tweak | t = int(tagged_hash("TapTweak", x(P))) mod n, no Merkle root |
| Tagged hash | SHA256(SHA256(tag) ‖ SHA256(tag) ‖ msg) |
| Output key | Q = P + t·G, published as x(Q) (32 bytes) |
| Address length | 62 characters on mainnet |
| Checksum | bech32m, constant 0x2bc830a3 = 734539939 |
| Output script | OP_1 0x20 ‖ 32-byte key (34 bytes) |
| Witness for a key-path spend | one 64-byte Schnorr signature |