Every coin on Bitcoin sits behind a lock: a small program, the scriptPubKey, that states under which conditions the coin can be spent. Spending means presenting what the lock asks for, normally a signature proving possession of the right key. The lock types are few, born one after another along the history of the protocol, and each answers the same question in its own way: how much of what locks the coin can be seen from the outside. Some locks place the public key in the clear on the output from the first instant; others expose only its hash, and the key shows only when the coin moves. That is what this piece looks at: what shape the locks have, how they changed, what tells one from another.

This text stays at the overview level: the chronology of the types, the tables that line them up, and the byte form of each lock. How a whole transaction is actually built and signed, its format piece by piece, is matter for a second, more technical pass. Readers arriving from the pieces on quantum computing and its measured numbers will find here the mechanism those pieces take as known.

A lock and an address are not the same thing

Best to clear this up right away, because it is the most common misunderstanding. The lock is the scriptPubKey, the script that sits on the output and fixes its spending conditions. The address is not the lock: it is just a compact way of writing it, an encoding designed to fit in a message, with an integrity check that keeps a mistyped character from slipping through. From an address the wallet derives the lock to put on the output; on the ledger there are no addresses, there are scripts.

Keeping the two apart explains several asymmetries. Some lock types have no address of their own (the oldest ones). Different encodings coexist at the same time, one per family of locks. And the very same address, reused to receive twice, produces two outputs with the same lock: when the first is spent, the key becomes public, and the second finds itself uncovered without anyone having touched it.

The language of locks

A lock is a program, and the language it is written in is called Script: a sequence of instructions, the ops (opcodes), executed on a stack of data. Whoever spends presents their unlocking data, normally a signature and a key, sometimes a whole script; nodes put that data on the stack and then run the lock: if "true" is left on the stack at the end, the spend is valid. That is all: there is no gatekeeper deciding, there is a program that every node in the world re-runs and that gives everyone the same outcome.

When a block reaches a node, the checks run in a precise order, from cheapest to most expensive: first the header and the proof-of-work, which cost one hash; then the shape of the block; then, for each transaction, that the coins being spent really exist and are not already spent; and only at the end are the locks executed, that is, the signatures verified. It is the step that costs the most, and it comes last on purpose: whoever sends garbage is stopped before making everyone pay for it. For many transactions, moreover, the node has already done that work when it saw them go by, and it remembers. The locks in this piece are that last link.

The allowed operations are few and concrete: putting data on the stack, duplicating and comparing it (OP_DUP, OP_EQUAL), computing its hash (OP_HASH160, OP_SHA256), verifying signatures (OP_CHECKSIG, OP_CHECKMULTISIG), opening a branch (OP_IF), binding the spend to time (OP_CHECKLOCKTIMEVERIFY, OP_CHECKSEQUENCEVERIFY). Loops are missing on purpose: a script cannot repeat itself, so every verification is certain to end and costs a predictable amount of time. In 2010, after some flaws were found, part of the ops were disabled (OP_CAT and others): the language has been narrower than it was born ever since.

The families, in a table; the locks of the chronology all live in the first five rows.

family main ops what they are for
constants and pushes OP_0OP_16, the direct pushes putting data on the stack (signatures, keys, hashes)
stack OP_DUP, OP_DROP, OP_SWAP duplicating and reordering what sits on top
comparison OP_EQUAL, OP_EQUALVERIFY comparing the two elements on top
hash OP_HASH160, OP_SHA256 computing hashes
signatures OP_CHECKSIG, OP_CHECKMULTISIG (in tapscript: OP_CHECKSIGADD) verifying signatures against keys
flow OP_IF / OP_ELSE / OP_ENDIF, OP_VERIFY, OP_RETURN branches, halts, outputs declared unspendable
time OP_CHECKLOCKTIMEVERIFY, OP_CHECKSEQUENCEVERIFY binding the spend to a height or a delay
arithmetic (reduced) OP_ADD, OP_SUB, the numeric comparisons small sums on small numbers
disabled (2010) OP_CAT, OP_MUL, OP_LSHIFT removed after the flaws; some are being discussed again today

Best seen on a real lock. This is P2PKH, the lock of addresses starting with 1, taken from the most famous of payments (the ten thousand pizza bitcoins, 2010), byte by byte:

P2PKH1… · since 2009 25 bytes 1 OP_DUP = 76 76 OP_DUP 1 OP_HASH160 = a9 a9 OP_HASH160 1 push 20 = 14 14 push 20 20 key hash · 20 bytes = 46af3fb481837fadbb421727f9959c2d32a36829 46 … 29 key hash 1 OP_EQUALVERIFY = 88 88 OP_EQUALVERIFY 1 OP_CHECKSIG = ac ac OP_CHECKSIG the 10,000 BTC output of the pizza transaction · tx a1075db5…, block 57043 (2010)
The P2PKH lock byte by byte: the scriptPubKey of output 0 (10,000 BTC) of a real transaction, the 10,000 BTC output of the pizza transaction, block 57043 (2010). Boxes: blue = ops, yellow = data, grey = length; the numbers above the boxes are their byte counts. Verifiable with getrawtransaction a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d.

Each box is a byte, or a group of bytes, with its hexadecimal value inside and the byte count above. Blue boxes are ops, the instructions; yellow ones are data; grey ones are a special kind of byte, the push, worth pausing on because it returns in every lock. A push byte orders nothing: it declares "the next N bytes are data, put them on the stack as they are". The grey 14, for instance, is the number twenty, and announces that twenty bytes follow, the hash of the key. This is how the script separates data from instructions without ambiguity: a value is never mistaken for a command, because its push always introduces it.

Before following an execution, it is worth making clear who brings what, because not everything comes from whoever spends.

The node already has the lock. Whoever spends does not send it: it is written on the output of an earlier transaction, normally confirmed some time before, and every node keeps it in the UTXO set, the collection of all coins spendable at that moment. The spending transaction merely points at it, saying which past transaction and which of its outputs. It could not be otherwise: if the lock came from whoever spends, everyone would bring their own.

Only one thing arrives from outside, the unlocking data: it travels inside the spending transaction, in the scriptSig, and from SegWit onwards in a separate area of the transaction, the witness.

And the message, the text the signature is verified against, does not travel at all. It is the spending transaction reduced to a number by fixed rules, rules that leave the unlocking data out: if they included it, the signature would have to cover itself. Whoever signs computes it before signing, the node recomputes it on its own from what it received, and the two numbers match only if the transaction has stayed the same. Nobody transmits it because nobody needs to receive it: both sides have the transaction.

One thing stays outside all this, the private key, which appears neither in the lock, nor in the unlocking data, nor in the message.

How much whoever spends has to bring depends on the type of lock: where there is a hash the key must be shown, because the node does not have it; where the key is already there, the signature is enough.

Ops do not "fire" on call: the script is walked left to right, one element at a time, and each op acts when its turn comes, on what the previous steps left on the stack. First, though, the unlocking data is stacked: whoever spends puts their signature and their key up front, and only then does the lock run. Unlock first, lock after, on the same stack. In the P2PKH above the order is this:

  1. the unlocking data goes on the stack: the signature and the public key;
  2. OP_DUP duplicates the key, OP_HASH160 computes its hash;
  3. OP_EQUALVERIFY compares that hash with the twenty bytes written in the lock: if they do not match, execution stops here and the spend is void;
  4. OP_CHECKSIG verifies the signature against the key: if it holds, it leaves "true" on the stack and the spend is valid.

These are the four ops and the single push that can be read in the strip, executed in that order.

Step 4 deserves a closer look, because that is where the mechanism closes. OP_CHECKSIG does not compare two values: it rebuilds the text the signature was meant to cover, that is, the transaction reduced to a number by fixed rules, and then verifies that a certain relation holds between that number, the public key and the two numbers of the signature. The relation holds only if the signer possessed the private key, and holds only for that transaction: the same signature, moved onto another one, would not. Which parts of the transaction enter the count is declared by the signature itself, with one byte at its tail.

What the check establishes stops there, though: that the signer possessed the key at that moment, and consented to that spend. Not who they were, not whether the key was theirs alone, not whether it had been taken from them. On Bitcoin ownership is defined exactly like this, as possession of the key, and the lock has no way of looking further.

Then there is a limit of the same kind: OP_CHECKSIG judges the signature it receives, not the way it was produced. That side has conditions of its own too, all on the signer's side: the most delicate one concerns the nonce, the number every signature consumes exactly once and that must never repeat.

One question about the language remains, and it carries all that follows: if it allows arbitrary combinations, why are the lock types a handful? Because nodes, when deciding whether to relay a transaction, recognize a few fixed shapes, the standard scripts: outside those shapes a lock can be valid under consensus rules yet travel poorly on the network. The types in the next table are precisely those shapes, and their chronology is the story of how the list grew longer. Over time where the unlocking data lives has changed as well (inside the body of the transaction, then in a section of its own: more under SegWit), but the stack mechanism has stayed the same.

A handful of types

Before the chronology, the overview. This table is for consulting, not studying: it pays to come back to it while reading the sections that follow.

lock shape (schematic) address when the key shows
P2PK <key> OP_CHECKSIG none at once: the key is already on the output
P2PKH OP_DUP OP_HASH160 <key hash> OP_EQUALVERIFY OP_CHECKSIG 1… at the first spend
bare multisig m <keys…> n OP_CHECKMULTISIG none at once: the keys are on the output
P2SH OP_HASH160 <script hash> OP_EQUAL 3… at the spend, when the script is revealed
P2WPKH OP_0 <key hash, 20 bytes> (witness v0) bc1q… (short) at the first spend
P2WSH OP_0 <script hash, 32 bytes> (witness v0) bc1q… (long) at the spend, when the script is revealed
P2TR OP_1 <32-byte key> (witness v1) bc1p… at once: the (tweaked) key is on the output; the script tree only if used

The acronyms starting with P2 all read the same way: pay to, and the rest says what is being paid to, a public key (PK), the hash of a key (PKH), the hash of a script (SH), with the W of witness for the segwit forms and the TR of Taproot.

Outside the table remain the non-standard scripts and the shapes that are not locks to be spent (for instance OP_RETURN outputs, which carry data and are known in advance to have no redeemer). The useful distinction is between the types that put the key in view by construction (P2PK, bare multisig, and in part P2TR) and those that keep it behind a hash until the spend arrives (the hash families: P2PKH, P2SH, P2WPKH, P2WSH).

The chronology: how they were born, one after another

P2PK, the origins (2009)

The first lock is also the most direct: the public key in the clear on the script, followed by the order to verify the signature. It is the form of the early coinbase outputs and of the first payment ever made, from Satoshi to Hal Finney (block 170, January 2009). It has no address: the key itself was handled directly. Simple, and for that reason exposed: the key sits on the ledger from the first moment, with no shield at all.

P2PKno address · since 2009 67 bytes 1 push 65 = 41 41 push 65 65 public key · 65 bytes = 04ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84c 04 ae 1a 62 fe 09 … d8 4c public key 1 OP_CHECKSIG = ac ac OP_CHECKSIG the first payment in history (Satoshi → Hal Finney) · tx f4184fc5…, block 170 (2009)
The P2PK lock byte by byte: the scriptPubKey of output 0 (10 BTC) of a real transaction, the first payment in history (Satoshi → Hal Finney), block 170 (2009). Boxes: blue = ops, yellow = data, grey = length; the numbers above the boxes are their byte counts. Verifiable with getrawtransaction f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16.

P2PKH, the classic address (2009)

Soon after comes the form that held for years as the de facto standard, that of addresses starting with 1. The lock does not carry the key but its hash, twenty bytes; the actual key shows only at spend time, inside the data that unlocks the output. Two advantages: a shorter address to pass around, and a key that stays covered as long as the coin does not move. From here on "address" becomes, in common use, a synonym of this encoded hash. Its byte strip is the one in the language section: four ops and one push around the twenty-byte hash.

bare multisig, raw multiple signatures (from the start, standard in 2011)

The operation for requiring several signatures, m valid signatures out of n listed keys, has been in the protocol from the beginning; as a shape recognized by relay rules it arrives in 2011 (BIP 11). In the "bare" version the keys sit whole on the output's script, in the clear. It works, but it takes up space and exposes the keys by construction; with the arrival of a cleaner solution (below) it falls out of use as a direct form, and survives mostly inside other locks.

bare multisigno address · since 2011 135 bytes 1 OP_1 = 51 51 OP_1 1 push 65 = 41 41 push 65 65 key 1 · 65 bytes = 04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 04 cc 71 eb 30 d6 … 8a c4 key 1 1 push 65 = 41 41 push 65 65 key 2 · 65 bytes = 0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 04 61 cb dc c5 40 … 42 af key 2 1 OP_2 = 52 52 OP_2 1 OP_CHECKMULTISIG = ae ae OP_CHECKMULTISIG 1-of-2: two keys in the clear, one signature suffices · tx 60a20bd9…, block 164467 (2012)
The bare multisig lock byte by byte: the scriptPubKey of output 0 (0.01 BTC) of a real transaction, 1-of-2: two keys in the clear, one signature suffices, block 164467 (2012). Boxes: blue = ops, yellow = data, grey = length; the numbers above the boxes are their byte counts. Verifiable with getrawtransaction 60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1.

In the strip one reads the two whole keys, one after the other, each introduced by its push. Framing them are the two numbers of the rule: OP_1 at the head, the signatures required, and OP_2 at the tail, the keys listed, before OP_CHECKMULTISIG. No hash, everything in the clear.

P2SH, the commitment to a script (2012, BIP 16)

The step that generalizes the hash idea. Instead of committing to a single key, the lock commits to the hash of a whole script, the redeem script, which at spend time must be revealed and satisfied. Addresses starting with 3 are born. With P2SH multiple signatures become convenient: the payer sees only a short address, and the complexity (how many keys, what conditions) stays hidden behind the hash until it is needed. From here on multisig is almost always used like this, inside P2SH, and no longer bare.

P2SH3… · since 2012 23 bytes 1 OP_HASH160 = a9 a9 OP_HASH160 1 push 20 = 14 14 push 20 20 redeem script hash · 20 bytes = e9c3dd0c07aac76179ebc76a6c78d4d67c6c160a e9 … 0a redeem script hash 1 OP_EQUAL = 87 87 OP_EQUAL a payment to address 3P14159… · tx 40eee3ae…, block 272295 (2013)
The P2SH lock byte by byte: the scriptPubKey of output 0 (0.0099 BTC) of a real transaction, a payment to address 3P14159…, block 272295 (2013). Boxes: blue = ops, yellow = data, grey = length; the numbers above the boxes are their byte counts. Verifiable with getrawtransaction 40eee3ae1760e3a8532263678cdf64569e6ad06abc133af64f735e52562bccc8.

Twenty-three bytes in all: OP_HASH160, the twenty bytes of the script hash, OP_EQUAL. Whether behind it there is a single key or a complicated multisig, from the outside the lock is identical.

SegWit v0, the separated witness (2017, BIP 141/143)

A structural change. The data that unlocks the output (signature and key, or script and signatures) leaves the body of the transaction and moves to a section of its own, the witness. Two new types mirror the earlier ones: P2WPKH is the counterpart of P2PKH (hash of a key), P2WSH that of P2SH (hash of a script), with a more robust hash for scripts. The address encoding changes too, moving to bc1q. The separation of the witness fixes a historical defect (the possibility of altering a transaction's identifier before confirmation) and opens the way to upper layers such as Lightning.

P2WPKHbc1q… · since 2017 22 bytes 1 OP_0 = 00 00 OP_0 1 push 20 = 14 14 push 20 20 key hash · 20 bytes = 8d7a0a3461e3891723e5fdf8129caa0075060cff 8d … ff key hash in the SegWit activation block · tx dfcec48b…, block 481824 (2017)
The P2WPKH lock byte by byte: the scriptPubKey of output 0 (0.001943 BTC) of a real transaction, in the SegWit activation block, block 481824 (2017). Boxes: blue = ops, yellow = data, grey = length; the numbers above the boxes are their byte counts. Verifiable with getrawtransaction dfcec48bb8491856c353306ab5febeb7e99e4d783eedf3de98f3ee0812b92bad.
P2WSHbc1q… · since 2017 34 bytes 1 OP_0 = 00 00 OP_0 1 push 32 = 20 20 push 32 32 witness script hash · 32 bytes = cdbf909e935c855d3e8d1b61aeb9c5e3c03ae8021b286839b1a72f2e48fdba70 cd … ba 70 witness script hash in the SegWit activation block · tx 461e8a4a…, block 481824 (2017)
The P2WSH lock byte by byte: the scriptPubKey of output 0 (0.005 BTC) of a real transaction, in the SegWit activation block, block 481824 (2017). Boxes: blue = ops, yellow = data, grey = length; the numbers above the boxes are their byte counts. Verifiable with getrawtransaction 461e8a4aa0a0e75c06602c505bd7aa06e7116ba5cd98fd6e046e8cbeb00379d6.

The two strips state the simplification at a glance: OP_0 followed by one push and one hash, and nothing else. Twenty bytes for the key (P2WPKH), thirty-two for the script (P2WSH): the only visible difference between the two is the length of the hash.

For the transition there is a hybrid form, segwit nested inside P2SH: it keeps a 3 address compatible with old wallets, but inside it works as segwit. It is a bridge, not a new type.

Taproot, the key and the tree (2021, BIP 340/341/342)

The latest active family. It introduces a different kind of signature, cleaner to verify and to combine (Schnorr), and a lock, P2TR, that holds two spending paths together: an internal key, for the simple and common case, and a hidden script tree, which stays invisible until used. Whoever spends by the key path shows very little, and complex spends become indistinguishable from simple ones. Addresses start with bc1p, with an encoding revised from segwit v0. On the exposure front, Taproot puts the key in view by construction, like P2PK: on the output there is no hash but the key itself, adjusted by a small tweak that commits to the script tree. It is a trait the quantum computing pieces weigh in their sums.

P2TRbc1p… · since 2021 34 bytes 1 OP_1 = 51 51 OP_1 1 push 32 = 20 20 push 32 32 tweaked key · 32 bytes = 667bdd93c7c029767fd516d2ea292624b938fefefa175ac9f1220cf508963ff3 66 … 3f f3 tweaked key in the Taproot activation block · tx 777c9986…, block 709632 (2021)
The P2TR lock byte by byte: the scriptPubKey of output 0 (0.0003 BTC) of a real transaction, in the Taproot activation block, block 709632 (2021). Boxes: blue = ops, yellow = data, grey = length; the numbers above the boxes are their byte counts. Verifiable with getrawtransaction 777c998695de4b7ecec54c058c73b2cab71184cf1655840935cd9388923dc288.

The strip resembles P2WSH's, OP_1 in place of OP_0 and thirty-two bytes, but those thirty-two bytes are not a hash: they are the key, in the clear.

The quantum-resistant proposals (under discussion)

The open line. Facing the prospect of a machine able to derive the private key from the public one, locks of a new kind are being studied, based on signatures believed to resist that kind of attack (the draft furthest along is BIP 360, today Pay-to-Merkle-Root), together with proposals to retire legacy signatures over time (BIP 361). They are proposals, not active parts of the protocol. The why of all this, and how much it would weigh, is in the piece on quantum computing; here it is enough to mark its place in the row: the next step, if and when it comes.

The address encodings

Parallel to the chronology of locks runs that of encodings, the way an address is written. They are few and go with the families.

encoding locks addresses notes
Base58Check P2PKH, P2SH 1…, 3… alphabet without ambiguous characters, checksum at the tail
Bech32 P2WPKH, P2WSH (witness v0) bc1q… a single character case, convenient in QR codes, error detection guaranteed by the checksum
Bech32m P2TR (witness v1) bc1p… a variant of Bech32 corrected for witness versions 1 and up

Pairs that get confused

A few distinctions that, held firm, dissolve most of the confusion:

  • lock ≠ address: the scriptPubKey on the output is the lock; the address is just a readable encoding of it;
  • key ≠ key hash: the hash types (P2PKH, P2WPKH) do not show the key until the spend, the ones without (P2PK, the key path of P2TR) show it at once;
  • signature nonce ≠ block nonce: the same word sits in two places that have nothing to do with each other; the mining one is public and is searched for by trial, the signature one is secret and is not to be searched for at all;
  • repeated nonce ≠ weak key: the key can be perfect, it is the signature that handed it over;
  • nested segwit ≠ native segwit: a 3 address can be any P2SH or a segwit wrapped in P2SH for compatibility; from the outside they look alike, inside they do not;
  • valid ≠ standard: the language accepts many more shapes than nodes relay; the types in this piece are the standard shapes, not everything possible.

In short

Bitcoin's locks are few and read in historical order: the bare key of the origins (P2PK), the hash of a key that becomes the standard (P2PKH), multiple keys in the clear (bare multisig), the commitment to a whole script (P2SH), the separation of the witness (P2WPKH and P2WSH), the union of key and script tree (P2TR), and the still open line of proposals designed for a world with quantum computers. The movement is not a single line: what it takes to spend first sits in the clear on the output, then retreats behind a hash and shows only at the spend, and with Taproot comes back into view, for reasons that have nothing to do with keeping the key private. Under every type the same practical question remains: whether and when the public key ends up in the clear on the ledger.

References