§ 04

§ 04 · Keys & addresses

Public key

A point on the curve that anyone can check signatures against

A public key is a point on a curve, computed from your private key. Anyone can use it to check your signatures. No one can use it to find your private key.

The trick is a one-way street. Multiplying a starting point by your secret number takes a computer microseconds. Working out the secret number from the result would take every computer on Earth longer than the universe has existed. So you can show your public key to the world and keep the private key to yourself.

Try it

Multiply points on a tiny curve you can see, then watch the same idea at full size.

Toy elliptic curve

y² = x³ + 7, the same equation as Pearl’s keys, on a tiny grid

001122334455667788991010111112121313141415151616QP−(P+Q)P+Q

(6, 6) + (1, 5)

= (8, 14)

Draw the line through P and Q (the shaded cells; on a grid mod 17 a line wraps around the edges). It hits exactly one more curve point, −(P+Q). Flip it across the middle line to get P + Q.

Field
0 … 16, mod 17
Points
18 (17 + O)
G
(6, 6)
Order of G
18

The real thing: secp256k1, the same equation over a prime of about 2²⁵⁶. It has about 1.16 × 10⁷⁷ points, far too many to draw or to search.

Runs in your browser · a toy, not real keys src/lib/keys.ts · toyCurve

What is a point on secp256k1?

Pearl uses secp256k1, the same elliptic curve as Bitcoin. Its points are pairs of whole numbers (x, y) that satisfy one equation, with all arithmetic done modulo a prime p:

y2≡x3+7(modp),p=2256−232−977y^2 \equiv x^3 + 7 \pmod{p}, \qquad p = 2^{256} - 2^{32} - 977

One point is special. The generator G is fixed by the standard, and every public key is a multiple of it:

Value
G.x 79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
G.y 483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8

How is the public key computed?

By multiplying G by the private key d:

P=d⋅GP = d \cdot G

“Multiplying” a point means adding it to itself d times, using the curve’s own addition rule. Computers do it with about 256 doublings and additions, not d steps, so it’s quick even for a 78-digit d.

Some easy cases:

  • Key 1 gives P = G itself. That’s why the x-coordinate above appears so often in examples.
  • Key 2 gives a point with x = c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5.
  • Key 3 gives x = f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9.

The x-coordinates look unrelated, even though the keys are 1, 2 and 3. That’s the point: nothing about P hints at d. Finding d from P is the discrete logarithm problem, and no known method on ordinary computers solves it for a curve this size.

What formats are there?

A point is two 32-byte numbers. There are three ways to write one down, and Pearl uses all three in different places.

Key 1's public key (the point G) in each format
Field Size Type Description
uncompressed 65 04 ‖ x ‖ y 04 79be…1798 483a…d4b8. Old and wasteful. In Pearl only a legacy WIF flag can mean it.
compressed 33 02/03 ‖ x 02 79be…1798. Used in HD wallets: extended keys, fingerprints and the WIF "compressed" flag.
x-only 32 x 79be…1798. Everything on chain: Taproot output keys, signatures and keys inside scripts.
Size
32 bytes
Type
x-coordinate of a curve point
Format
Big-endian

Why can you throw away y?

Because the curve is symmetric. For each x on the curve, there are exactly two points, (x, y) and (x, p − y). One y is even and the other is odd.

  • A compressed key keeps one byte to say which: 02 for even y, 03 for odd y. Key 1 starts with 02; key 6 starts with 03 (03fff97bd5…297556).
  • An x-only key drops even that byte. By rule (BIP-340’s “lift_x”), it always means the point with even y.

That saves a byte everywhere a key appears on chain, and Taproot was designed around it.

What does a signature prove?

A Schnorr signature proves that whoever made it knew d for this public key and this exact message, without revealing anything about d.

It is 64 bytes: the x-coordinate of a random-looking point R, then a number s. The verifier computes a challenge e from R, P and the message m, and checks one equation:

s⋅G=R+e⋅P,e=TaggedHashBIP0340/challenge(Rx∥P∥m)s \cdot G = R + e \cdot P, \qquad e = \text{TaggedHash}_{\text{BIP0340/challenge}}(R_x \parallel P \parallel m)

Here is a signature made with Pearl’s own code, over the message SHA256(“Pearl”):

Value
Secret key 0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d
Public key P (x-only) d0de0aaeaefad02b8bdc8a01a1b8b11c696bd3d66a2c5f10780d95b7df42645c
Message m 72bc556112e9538a2433f0a9b87b032eae43c69e367e9dca1b176929dcf20ed5
R.x 85df688c0be1ff7cf23866497fda839fbb6009b820451ff3600caa2cf32dc490
s 80e136b1f442d43275a394cf4b007d7031e052499ad02984cea0bbd3179af2db

That signature used the auxiliary random value 00…01 from BIP-340’s recipe. Flip one bit of m and verification fails.

Schnorr signature

Sign with the private key, verify with the public key, then break it

P Public key (x-only)

41a72a7b46ce0ab003c631b38d980ade640612963d3e884949a16bf961e28456

m Message digest

bcd410d351ce06c9bdfb6e0cf733d2ba1eeb2854d49d542288096232b29523ac

σ Signature: R.x ‖ s

9af473ce79bba67a44d3a7833ae89e8301f05d1705e533b8887fd36cc2c8e560b9db803321cf0aab34993413b309f4895914117da4aba9dc27b07a90b1fea960

64 bytes: the x-coordinate of a one-time point R, then the number s. Pearl’s default signer picks R deterministically (RFC 6979), so the same key and message always give the same signature.

Flip one bit in the

Signature valid

The equation s·G = R + e·P holds, so whoever made this signature knew the private key for P, and the message is exactly this one.

How verification works

The verifier never sees the private key. It recomputes a challenge number e from R, P and the message, then checks one equation on the curve:

e = TaggedHash(“BIP0340/challenge”, R.x ‖ P.x ‖ m) mod n

R.x
9af473ce79bba67a44d3a7833ae89e8301f05d1705e533b8887fd36cc2c8e560
P.x
41a72a7b46ce0ab003c631b38d980ade640612963d3e884949a16bf961e28456
m
bcd410d351ce06c9bdfb6e0cf733d2ba1eeb2854d49d542288096232b29523ac
e
e5e183f0c353ca76e8e0e571ef42443a2b5a4002e7d8751fafee3ec41f237715
s
b9db803321cf0aab34993413b309f4895914117da4aba9dc27b07a90b1fea960

s·G = R + e·P

Only someone who knows d (P = d·G) could have chosen s = k + e·d so that this balances, and e can’t be known before R is fixed. Signing a different message or with a different key changes e, which is why every flip above fails.

Runs in your browser · nothing is sent src/lib/keys.ts · BIP-340, Pearl’s RFC 6979 signer

The Schnorr page derives the equation and shows why it can’t be forged.

Where do public keys appear on Pearl’s chain?

Mostly in disguise.

  • In Taproot outputs the 32 bytes are the tweaked output key Q, not your P. The Taproot tweak page explains why.
  • Inside scripts a key is pushed as 20 followed by 32 bytes. The real 2-of-2 P2MR spend in block 130,822 pushes two of them, b5b010c0…4d307d and 03b77fbc…c7ae89. (That second key’s first byte happens to be 03. In an x-only key that’s just data, not a parity flag.)
  • In HD wallets extended keys carry 33-byte compressed keys, and parent fingerprints hash them. These never go on chain.

The codex

Suggested

Up and down arrows move through the results, Enter opens one, Escape closes. Anywhere on the site, slash or Command K opens this search, and the square brackets [ and ] go to the previous and next page.