§ 06

§ 06 · Script · Overview

Script

The tiny language that locks every coin

Script 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.

Script runner

Step through a tapscript and watch the stack

Script · 5 steps

  1. OP_2
  2. OP_3
  3. OP_ADD
  4. OP_5
  5. OP_EQUAL

Ready. The witness items are already on the stack; press Step.

Stack · top first

  1. empty

Teaching subset Push, stack, arithmetic, comparison, hashing, OP_CAT and the signature opcodes, run the way tapscript runs them. Signatures are mocked: “signs” is a stand-in, not a real BIP-340 check. Real scripts run inside a Taproot or P2MR spend.

Runs in your browser · nothing is sent src/lib/script.ts · hash.ts

How does Script work?

With a stack: 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 01 to 4b means “push the next that-many bytes”; OP_1 to OP_16 push small numbers.
  • Opcodes 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:

  1. 52 OP_2

    Stack: 2
  2. 53 OP_3

    Stack: 2, 3
  3. 93 OP_ADD

    Takes 3 and 2, pushes 5. Stack: 5
  4. 55 OP_5

    Stack: 5, 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
node/txscript/standard.go Go
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 spend, and it runs under the rules of tapscript (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 budget 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 0xde checks a post-quantum XMSS signature. Its own page.
  • No upgrade hooks. Bitcoin’s tapscript leaves unused opcodes as OP_SUCCESS (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.
Topic Bitcoin Pearl Explained in
Valid output scripts Any script Three shapes: P2TR, P2MR, OP_RETURN
Where scripts run Legacy, P2SH, segwit v0, tapscript Tapscript only
OP_CAT Disabled (OP_SUCCESS in tapscript) Enabled in tapscript
Post-quantum check None OP_CHECKXMSSSIG (0xde)
Unknown opcodes OP_SUCCESS: script succeeds Script fails
Witness version 2 Undefined (BIP-360 is a draft) P2MR, active

In this group

  1. 06.01 Opcodes Every byte value, and what it does on Pearl
  2. 06.02 P2TR One key in front, any number of scripts behind it
  3. 06.03 P2MR Taproot without the key: a Merkle root of scripts
  4. 06.04 OP_RETURN An output for data that can never be spent
  5. 06.05 OP_CHECKXMSSSIG Pearl's post-quantum signature check, opcode 0xde

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.