Research/Bitcoin

Miniscript: Composable and Analyzable Bitcoin Scripts

How Miniscript makes Bitcoin scripts safer to write, analyze, and compose for wallets and custody solutions.

bcTanjiFeb 27, 2026

Bitcoin's scripting system is powerful enough to encode complex spending conditions, but raw Bitcoin Script is notoriously difficult to work with. Writing a script that enforces a 2-of-3 multisig with a timelock fallback to a single recovery key is straightforward to describe in English but surprisingly error-prone to implement correctly. Miniscript solves this by introducing a structured, analyzable representation layer between human intent and the raw opcodes that Bitcoin nodes execute.

Developed by Pieter Wuille, Andrew Poelstra, and Sanket Kanjalkar, Miniscript provides a formal framework for writing, analyzing, and composing Bitcoin spending conditions. It has been adopted by Bitcoin Core, the Bitcoin Dev Kit (BDK), and several hardware signing devices, making it the emerging standard for constructing non-trivial scripts in production wallets.

The Problem with Raw Bitcoin Script

Bitcoin Script is a stack-based language with roughly 100 opcodes. It is intentionally limited: no loops, no recursion, no floating-point arithmetic. These constraints make it safe for consensus validation, but they also make it unintuitive for developers. The same spending policy can be expressed in many different Script encodings, each with different size, fee cost, and security properties.

Consider a simple corporate treasury policy: two executives must sign together, or after 90 days, the CFO alone can spend. In raw Script, you need to manually arrange opcodes like OP_IF, OP_CHECKMULTISIG, OP_CHECKSEQUENCEVERIFY, and OP_CHECKSIG in the correct order, ensuring the stack is clean at every branch. A single misplaced opcode can make the script unspendable or, worse, spendable by anyone.

Three problems emerge from writing raw Script:

  • Analysis difficulty: given an arbitrary Script, it is computationally hard to determine all the conditions under which it can be spent
  • Composition risk: combining two correct scripts into a single script can produce an incorrect result because of stack interactions
  • Wallet incompatibility: a wallet that does not understand a script cannot sign for it, estimate fees, or determine whether it can contribute to spending
Why this matters for wallets: If a wallet cannot analyze a script, it cannot determine the maximum witness size, which means it cannot accurately estimate transaction fees. This is not a theoretical concern: incorrect fee estimation has caused stuck transactions and overpayment in production systems.

Miniscript's Three Layers

Miniscript introduces a clean separation between what you want (policy), how it maps to a structured representation (miniscript), and the actual bytes that hit the blockchain (script). Each layer has a distinct role and formal properties.

Layer 1: Policy Language

The policy language is the human-readable description of spending conditions. It uses a simple, composable syntax:

  • pk(KEY): require a signature from KEY
  • older(N): require N blocks of relative timelock (CSV)
  • after(N): require absolute timelock (CLTV)
  • sha256(H): require a preimage for hash H
  • and(X,Y): require both X and Y
  • or(X,Y): require either X or Y
  • thresh(k,X,Y,...): require k-of-n sub-policies

The corporate treasury example from above becomes a single line:

or(and(pk(exec_A),pk(exec_B)),and(pk(cfo),older(12960)))

This reads naturally: either both executives sign, or the CFO signs after approximately 90 days (12,960 blocks at 10 minutes per block). The policy language is not consensus-critical: it exists only as input to the compiler.

Layer 2: Miniscript

The compiler transforms a policy into a miniscript expression. Miniscript is a subset of Bitcoin Script where every valid expression maps one-to-one to Script opcodes, and every expression has formally provable properties. The key insight is the type system: each miniscript fragment carries a type that describes what it puts on the stack and under what conditions.

The type system uses four basic types:

TypeStack BehaviorDescription
B (Base)Pushes 0 or 1The top-level type: consumes input and pushes a boolean result
V (Verify)Pushes nothing or abortsLike B but uses VERIFY semantics: either succeeds silently or fails the script
K (Key)Pushes a keyPushes a public key onto the stack, typically consumed by a CHECKSIG
W (Wrapped)Pushes 0 or 1 with prefixLike B but prefixed with OP_SWAP so it can be used in multi-argument contexts

Beyond the four base types, miniscript fragments carry modifier flags (such as z, o, n, d, u) that encode additional guarantees about stack consumption, non-zero results, and dissatisfaction behavior. These flags enable the compiler to verify that fragment composition is safe at compile time, eliminating an entire class of Script bugs.

Layer 3: Bitcoin Script

Each miniscript expression maps deterministically to a sequence of Bitcoin Script opcodes. This mapping is bijective for the supported fragment set: you can decode any valid miniscript back from raw Script. This property is what makes wallet analysis possible. A wallet that understands miniscript can take an arbitrary output script, decode it to miniscript, and then reason about spending conditions without understanding the original developer's intent.

Taproot and Miniscript: Taproot extends Miniscript's power significantly. With Taproot's script tree structure, each leaf in the MAST (Merkelized Alternative Script Tree) can contain a different miniscript expression, and only the executed branch is revealed on-chain. This provides both privacy and fee savings for complex policies.

Compilation: From Policy to Script

The Miniscript compiler does more than translate: it optimizes. For a given policy, there may be dozens of valid Script encodings. The compiler explores the search space and selects the encoding that minimizes witness size (and therefore transaction fees) for the most likely spending path.

Consider the probabilities in our corporate treasury example. If 95% of transactions use the two-executive path and the timelock fallback is rarely exercised, the compiler can place the common path in a position that requires a smaller witness. With Taproot, the compiler can assign the common path to the key-spend path (a single Schnorr signature) and relegate the fallback to a script-spend leaf.

Compilation Steps

  1. Parse the policy string into an abstract syntax tree
  2. Enumerate possible miniscript representations for each sub-policy
  3. Score each representation by witness size, weighted by spending probability
  4. Select the optimal composition and emit the final miniscript
  5. Encode the miniscript to raw Bitcoin Script opcodes

The output can be expressed as a standard output descriptor, which is the format Bitcoin Core and other wallets use to track addresses. For example, a descriptor like wsh(or_d(pk(A),and_v(v:pk(B),older(1000)))) tells a wallet everything it needs to derive addresses, estimate fees, and construct valid spending transactions.

Automatic Script Analysis

One of Miniscript's most valuable properties is that given any valid miniscript, a wallet can automatically determine:

  • All possible spending paths and the keys, hashes, or timelocks each requires
  • The maximum witness size for any spending path (critical for fee estimation)
  • Whether a specific key can contribute to spending under current conditions
  • Whether any spending path is currently available given the blockchain state (block height, sequence)

This analysis is performed structurally by walking the miniscript tree. Each node's type annotation provides the information needed to compute satisfaction and dissatisfaction witnesses at every branch. No Script simulation or symbolic execution is required.

For PSBTs (Partially Signed Bitcoin Transactions), this analysis is transformative. A hardware signing device can parse a PSBT, decode the spending script to miniscript, determine whether it holds a key that participates in a valid spending path, and sign only if the conditions are met. Without miniscript, the device would have to either blindly trust the host software or implement a custom parser for every possible script template.

Wallet Implementations

Miniscript support has been integrated into the two most important open-source Bitcoin wallet libraries, making it accessible to any project building on Bitcoin.

Bitcoin Core

Bitcoin Core added Miniscript support for P2WSH output descriptors in version 24.0 and extended it to Taproot (P2TR) descriptors in version 26.0. With these changes, users can import miniscript-based descriptors into their wallet, and Bitcoin Core will automatically track relevant outputs, estimate fees, and construct spending transactions.

The practical significance: any spending policy expressible in miniscript can now be managed by Bitcoin Core's mature wallet infrastructure. This includes cold storage setups with timelock recovery, corporate multisig arrangements, and inheritance plans with time-delayed fallbacks.

Bitcoin Dev Kit (BDK)

BDK is a Rust library for building Bitcoin wallets, and miniscript is fundamental to its architecture. BDK uses the rust-miniscript library internally for descriptor parsing, address derivation, fee estimation, and transaction finalization. Developers using BDK can define spending policies at the descriptor level and let the library handle the Script mechanics entirely.

BDK's integration means that mobile wallets, desktop applications, and server-side infrastructure built on this toolkit automatically inherit miniscript support. Combined with HD wallet derivation and PSBT workflows, BDK provides a complete stack for building wallets that support complex spending conditions.

FeatureBitcoin CoreBDK (rust-miniscript)
P2WSH miniscriptSince v24.0Since 0.1
Taproot miniscriptSince v26.0Supported
Policy compilerNot includedIncluded
Descriptor importVia importdescriptorsProgrammatic API
PSBT finalizationAutomaticAutomatic
Fee estimationWitness-awareWitness-aware
LanguageC++Rust (with C/Swift/Kotlin bindings)

Applications

Complex Custody Arrangements

Multisig is the most common Bitcoin custody pattern, but real-world custody needs often exceed what simple k-of-n multisig provides. A corporate treasury might require: any two of three executives for day-to-day operations, a three-of-five board approval for amounts exceeding a threshold, and a single cold storage recovery key after a one-year timelock.

Without miniscript, implementing this requires hand-crafted Script and custom wallet software that understands the bespoke format. With miniscript, the entire policy compiles to a standard descriptor that any miniscript-aware wallet can parse and sign for.

Inheritance and Estate Planning

Bitcoin inheritance is a well-known problem: key management schemes that protect against theft often make inheritance difficult, and vice versa. Miniscript enables policies that balance both concerns using timelocks:

or(pk(owner),and(pk(heir),after(210000)))

The owner can spend at any time. If the owner does not move the funds for approximately four years (210,000 blocks), the heir can also spend. The owner simply needs to periodically move their Bitcoin to a new address with a refreshed timelock to prevent premature inheritance activation. This works today, on mainnet, without any protocol changes.

Timelocked Recovery

Timelocks in Bitcoin come in two variants: CLTV (absolute) and CSV (relative). Miniscript supports both through the after() and older() primitives. This enables degrading security models where the number of required signers decreases over time:

thresh(3,pk(A),pk(B),pk(C),and(pk(D),older(4320)),and(pk(E),older(8640)))

Initially, three of A, B, C must sign. After approximately 30 days (4,320 blocks), D becomes eligible, lowering the effective threshold. After 60 days, E joins as well. This pattern is useful for organizational key rotation: if key holders become unavailable, the recovery set gradually expands rather than locking funds permanently.

Hash-Locked Contracts

Miniscript's sha256() and hash256() primitives enable hash-locked contracts, the same mechanism underlying HTLCs in the Lightning Network. A miniscript policy can combine hash locks with timelocks and multisig to create atomic swap conditions, escrow arrangements, or conditional payments without custom Script development.

Miniscript and Layer 2 Protocols

Layer 2 protocols rely heavily on pre-signed transactions with specific spending conditions. The Lightning Network uses HTLCs, revocation keys, and timelocks to enforce channel state. When a counterparty broadcasts an outdated channel state, the justice transaction uses the revocation key to claim all channel funds as a penalty.

These dispute resolution scripts are currently hand-crafted for each protocol. Miniscript offers an alternative: protocol designers can express dispute logic as composable policies, compile them to optimal Script, and rely on Miniscript's formal analysis to verify correctness. This matters especially as protocols explore more complex settlement conditions involving multiple participants, tiered timelocks, and conditional penalties.

Spark, which uses pre-signed exit transactions and relative timelocks for its settlement layer, is one protocol where Miniscript could streamline the construction of dispute resolution logic. As Spark's operator set grows and settlement conditions become more nuanced, expressing these conditions in a formally verifiable policy language rather than hand-crafted Script reduces the risk of subtle bugs in consensus-critical code.

Covenants and Future Directions

Covenants are a proposed extension to Bitcoin that would allow scripts to constrain where funds can be sent, not just who can spend them. If covenant opcodes like OP_CHECKTEMPLATEVERIFY or OP_VAULT are activated, Miniscript could be extended to incorporate them. This would enable policies like: "this UTXO can only be spent to one of these three addresses, and only after a one-week delay." The combination of Miniscript's composability with covenant restrictions would unlock vault designs, payment pools, and congestion control mechanisms that are currently impossible.

Limitations and Tradeoffs

Miniscript is not a universal solution. It deliberately covers a subset of what Bitcoin Script can express, trading generality for analyzability.

AspectMiniscriptRaw Bitcoin Script
AnalyzabilityFull: all spending paths are enumerableRequires per-script custom analysis
ComposabilitySafe composition via type systemManual stack management, error-prone
ExpressivenessCovers common patterns (multisig, timelocks, hashlocks)Arbitrary logic within opcode limits
OptimizationCompiler selects optimal encodingManual optimization by developer
Wallet supportGrowing (Bitcoin Core, BDK, hardware wallets)Limited to recognized templates
Learning curvePolicy language is accessibleRequires deep opcode knowledge

Specific limitations to be aware of:

  • Miniscript cannot express all valid Bitcoin scripts: constructs usingOP_SIZE, OP_DEPTH, or certain arithmetic opcodes fall outside its scope
  • The compiler's search space grows combinatorially with policy complexity: very large policies can take significant time to compile
  • Miniscript policies are publicly visible on-chain for P2WSH outputs (Taproot mitigates this by revealing only the executed branch)
  • Mixing absolute and relative timelocks in the same spending path has restrictions due to Bitcoin consensus rules around nLockTime and nSequence field interactions

Formal Specification and Standardization

Miniscript is formally specified in BIP 379, which defines the grammar, type system, and satisfaction algorithm. The reference implementation and interactive explorer are available at bitcoin.sipa.be/miniscript, where developers can input policies, view the compiled miniscript and Script output, and inspect the type analysis for each fragment.

The rust-miniscript library serves as the reference Rust implementation. It provides the policy compiler, miniscript parser and serializer, descriptor support, and PSBT finalization logic. The library undergoes regular fuzz testing to ensure correctness of the satisfaction and dissatisfaction algorithms.

Getting Started

For developers building on Bitcoin, Miniscript is approachable once you understand the layered architecture. The practical workflow:

  1. Define your spending policy using the policy language primitives (pk, older, after, and, or, thresh)
  2. Compile the policy using rust-miniscript or the online compiler to obtain a descriptor
  3. Import the descriptor into a wallet (Bitcoin Core, BDK-based, or any miniscript-aware wallet)
  4. Use standard PSBT workflows for signing: each signer adds their signature, and the wallet automatically finalizes the transaction when sufficient conditions are met

For Taproot outputs, the workflow extends naturally. The policy compiler distributes spending paths across tree leaves, and the key-spend path can use MuSig2 or FROST threshold signatures for the most common spending condition, keeping on-chain costs minimal.

Conclusion

Miniscript transforms Bitcoin scripting from a low-level, error-prone craft into a structured engineering discipline. By separating policy from encoding and providing a formal type system for analysis, it makes complex spending conditions safe to write, compose, and verify. The integration into Bitcoin Core and BDK means that developers building wallets, custody systems, and Layer 2 protocols can rely on Miniscript as foundational infrastructure rather than reinventing Script construction from scratch.

As Bitcoin's ecosystem grows more sophisticated, the demand for expressive yet auditable spending conditions will only increase. Whether the use case is corporate key management, inheritance planning, or protocol dispute resolution, Miniscript provides the tooling to build it correctly.

This article is for educational purposes only. It does not constitute financial or investment advice. Bitcoin and Layer 2 protocols involve technical and financial risk. Always do your own research and understand the tradeoffs before using any protocol.