Post-Quantum Cryptography
BTX post-quantum signature algorithms, P2MR output construction, Merkle-tree spend paths, address format, and key generation.
Overview
BTX uses NIST-standardized post-quantum signature algorithms as its primary cryptographic primitives. All transaction outputs use P2MR (Pay-to-Merkle-Root), a witness version 2 output type that commits to a Merkle tree of spend paths. Each leaf in the tree can use a different signature algorithm, enabling flexible security policies including primary/recovery key separation and threshold multisig with mixed algorithm types.
Signature Algorithms
| Algorithm | Standard | Public Key | Secret Key | Signature | Sigop Weight |
|---|---|---|---|---|---|
| ML-DSA-44 | FIPS 204 | 1,312 bytes | 2,560 bytes | 2,420 bytes | 500 |
| SLH-DSA-SHAKE-128s | FIPS 205 | 32 bytes | 64 bytes | 7,856 bytes | 5,000 |
ML-DSA-44 (lattice-based) is the primary signature scheme, offering compact public keys relative to its security level and fast verification. SLH-DSA-SHAKE-128s (hash-based) serves as the backup/recovery algorithm, providing a conservative security assumption that depends only on hash function security rather than lattice hardness.
P2MR Output Construction
Output Script
A P2MR output is a standard witness v2 script:
OP_2 <32-byte-merkle-root> The 32-byte witness program is the root of a Merkle tree whose leaves are individual spend-path scripts.
Merkle Tree
- Leaf hash:
P2MRLeaf(leaf_version || script) - Branch hash:
P2MRBranch(sort(h1, h2)) - Sibling hashes are sorted before branching, ensuring canonical tree construction.
Control Block
The witness stack for spending includes a control block containing:
- 1-byte leaf version (
0xc2masked with0xfe) - Zero or more 32-byte sibling hashes for the Merkle proof
Leaf Script Types
ML-DSA Single-Sig Leaf
<1312-byte-pubkey> OP_CHECKSIG_MLDSA SLH-DSA Single-Sig Leaf
<32-byte-pubkey> OP_CHECKSIG_SLHDSA Multisig Leaf (m-of-n)
OP_CHECKSIG_{algo1}
OP_CHECKSIGADD_{algo2}
...
OP_CHECKSIGADD_{algon}
OP_NUMEQUAL
Algorithms are per-key and may mix ML-DSA and SLH-DSA within a single leaf.
Maximum 8 public keys per multisig leaf (MAX_PQ_PUBKEYS_PER_MULTISIG).
Leaf scripts may be up to 10,000 bytes (MAX_P2MR_SCRIPT_SIZE).
Default Leaf Roles
A typical P2MR tree contains leaves for distinct security purposes:
| Role | Leaf Type | Purpose |
|---|---|---|
| Primary | ML-DSA single-sig | Day-to-day spending with fast verification |
| Recovery | SLH-DSA single-sig | Backup path using hash-based signatures (conservative assumption) |
| CTV | OP_CHECKTEMPLATEVERIFY | Pre-committed transaction templates (vaults, covenants) |
| CSFS | OP_CHECKSIGFROMSTACK | Delegation and oracle-based spending conditions |
| Threshold | m-of-n multisig | Organizational custody with mixed PQ algorithms |
PQ Opcodes
| Opcode | Value | Stack Effect | Context |
|---|---|---|---|
OP_CHECKSIG_MLDSA | — | Pops sig + 1312-byte pubkey; pushes success | P2MR only |
OP_CHECKSIG_SLHDSA | — | Pops sig + 32-byte pubkey; pushes success | P2MR only |
OP_CHECKSIGADD_MLDSA | 0xbe | (sig n pubkey → n+success) | P2MR only |
OP_CHECKSIGADD_SLHDSA | 0xbf | (sig n pubkey → n+success) | P2MR only |
All PQ opcodes are valid only under SigVersion::P2MR. Empty signatures
skip verification and charge no validation weight. Non-empty failing signatures
are rejected under SCRIPT_VERIFY_NULLFAIL.
Sighash Construction
P2MR uses a BIP341-style digest structure with a distinct epoch byte for witness v2 script execution. The signed message is a 32-byte digest computed over:
- Transaction context (version, locktime, inputs, outputs)
- Leaf commitment context (leaf version, script, control block path)
Address Format
P2MR addresses use Bech32m encoding with witness version 2:
| Property | Value |
|---|---|
| Encoding | Bech32m (BIP350) |
| Witness version | 2 |
| Mainnet prefix | btx1z... |
| Witness program | 32 bytes (Merkle root) |
Key Generation and Derivation
HD Derivation
BTX wallet derivation uses purpose 87h for P2MR descriptors,
following the path family m/87h/.... This provides deterministic
PQ key derivation compatible with external signer workflows.
Descriptor Grammar
mr(...)— P2MR descriptor rootpk_slh(...)— SLH-DSA backup leafmr(multi_pq(m,key1,key2,...))— threshold multisigmr(sortedmulti_pq(m,key1,key2,...))— sorted-key multisig
In descriptors, bare hex denotes an ML-DSA pubkey (1,312 bytes).
SLH-DSA pubkeys use the pk_slh(hex) wrapper (32 bytes).
sortedmulti_pq sorts keys by raw bytes before script construction.
Miniscript Support
The MiniscriptContext::P2MR context supports PQ-specific fragments:
pk_mldsa(KEY)/pk_slhdsa(KEY)multi_mldsa(k, KEY, ...)/multi_slhdsa(k, KEY, ...)
PQ fragments are rejected in non-P2MR contexts via context gating.
PSBT Profile
- Selected P2MR leaf script and control block are carried in input metadata.
- Partial PQ signatures are keyed by
(leaf_hash, pubkey). - Combine/finalize merges signer-contributed partial signatures and finalizes only when the threshold is met for the selected multisig leaf.
Constant-Time Requirements
- Secret key buffers are zeroized on clear/destruction.
- Sensitive comparisons use constant-time primitives.
- Signature and verification timing does not depend on secret key material.
Forward Compatibility
- P2MR supports
OP_SUCCESS-style upgrade behavior through P2MR-specific checks. - Defined PQ opcodes are explicitly excluded from unconditional success handling.
- P2MR annex data is parsed at consensus and remains non-standard under relay policy by default.