Private Key Tools

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.

64 hex digits = 32 bytes = 256 bits, in the range [1, n−1]. An optional 0x prefix, spaces and upper-case letters are accepted; shorter values are left-padded with zeros. This page builds the key-path-only form: no script tree, so the TapTweak input is the 32-byte internal key alone.
No input yet

Enter a value above and press Convert. Not sure what to type? Press “Use example” to load the sample from the placeholder.

How it works

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:

P = k·G  →  t = int( tagged_hash("TapTweak", x(P)) ) mod n  →  Q = P + t·G  →  address = bech32m("bc", 1, x(Q))
private key k→ P = k·G→ x-only internal key x(P)→ tagged hash "TapTweak"→ t (scalar)→ Q = P + t·G→ output key x(Q)→ bech32m, witness v1→ bc1p…

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:

BIPWhat it defines
BIP340Schnorr signatures for secp256k1, defined over 32-byte x-only public keys
BIP341Taproot outputs and spending rules: the output key, the TapTweak adjustment, key-path and script-path spends
BIP342Tapscript — the script language used inside script-path spends
BIP350bech32m, 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:

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:

t = int( tagged_hash( "TapTweak", x(P) ‖ merkle_root ) ) mod n    Q = P + t·G

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.

OffsetBytesContent
032x(P) — the x-only internal key
320 or 32the 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:

tagged_hash(tag, msg) = SHA256( SHA256(tag) ‖ SHA256(tag) ‖ msg )

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.

The tweak both commits and protects

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.

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.

Do not mix the two models up

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.

Cross the pairing and the address stops being an address

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.

OffsetBytesContentMeaning
0151OP_1 — pushes the value 1, which doubles as witness version 1
1120push the next 32 bytes (0x20 = 32)
232x(Q)the x-only tweaked output key — the witness program

And the address that encodes it:

PartDemo valueRule
hrpbcbc mainnet, tb testnet
Separator1split at the last 1; 1 is not in the data charset
Version characterpp = value 1 = witness v1 (q is value 0, used by P2WPKH)
Program charactersmsrhxnvm…777tx3s (52)256 bits ÷ 5 = 51.2, so 52 words: 51 full words plus 1 bit padded with four zeros
Checksumh54u636 characters, bech32m constant 0x2bc830a3
Total62 characters2 + 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

StepValue
Private key k0824c314772b2c2859c889471390a36bf66e53a1ca1642f96f0f2c9307a171ca
P = k·G — xff812e26116a9aa140abe629e7f8a38401653caf925119def59ed3758c67ee80
P = k·G — y8f49cde13c7ffd64f1d730bf87a743a578eda4971ddac4a08118160732b424ae
Paritylast 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 tbae9168523281e7f9e91d897a8d23f7ae6fe66d61854019dcb8abb5185e8fa4d
t·G — xbaa35582aa3a12f4c5fb2cace80671a765dfc95cee356d28b31eb757c52b94a8
t·G — ybe8ddfba65063b45db3fb0c2c14d909e0981d1f04cdcde251d5fb18eeda211ff
Q = P + t·G — x (output key)dc07734d9b91d7231b8574b459ad8e65d8351c1c4930ad2e171447297bde59a3
Q = P + t·G — yf54d5ae5bfeade97f7148ebd308fa916f268bde1bea6c64594e5baf904831284
Output script5120dc07734d9b91d7231b8574b459ad8e65d8351c1c4930ad2e171447297bde59a3
Data partpmsrhxnvmj8tjxxu9wj69ntvwvhvr28qufyc26tshz3rjj777tx3s
Checksumh54u63 (values 23 20 21 28 26 17)
P2TR addressbc1pmsrhxnvmj8tjxxu9wj69ntvwvhvr28qufyc26tshz3rjj777tx3sh54u63
Same key, testnettb1pmsrhxnvmj8tjxxu9wj69ntvwvhvr28qufyc26tshz3rjj777tx3squrnq7

8. Taproot next to P2WPKH

P2WPKH (witness v0)P2TR key path (witness v1)
Addressbc1q…, 42 charactersbc1p…, 62 characters
Encodingbech32, constant 1bech32m, constant 0x2bc830a3
Witness program20 bytes: hash160(compressed public key)32 bytes: x-only tweaked output key
Output script0014… (22 bytes)5120… (34 bytes)
SignatureECDSA, DER, ~71–72 bytesSchnorr (BIP340), 64 bytes
Weight per input272 WU (68 vB)230 WU (57.5 vB) — 15.4% lighter
What a spend revealsthe public key, every timenothing, if the key path is used
Script flexibilityfixed: one key, one signaturekey path or any tapscript leaf, invisible until used
Activated24 August 201714 November 2021

9. Where this fits in the chain

10. Security notes

11. Common mistakes

12. Quick reference

ItemValue
Address typeP2TR, witness version 1, key-path-only (BIP86 style)
Tweakt = int(tagged_hash("TapTweak", x(P))) mod n, no Merkle root
Tagged hashSHA256(SHA256(tag) ‖ SHA256(tag) ‖ msg)
Output keyQ = P + t·G, published as x(Q) (32 bytes)
Address length62 characters on mainnet
Checksumbech32m, constant 0x2bc830a3 = 734539939
Output scriptOP_1 0x20 ‖ 32-byte key (34 bytes)
Witness for a key-path spendone 64-byte Schnorr signature