§ 06 · Script
OP_CHECKXMSSSIG
Pearl's post-quantum signature check, opcode 0xde
On this page 5 sections
OP_CHECKXMSSSIGOP_CHECKXMSSSIG: A Pearl-only opcode (0xde) that checks a post-quantum XMSS signature. It works only in tapscript, and the 2,340-byte signature is passed in five 468-byte pieces. is the one opcode Pearl added to Bitcoin’s Script. It checks an XMSSXMSS: A signature scheme built only from hash functions, believed to stay secure against quantum computers. Pearl's version allows 32 signatures per key, each 2,340 bytes. Using it is optional. signature: a signature built only from hash functions, which are believed to stay secure even against large quantum computers.
It’s an insurance policy. Schnorr signatures on secp256k1 protect every Pearl coin today, but a working quantum computer could break them. A Taproot address can carry an XMSS check as a hidden backup script, ready to use if that day comes (though the address’s Schnorr key path stays open too). The opcode exists and works; this page also covers how far Pearl’s wallet goes in using it.
What is XMSS, in short?
A one-time signature, multiplied. A Winternitz one-time key can sign exactly one message using nothing but hash chains. XMSS makes 32 of them (on Pearl) and ties their public keys together in a Merkle treeMerkle tree: A tree of hashes: hash the items in pairs, then hash those results in pairs, and so on up to one hash at the top. You can prove one item is in the tree with just a handful of hashes., whose root is the long-term public key. Each signature uses one leaf and includes the path that proves the leaf belongs to the tree.
The catch: it’s stateful. The signer must never use a leaf twice, and when all 32 are used, the key is spent. The XMSS page covers the scheme in depth.
- Hash function
- SHAKE256
- n = 32 bytes
- Winternitz w
- 16
- 67 hash chains per one-time key
- Tree height
- 5
- 32 signatures per key
- Public key
- 64 bytes
- Merkle root + public seed
- Signature
- 2,340 bytes
- 5 × 468 on the stack
- Opcode
- 0xde
- 222; tapscript only
Pearl’s code names the parameter set “XMSS-SHAKE256_5_256 (not in RFC 8391)”: the RFC’s standard sets have taller trees.
| Field | Size | Description |
|---|---|---|
| index | 4 | Which of the 32 one-time keys was used. |
| randomness r | 32 | Per-signature randomness mixed into the message hash. |
| WOTS signature | 67 × 32 | 2,144 bytes: one value from each of the 67 Winternitz hash chains. |
| authentication path | 5 × 32 | 160 bytes: the sibling hashes from that leaf up to the root. |
What does the opcode do?
- Size
- 1 byte (0xde)
- Type
- Tapscript opcode
- Format
- [sig1 sig2 sig3 sig4 sig5 pubkey] → [true]
-
Tapscript only
Anywhere else, it’s an invalid opcode. (On Pearl, every script that runs is tapscript anyway.) -
Pop the public key
The top item must be the 64-byte XMSS public key. -
Pop five pieces
The next five items are the signature, sig1 deepest. Each must be exactly 468 bytes. Any wrong length fails the script. -
Charge the budget
1,000 is taken from the input’s signature budget (50 plus the witness size). A Schnorr check costs 50. -
Compute the message
The BIP-342 tapscript sighash, always with the default sighash type: the same message an OP_CHECKSIG in this leaf would sign. -
Verify
Join the pieces and verify. Valid: push true. Invalid: the script fails immediately. Unlike OP_CHECKSIG, it never pushes false.
const numSigChunks = 5
const sigChunkSize = xmss.SignatureLen / numSigChunks // 468 bytes = 2340 / 5
…
// XMSS_SHAKE256_5_256 verification is capped at 67 * 16 + 5 hashes
if err := vm.tapscriptCtx.tallyOpCost(1000); err != nil {
return err
}
…
valid := xmss.Verify(pk, msg, sig)
if !valid {
str := "invalid XMSS signature"
return scriptError(ErrNullFail, str)
} The 520-byte limit on stack items is why the signature arrives in five pieces. Run de alone, with an empty stack, and Pearl’s engine fails trying to pop the key; in Bitcoin’s tapscript the same byte is OP_SUCCESS222 and the script would pass.
How does Pearl’s wallet use it?
Only when asked. getnewaddress … pq=true makes a quantum-safe address like this:
-
Two keys from one seed
The usual Schnorr key at m/86’/808276’/0’/branch/index, and a second key at m/222’/808276’/0’/branch/index. 222 is 0xDE, the opcode’s byte. -
XMSS seeds
The second key’s 32-byte private key goes through HKDF-SHA256 with the label “XMSS-SEED-EXPANSION”, giving a 64-byte private seed and a 32-byte public seed. XMSS key generation runs from those. -
One leaf
The leaf script is40‖ the 64-byte XMSS public key ‖de: 66 bytes. -
Tweak
The Schnorr key is tweaked with that leaf’s hash, exactly as on the Taproot tweak page.
The result is an ordinary prl1p… Taproot address, not a P2MR one. Here is the real example the research for this site produced with Oyster, from the public test phrase “abandon … about” (index 3):
| Value | |
|---|---|
| Schnorr internal key P (m/86’/808276’/0’/0/3) | a93c92e3e66d37d57328f72b9705a8d76abaa0aff97c4cd9a3bc147bc3894f1b |
| PQ key (m/222’/808276’/0’/0/3), HKDF input | c8d6edf779a83c7faef392035015191b634a85dc198c9e123f164872b86c2870 |
| XMSS public key (64 bytes) | 5e70da755554f576…ce2f0df31994e54b25ee8f8 |
| Leaf hash = tree root | 4d136994bb1cfe36b7b495d9bda4de40afe915112882d7b4730bf90fea75a91a |
| Output key Q | b9cacae9a3006236c709e93e003a649f38ac20966a00dd3de5a80c1acbf5837d |
| Address (pq=true) | prl1ph89v46drqp3rd3cfaylqqwnynuu2cgykdgqd60094qxp4jl4sd7ssg6v5j |
| Same P with no leaf, for comparison | prl1prmdvgxfgzmqzq5dj58enw6y7c55wpsfdpr0af7t79x65z6gmms8q4qa93j |
The leaf changes the tweak, so the address differs from the plain one, but nobody can tell from outside that it has a backup path.
What would an XMSS spend look like?
It would be a Taproot script-path spend with seven witness items: the five signature pieces, the 66-byte leaf, and a 33-byte control block (leaf version and parity, then P). Adding up the bytes:
That’s about 615 vbytes of witness, so a one-input, two-output XMSS spend would be roughly 750 vbytes, around five times a Schnorr payment. Its budget, 50 + 2,457 = 2,507, easily covers the 1,000-unit check. These are estimates from the rules; no such spend has been observed.
Can I use it today?
Partly, and it’s worth being precise:
- Consensus is ready. Any node running v1.5.2 verifies OP_CHECKXMSSSIG in any tapscript leaf, Taproot or P2MR.
- The wallet commits, but doesn’t sign. Oyster can create pq addresses, but a search of the v1.5.2 code found no
xmss.Signcaller outside tests. Coins at pq addresses are spent by the Schnorr key path, like any other. The XMSS leaf is an emergency exit that wallet software doesn’t yet open. - It’s opt-in and rare. The Pearl Desktop Wallet never asks for pq addresses, change addresses never include the leaf, and the default is plain BIP-86.
- Restoring is untested. Reading the code, a fresh restore from 12 words may not rescan pq addresses. See the note on the mnemonic page.