← Back to Bitcoin Improvement Proposals
BIP 379informationalDraftkey-managementscriptsigning

Miniscript

This document specifies Miniscript, a language for writing (a subset of) Bitcoin Scripts in a structured way, enabling analysis, composition, generic signing and more.

No reviews
Pieter Wuille·Updated Mar 29, 2026·0 reviews·0 attestations·View source
Collections:BIPs — Merged

Specification

Abstract

This document specifies Miniscript, a language for writing (a subset of) Bitcoin Scripts in a structured way, enabling analysis, composition, generic signing and more.

Copyright

This document is licensed under the Creative Commons CC0 1.0 Universal license.

Motivation

Bitcoin Script is an unusual stack-based language with many edge cases, designed for implementing spending conditions consisting of various combinations of signatures, hash locks, and time locks. Yet, despite being limited in functionality, it is still highly nontrivial to:

  • Given a combination of spending conditions, finding the most economical script to implement it.
  • Given two scripts, construct a script that implements a composition of their spending conditions (e.g. a multisig where one of the "keys" is another multisig).
  • Given a script, find out what spending conditions it permits.
  • Given a script and access to a sufficient set of private keys, construct a general satisfying witness for it.
  • Given a script, be able to predict the cost of spending an output.
  • Given a script, know whether particular resource limitations like the ops limit might be hit when spending.

Miniscript functions as a representation for scripts that makes this sort of operations possible. It has a structure that allows composition. It is very easy to statically analyze for various properties (spending conditions, correctness, security properties, malleability, ...). It can be targeted by spending policy compilers. Finally, compatible scripts can easily be converted to Miniscript form - avoiding the need for additional metadata for e.g. signing devices that support it.

Specification

These specifications apply to P2WSH (BIP 141) and Tapscript (BIP 342) scripts, with only minor variations between the two. Differences are noted inline. Unless explicitly stated otherwise, specifications apply to both. P2SH and bare scripts are excluded from this specification.

Translation Table

Miniscript consists of a set of script fragments which are designed to be safely and correctly composable.

This table shows all Miniscript fragments and their associated semantics and Bitcoin Script. Fragments that do not change the semantics of their subexpressions are called wrappers. Normal fragments use a fragment(arg1,arg2,...) notation, while wrappers are written using prefixes separated from other fragments by a colon. The colon is dropped between subsequent wrappers; e.g. dv:older(144) is the d: wrapper applied to the v: wrapper applied to the older fragment for 144 blocks.

The pk, pkh, and and_n fragments and t:, l:, and u: wrappers are syntactic sugar for other Miniscripts, as listed in the table below. Note that <20> are in hex representation in this document.

Miniscript fragments are expected to be used in BIP 382 wsh() descriptors and BIP 386 tr() descriptors. Key expressions are specified in BIP 380. Additionally, BIPs 382 and 386 specify restrictions on key expressions and what they resolve to - these apply to key expressions in Miniscript. BIP 382's key expression restrictions apply to Miniscript in P2WSH contexts, and BIP 386's key expression restrictions apply to Miniscript in P2TR contexts. From a user's perspective, Miniscript is not a separate language, but rather a significant expansion of the descriptor language.

| Semantics | Miniscript Fragment | Bitcoin Script |----------------------------------------------------------|-------------------------------|--------------- | false | 0 | 0 | true | 1 | 1 | check(key) | pk_k(key) | <key> | | pk_h(key) | DUP HASH160 <HASH160(key)> EQUALVERIFY | | pk(key) = c:pk_k(key) | <key> CHECKSIG | | pkh(key) = c:pk_h(key) | DUP HASH160 <HASH160(key)> EQUALVERIFY CHECKSIG | nSequence ≥ n (and compatible) | older(n) | <n> CHECKSEQUENCEVERIFY | nLockTime ≥ n (and compatible) | after(n) | <n> CHECKLOCKTIMEVERIFY | len(x) = 32 and SHA256(x) = h | sha256(h) | SIZE <0x20> EQUALVERIFY SHA256 <h> EQUAL | len(x) = 32 and HASH256(x) = h | hash256(h) | SIZE <0x20> EQUALVERIFY HASH256 <h> EQUAL | len(x) = 32 and RIPEMD160(x) = h | ripemd160(h) | SIZE <0x20> EQUALVERIFY RIPEMD160 <h> EQUAL | len(x) = 32 and HASH160(x) = h | hash160(h) | SIZE <0x20> EQUALVERIFY HASH160 <h> EQUAL | (X and Y) or Z | andor(X,Y,Z) | [X] NOTIF [Z] ELSE [Y] ENDIF | X and Y | and_v(X,Y) | [X] [Y] | | and_b(X,Y) | [X] [Y] BOOLAND | | and_n(X,Y) = andor(X,Y,0) | [X] NOTIF 0 ELSE [Y] ENDIF | X or Z | or_b(X,Z) | [X] [Z] BOOLOR | | or_c(X,Z) | [X] NOTIF [Z] ENDIF | | or_d(X,Z) | [X] IFDUP NOTIF [Z] ENDIF | | or_i(X,Z) | IF [X] ELSE [Z] ENDIF | X_1 + ... + X_n = k | thresh(k,X_1,...,X_n) | [X_1] [X_2] ADD ... [X_n] ADD ... <k> EQUAL | check(key_1) + ... + check(key_n) = k (P2WSH only) | multi(k,key_1,...,key_n) | <k> <key_1> ... <key_n> <n> CHECKMULTISIG | check(key_1) + ... + check(key_n) = k (Tapscript only) | multi_a(k,key_1,...,key_n) | <key_1> CHECKSIG <key_2> CHECKSIGADD ... <key_n> CHECKSIGADD <k> NUMEQUAL | X (identities) | a:X | TOALTSTACK [X] FROMALTSTACK | | s:X | SWAP [X] | | c:X | [X] CHECKSIG | | t:X = and_v(X,1) | [X] 1 | | d:X | DUP IF [X] ENDIF | | v:X | [X] VERIFY (or VERIFY version of last opcode in [X]) | | j:X | SIZE 0NOTEQUAL IF [X] ENDIF | | n:X | [X] 0NOTEQUAL | | l:X = or_i(0,X) | IF 0 ELSE [X] ENDIF | | u:X = or_i(X,0) | IF [X] ELSE 0 ENDIF

Type System

Not every Miniscript expression can be composed with every other. Some return their result by putting true or false on the stack; others can only abort or continue. Some require subexpressions that consume an exactly known number of arguments, while others need a subexpression that has a nonzero top stack element to satisfy. To model all these properties, we define a correctness type system for Miniscript.

Correctness

Every miniscript expression has one of four basic types: "B" (base), "V" (verify), "K" (key) and "W" (wrapped). Then there are 5 type modifiers that guarantee additional properties: "z" (zero-arg), "o" (one-arg), "n" (nonzero), "d" (dissatisfiable), and "u" (unit).

The following table lists the correctness requirements for each of the Miniscript expressions, and its type properties in function of those of their subexpressions.

| Miniscript | Requires | Type | Properties |------------------------------|-------------------------------------------------------|-------------|----------- | 0 | | B | z; u; d | 1 | | B | z; u | pk_k(key) | | K | o; n; d; u | pk_h(key) | | K | n; d; u | older(n), after(n) | 1 ≤ n < 2<sup>31</sup> | B | z | sha256(h) | | B | o; n; d; u | ripemd160(h) | | B | o; n; d; u | hash256(h) | | B | o; n; d; u | hash160(h) | | B | o; n; d; u | andor(X,Y,Z) | X is Bdu; Y and Z are both B, K, or V | same as Y/Z | z=z<sub>X</sub>z<sub>Y</sub>z<sub>Z</sub>; o=z<sub>X</sub>o<sub>Y</sub>o<sub>Z</sub> or o<sub>X</sub>z<sub>Y</sub>z<sub>Z</sub>; u=u<sub>Y</sub>u<sub>Z</sub>; d=d<sub>Z</sub> | and_v(X,Y) | X is V; Y is B, K, or V | same as Y | z=z<sub>X</sub>z<sub>Y</sub>; o=z<sub>X</sub>o<sub>Y</sub> or z<sub>Y</sub>o<sub>X</sub>; n=n<sub>X</sub> or z<sub>X</sub>n<sub>Y</sub>; u=u<sub>Y</sub> | and_b(X,Y) | X is B; Y is W | B | z=z<sub>X</sub>z<sub>Y</sub>; o=z<sub>X</sub>o<sub>Y</sub> or z<sub>Y</sub>o<sub>X</sub>; n=n<sub>X</sub> or z<sub>X</sub>n<sub>Y</sub>; d=d<sub>X</sub>d<sub>Y</sub>; u | or_b(X,Z) | X is Bd; Z is Wd | B | z=z<sub>X</sub>z<sub>Z</sub>; o=z<sub>X</sub>o<sub>Z</sub> or z<sub>Z</sub>o<sub>X</sub>; d; u | or_c(X,Z) | X is Bdu; Z is V | V | z=z<sub>X</sub>z<sub>Z</sub>; o=o<sub>X</sub>z<sub>Z</sub> | or_d(X,Z) | X is Bdu; Z is B | B | z=z<sub>X</sub>z<sub>Z</sub>; o=o<sub>X</sub>z<sub>Z</sub>; d=d<sub>Z</sub>; u=u<sub>Z</sub> | or_i(X,Z) | both are B, K, or V | same as X/Z | o=z<sub>X</sub>z<sub>Z</sub>; u=u<sub>X</sub>u<sub>Z</sub>; d=d<sub>X</sub> or d<sub>Z</sub> | thresh(k,X_1,...,X_n) | 1 ≤ k ≤ n; X<sub>1</sub> is Bdu; others are Wdu | B | z=all are z; o=all are z except one is o; d; u | multi(k,key_1,...,key_n) | 1 ≤ k ≤ n ≤ 20 | B | n; d; u | multi_a(k,key_1,...,key_n) | 1 ≤ k ≤ n | B | d; u | a:X | X is B | W | d=d<sub>X</sub>; u=u<sub>X</sub> | s:X | X is Bo | W | d=d<sub>X</sub>; u=u<sub>X</sub> | c:X | X is K | B | o=o<sub>X</sub>; n=n<sub>X</sub>; d=d<sub>X</sub>; u | d:X | X is Vz | B | o; n; d; (Tapscript only) u | v:X | X is B | V | z=z<sub>X</sub>; o=o<sub>X</sub>; n=n<sub>X</sub> | j:X | X is Bn | B | o=o<sub>X</sub>; n; d; u=u<sub>X</sub> | n:X | X is B | B | z=z<sub>X</sub>; o=o<sub>X</sub>; n=n<sub>X</sub>; d=d<sub>X</sub>; u

Timelock Type Mixing

There is one additional correctness property that Miniscript expressions must satisfy: the four timelock types (absolute time based, absolute height based, relative time based, and relative height based) must not be mixed in an incompatible way.

Within and combinators and the thresh combinator where k >= 2, it is illegal for both absolute height based and time based timelocks to appear, or for both relative height based and time based timelocks to appear.

For all other combinators, it is legal to mix timelock types. It is also always legal to mix absolute and relative timelocks (even if one is height based and the other is time based).

Malleability

Malleability is the ability for a third party (someone who does not hold a participating private key) to modify an existing satisfaction into another valid satisfaction. To analyze the malleability guarantees of a script we define three additional type properties: "s" (signed), "f" (forced) and "e" (expressive).

The following table lists the malleability properties and requirement of each fragment.

| Miniscript | Requires | Properties |------------------------------|---------------------------------------------------------------------|----------- | 0 | | s, e | 1 | | f | pk_k(key) | | s, e | pk_h(key) | | s, e | older(n) | | f | after(n) | | f | sha256(h) | | | ripemd160(h) |

[Content truncatedview full spec at source]

Discussion (0 threads)

Loading discussions...