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_0…OP_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:
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:
- the unlocking data goes on the stack: the signature and the public key;
OP_DUPduplicates the key,OP_HASH160computes its hash;OP_EQUALVERIFYcompares that hash with the twenty bytes written in the lock: if they do not match, execution stops here and the spend is void;OP_CHECKSIGverifies 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.
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.
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.
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.
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.
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
3address 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
- BIP 11, M-of-N Standard Transactions: github.com/bitcoin/bips/blob/master/bip-0011.mediawiki
- BIP 16, Pay to Script Hash: github.com/bitcoin/bips/blob/master/bip-0016.mediawiki
- BIP 141, Segregated Witness (Consensus layer): github.com/bitcoin/bips/blob/master/bip-0141.mediawiki
- BIP 143, Transaction Signature Verification for Version 0 Witness Program: github.com/bitcoin/bips/blob/master/bip-0143.mediawiki
- BIP 173, Base32 address format for native v0-16 witness outputs (Bech32): github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
- BIP 340, Schnorr Signatures: github.com/bitcoin/bips/blob/master/bip-0340.mediawiki; BIP 341, Taproot: github.com/bitcoin/bips/blob/master/bip-0341.mediawiki; BIP 342, Tapscript: github.com/bitcoin/bips/blob/master/bip-0342.mediawiki
- BIP 350, Bech32m format for v1+ witness addresses: github.com/bitcoin/bips/blob/master/bip-0350.mediawiki
- BIP 360, Pay-to-Merkle-Root (P2MR) (draft): github.com/bitcoin/bips/blob/master/bip-0360.mediawiki; BIP 361, Post Quantum Migration and Legacy Signature Sunset (draft): github.com/bitcoin/bips/blob/master/bip-0361.mediawiki. The context is in the piece "Bitcoin and quantum computing".
- The Script language and the list of ops: the Script page of the Bitcoin wiki
and, for execution semantics, the Bitcoin Core source,
src/script/interpreter.cpp - A. M. Antonopoulos, D. A. Harding, Mastering Bitcoin (3rd ed., O'Reilly 2023), the chapters on transactions, Script and addresses; the text is also in a public repo
- The technical pages of learn me a bitcoin (G. Walker): byte-by-byte walkthroughs of scripts, transactions and encodings
- The numbers on how many coins have their key already in view are in the data piece.