§ 06 · Script · Overview
Script
The tiny language that locks every coin
On this page 7 sections
ScriptScript: The tiny programming language that sets the conditions for spending an output, such as “show a valid signature for this key”. is the small programming language that says what it takes to spend a coin. Every output carries a little program, and a spender has to supply data that makes it succeed.
It’s deliberately limited: no loops, no memory beyond a stack, a few hundred bytes at a time. That keeps every node’s job predictable. Pearl takes Bitcoin’s Script and narrows it further: one family of locks, Taproot, plus its keyless cousin P2MR and data outputs. This group goes through what’s left, opcode by opcode.
Try it
Step through a tiny script and watch the stack.
How does Script work?
With a stackStack: The pile of values a script works on. Each step pushes a value on top or takes values off the top, like a stack of plates. A spend succeeds if exactly one true value is left at the end.: a pile of byte strings where you can only touch the top. A script is a row of instructions, read left to right:
- Data pushes put bytes on top of the stack. A byte from
01to4bmeans “push the next that-many bytes”;OP_1toOP_16push small numbers. - OpcodesOpcode: One instruction in Script, written as a single byte, such as OP_CAT (0x7e). On Pearl, an unknown opcode makes the script fail. take items off the top, do something, and may push a result.
- At the end, the spend is valid if exactly one item is left and it’s true (not zero).
The script OP_2 OP_3 OP_ADD OP_5 OP_EQUAL is the five bytes 52 53 93 55 87:
-
52 OP_2
Stack: 2 -
53 OP_3
Stack: 2, 3 -
93 OP_ADD
Takes 3 and 2, pushes 5. Stack: 5 -
55 OP_5
Stack: 5, 5 -
87 OP_EQUAL
Takes both, pushes true because they match. Stack: 1. Success.
Real scripts check signatures instead of sums, but the mechanics are the same.
Which locking scripts can an output have?
On Bitcoin, any script at all can lock an output. On Pearl, exactly three shapes are valid, and the rule is consensus: a transaction with any other output is invalid, coinbase included.
| Locking script | Bytes | Type | Spent how |
|---|---|---|---|
OP_1 <32 bytes> |
51 20 ‖ 32 |
Taproot (P2TR) | One signature, or a script from its tree |
OP_2 <32 bytes> |
52 20 ‖ 32 |
P2MR | A script from its tree; no key path |
OP_RETURN … |
6a ‖ anything |
OP_RETURN | Never: a data output |
func GetScriptClass(script []byte) ScriptClass {
switch {
case isNullDataScript(script):
return NullDataTy
case isWitnessTaprootScript(script):
return WitnessV1TaprootTy
case isWitnessMerkleRootScript(script):
return WitnessV2MerkleRootTy
default:
return NonStandardTy
}
} Pearl’s consensus check rejects anything that comes back NonStandardTy. That one rule removes P2PK, P2PKH, P2SH, bare multisig, segwit v0, pay-to-anchor and every future witness version from the chain, before they ever exist.
Pearl’s code doesn’t explain the choice, but each kept shape has a clear role. Taproot is the general-purpose lock, cheap and private. P2MR is Taproot without the key path, for script trees with no elliptic-curve key on top. OP_RETURN carries data without adding to the set of spendable coins.
Where do scripts actually run?
Not in the locking script itself: that’s just a version byte and 32 bytes. The real scripts sit in a Merkle tree whose root those 32 bytes commit to (for Taproot, through the tweak). A script runs only when a spender reveals it, in a script-path spendScript-path spend: Spending a Taproot or P2MR output by revealing one of its committed scripts, the inputs that satisfy it and a control block that proves the script was committed to., and it runs under the rules of tapscriptTapscript: The version of Script used when spending through a Taproot or P2MR script tree. Pearl's extra opcodes, OP_CAT and OP_CHECKXMSSSIG, work only here. (BIP-342).
| Spend | What runs |
|---|---|
| Taproot key path | No script. One Schnorr signature is checked against the output key. |
| Taproot script path | The revealed leaf, as tapscript. |
| P2MR | The revealed leaf, as tapscript. (P2MR has no key path.) |
| OP_RETURN | Nothing: it can’t be spent. |
Pearl’s engine still contains Bitcoin’s old code path for running scriptSig and scriptPubKey together, but no output it could apply to can be created, so it’s unreachable.
What does a real script look like?
Here is one from mainnet: the leaf revealed by a P2MR spend in block 130,822. It’s a 2-of-2 multisig, 71 bytes:
00 OP_0
20 b5b010c0…ed4d307d push 32 bytes: key A
ba OP_CHECKSIGADD
20 03b77fbc…57c7ae89 push 32 bytes: key B
ba OP_CHECKSIGADD
52 OP_2
9c OP_NUMEQUAL The spender’s witness supplies two signatures, which start on the stack with signature A on top:
| Step | Stack after (top on the right) |
|---|---|
| start | sig B, sig A |
OP_0 |
sig B, sig A, 0 |
| push key A | sig B, sig A, 0, key A |
OP_CHECKSIGADD |
sig B, 1 (sig A is valid for key A, so the counter 0 becomes 1) |
| push key B | sig B, 1, key B |
OP_CHECKSIGADD |
2 (sig B is valid for key B, so 1 becomes 2) |
OP_2 |
2, 2 |
OP_NUMEQUAL |
1: exactly one true item, so success |
The P2MR page shows how the node checked that this leaf was the one committed in the output.
What’s different about Pearl’s tapscript?
Most of it is BIP-342 as Bitcoin runs it: minimal pushes, strict OP_IF arguments, items of at most 520 bytes, at most 1,000 on the stack, no 201-opcode limit, and a per-input signature budgetSigops budget: A per-input allowance of 50 plus the witness size. Each signature check spends 50, OP_CAT spends a little, OP_CHECKXMSSSIG spends 1,000. It is Pearl's only limit on signature work; there is no block sigops limit. of 50 plus the witness size. Then Pearl’s changes:
- OP_CAT works. Joining two items, disabled in Bitcoin since 2010, is enabled in Pearl’s tapscript.
- OP_CHECKXMSSSIG exists. Opcode
0xdechecks a post-quantum XMSS signature. Its own page. - No upgrade hooks. Bitcoin’s tapscript leaves unused opcodes as OP_SUCCESSOP_SUCCESS: In Bitcoin tapscript, unused opcodes that make a script succeed at once, kept for future upgrades. Pearl has none: an unknown opcode makes the script fail. (instant success), and treats unknown leaf versions and unknown key sizes as passing. Pearl turns all of these, and the OP_NOP1, OP_NOP4–10 opcodes, into failures.
- No activation history. Every rule is on from block 1; Pearl’s engine runs with no script flags at all.
Closing the hooks has a consequence, as we read the code: a new opcode can’t be slipped in by soft fork the way Bitcoin’s design allows. Changing script rules on Pearl means changing what every node accepts.
What’s in this group?
| Page | What you’ll learn |
|---|---|
| Opcodes | All 256 byte values: which work, which fail, and how Pearl differs. |
| P2TR | Taproot outputs: key path, script path and the control block. |
| P2MR | Pay-to-Merkle-Root, witness version 2: scripts only, and its sighash quirk. |
| OP_RETURN | Data outputs: how big, how many, and what’s on chain. |
| OP_CHECKXMSSSIG | Pearl’s post-quantum signature opcode and the wallet’s opt-in XMSS leaf. |
In this group
- 06.01 Opcodes Every byte value, and what it does on Pearl
- 06.02 P2TR One key in front, any number of scripts behind it
- 06.03 P2MR Taproot without the key: a Merkle root of scripts
- 06.04 OP_RETURN An output for data that can never be spent
- 06.05 OP_CHECKXMSSSIG Pearl's post-quantum signature check, opcode 0xde