Public key
How a Bitcoin public key is made from a private key on the secp256k1 curve, and its compressed, uncompressed and x-only forms.
By Dimitar Zurnov · Updated 4 Nov 2026
Adapted from Learn Me a Bitcoin by Greg Walker.
You make a Bitcoin public key by multiplying a fixed point on the secp256k1 elliptic curve by your private key. The result is a point on that curve. It is the public half of the pair, so you can share it safely. Coins can be locked to a public key, and only the matching private key can unlock them. Bitcoin writes a public key as hex in one of three forms: compressed, uncompressed or x-only.
How is a public key made?
A public key comes from elliptic curve multiplication. The curve has a fixed starting point called the generator point, G. You multiply G by your private key, a number from 1 to n-1, and the result is a new point on the curve. That point is the public key. Multiplying is quick. Working backwards from the point to the private key is not practical with any known method. The elliptic curve page covers the maths.
The parameters of secp256k1 are published in SEC 2. This short script multiplies G by a private key using no library. It is slow, so it is for learning only. Real code should use a tested library.
# secp256k1 field prime and generator point
P = 2**256 - 2**32 - 977
G = (0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8)
def add(a, b):
if a is None: return b
if b is None: return a
if a[0] == b[0] and (a[1] + b[1]) % P == 0: return None
if a == b: s = 3 * a[0] * a[0] * pow(2 * a[1], -1, P) % P
else: s = (b[1] - a[1]) * pow(b[0] - a[0], -1, P) % P
x = (s * s - a[0] - b[0]) % P
return (x, (s * (a[0] - x) - a[1]) % P)
def public_key(k):
"""Multiply the generator point G by the private key k."""
point, result = G, None
while k:
if k & 1: result = add(result, point)
point = add(point, point)
k >>= 1
return result
k = int("dbf34ebb797c7b62960c0c7d1784c3209d5de7abf307df4a07dac9c76f22ee72", 16)
x, y = public_key(k)
prefix = "02" if y % 2 == 0 else "03"
print(prefix + f"{x:064x}") # 02cd33111c7fc75b33bc30ad959dd27433d631dee5bf072733334163dedc403070
The example key is the one from the private key page. Its compressed public key is 02cd33111c7fc75b33bc30ad959dd27433d631dee5bf072733334163dedc403070. Do not use this key with real coins, because anyone can see it.
What are the three forms of a public key?
Every form starts with the same x coordinate, a 32-byte number. The forms differ in what they add to it. A compressed key adds a one-byte prefix that records the parity of y. An uncompressed key adds the full y coordinate. An x-only key drops the prefix entirely.
What is a compressed public key?
A compressed public key is 33 bytes. It holds the full x coordinate, plus a prefix that says whether y is even or odd. The prefix is 02 when y is even and 03 when y is odd.
The curve is symmetric, so for any x that lies on the curve there are only two possible y values. They have opposite parity, one even and one odd. The prefix is enough to pick the right one, so the key stores the prefix and not the whole y. Our example key has an even y, so its prefix is 02.
You should use compressed keys by default. They are shorter, and Bitcoin Core's default policy rejects uncompressed keys in SegWit v0 scripts such as P2WPKH and P2WSH. Such transactions are not relayed or mined by default. The check is the SCRIPT_VERIFY_WITNESS_PUBKEYTYPE flag in Bitcoin Core's script interpreter. BIP 143 sets the same rule.
What is an uncompressed public key?
An uncompressed public key is 65 bytes. It starts with 04, followed by the full 32-byte x coordinate and the full 32-byte y coordinate. Early software used this form, before compressed keys were in common use. Our example key in uncompressed form is:
04cd33111c7fc75b33bc30ad959dd27433d631dee5bf072733334163dedc403070002efe73430d2af79531c7d8f64500b5c392db9af29ccfd60e2ca7ea9c198538
Uncompressed keys still work in legacy locking scripts, such as P2PK, P2PKH, P2SH and bare multisig. Default policy rejects them in P2WPKH and P2WSH outputs. So there is little reason to choose them today.
What is an x-only public key?
An x-only public key is the 32-byte x coordinate with no prefix. Taproot uses this form, as defined in BIP 340. Because there is no prefix, the key is read with an even y. Any point with an odd y can be replaced by its even partner, so only x needs to be stored.
Our example key has an even y, so its x-only form is the compressed key without its 02 prefix: cd33111c7fc75b33bc30ad959dd27433d631dee5bf072733334163dedc403070. A Taproot output locks to a tweaked version of this key. The Taproot word page explains the upgrade, which has been live since block 709,632.
How do you decompress a public key?
You can rebuild the full uncompressed key from a compressed one. The secp256k1 curve follows the equation y² = x³ + 7, taken modulo the field prime p. You know x, so you solve for y. The answer is a square root, which has two values, one even and one odd. The prefix tells you which to keep.
Because p is 3 modulo 4, the square root is one exponentiation. The short script below rebuilds the uncompressed key from the compressed one.
P = 2**256 - 2**32 - 977
compressed = "02cd33111c7fc75b33bc30ad959dd27433d631dee5bf072733334163dedc403070"
x = int(compressed[2:], 16)
# y^2 = x^3 + 7 (mod P); P is 3 mod 4, so a square root is one power away
y = pow((x**3 + 7) % P, (P + 1) // 4, P)
# the prefix says whether y must be even (02) or odd (03)
if (y % 2 == 0) != (compressed[:2] == "02"):
y = P - y
print("04" + f"{x:064x}" + f"{y:064x}")
Where do public keys appear on the blockchain?
A public key usually appears on the blockchain only when its coins are spent. The exception is a lock that holds the full key, such as P2PK or bare multisig. The table shows where it sits for three common types of output.
| Locking script | Where the public key sits | Real example |
|---|---|---|
| P2PK | Written in full in the scriptPubKey, so it is public as soon as the coins arrive | 18a16d32…ee444, a coinbase output in block 150,007 |
| P2PKH | Only a hash is in the lock. The full key is revealed in the scriptSig when spent | faf2987f…0808c, which spends a P2PKH output in block 770,002 |
| P2WPKH | Only a hash is in the lock. The key is in the witness when spent | 1674761a…4a6, which spends a P2WPKH output in block 682,085 |
P2PK is the simplest lock, but it exposes the key from the start. P2PKH and P2WPKH hide the key behind a hash until you spend. A Taproot output is different again: it shows a tweaked version of the key from the start, not the key you hold.
How does a public key receive bitcoins?
When someone pays you, the output is locked to your public key or to its hash. Only a signature made with the matching private key can unlock it. The network checks that signature against the key in the lock, and it never needs your private key.
Most wallets show you an address rather than the raw key, because an address is shorter and easier to copy. The address is built from the public key hash, which is the next page in this section.
Which key should you use?
Keep the private key secret. The public key is the part you can publish. For legacy and SegWit v0 outputs, use compressed keys, which are 33 bytes. Taproot uses the x-only form of the same point, which is the compressed key without its prefix.
The next page, public key hash, shows how this key is hashed into the value that most addresses carry.
In this section: Keys and addresses
Something look wrong? Tell us