Introduction

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

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 a 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 any code path requires violating particular resource limitations like the ops limit.

Miniscript functions as a representation for scripts that makes these 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 (see further). Finally, compatible scripts easily can be converted to miniscript - avoiding the need for additional metadata for e.g. signing devices that support it.

For now, Miniscript is really only designed for P2WSH and P2SH-P2WSH embedded scripts. Most of its constructions works fine in P2SH as well, but some of the (optional) security properties rely on Segwit-specific rules. Furthermore, the implemented policy compilers assume a Segwit-specific cost model.

Miniscript was designed and implemented by Pieter Wuille, Andrew Poelstra, and Sanket Kanjalkar at Blockstream Research, but is the result of discussions with several other people.

Source code links:

Policy to Miniscript compiler

Here you can see a demonstration of the Miniscript compiler. Write a spending policy following the instructions below, and observe how it affects the constructed Miniscript.

Supported expressions:
  • pk(NAME): Require public key named NAME to sign. NAME can be any string up to 16 characters.
  • after(NUM), older(NUM): Require that the nLockTime/nSequence value is at least NUM. NUM cannot be 0.
  • sha256(HEX), hash256(HEX): Require that the preimage of 64-character HEX is revealed. The special value H can be used as HEX.
  • ripemd160(HEX), hash160(HEX): Require that the preimage of 40-character HEX is revealed. The special value H can be used as HEX.
  • and(EXPR,EXPR): Require that both subexpressions are satisfied.
  • or([N@]EXPR,[N@]EXPR): Require that one of the subexpressions is satisfied. The numbers N indicate the relative probability of each of the subexpressions.
  • thresh(NUM,EXPR,EXPR,...): Require that NUM out of the following subexpressions are satisfied (all combinations are assumed to be equally likely).

Miniscript reference

Translation table

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(arguments,...)" notation, while wrappers are written using prefixes separated from other fragments by a colon. The colon is dropped between subsequent wrappers; e.g. vc:pk(key) is the v: wrapper applied to the c: wrapper applied to the pk fragment for a given key.

SemanticsMiniscript fragmentBitcoin Script
false 0 0
true 1 1
check(key) pk(key) <key>
pk_h(key) DUP HASH160 <HASH160(key)> EQUALVERFIFY
nSequence ≥ n (and compatible) older(n) <n> CHECKSEQUENCEVERIFY
nLockTime ≥ n (and compaitlbe) after(n) <n> CHECKLOCKTIMEVERIFY
len(x) = 32 and SHA256(x) = h sha256(h) SIZE <32> EQUALVERIFY SHA256 <h> EQUAL
len(x) = 32 and HASH256(x) = h hash256(h) SIZE <32> EQUALVERIFY HASH256 <h> EQUAL
len(x) = 32 and RIPEMD160(x) = h ripemd160(h) SIZE <32> EQUALVERIFY RIPEMD160 <h> EQUAL
len(x) = 32 and HASH160(x) = h hash160(h) SIZE <32> 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
X1 + ... + Xn = k thresh(k,X1,...,Xn) [X1] [X2] ADD ... [Xn] ADD ... <k> EQUAL
check(key1) + ... + check(keyn) = k thresh_m(k,key1,...,keyn) <k> <key1> ... <keyn> <n> CHECKMULTISIG
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
The and_n fragment and t:, l:, and u: wrappers are syntactic sugar for other Miniscripts, as listed in the table above. In what follows, they will not be included anymore, as their properties can be derived by looking at their expansion.

Correctness properties

Not every Miniscript expression can be composed with every other. Some return their result by putting zero or false on the stack; others distinguish by aborting or continuing. 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.

Every miniscript expression has one of four basic types:

  • "B" Base expressions:
    • Takes its inputs from the top of the stack.
    • When satisfied, pushes a nonzero value of up to 4 bytes onto the stack.
    • When dissatisfied, pushes a 0 onto the stack.
    • This is used for most expressions, and required for the top level one.
    • For example: older(n) = <n> CHECKSEQUENCEVERIFY.
  • "V" Verify expressions:
    • Takes its inputs from the top of the stack.
    • When satisfactied, pushes nothing.
    • Cannot be dissatisfied (aborts execution instead).
    • A "V" can be obtained using the v: wrapper on a "B" expression, or by combining other "V" expressions using and_v, or_i, or_c, or andor.
    • For example vc:pk(key) = <key> CHECKSIGVERIFY.
  • "K" Key expressions:
    • Takes its inputs from the top of the stack.
    • Always pushes a public key onto the stack, for which a signature is to be provided to satisfy the expression.
    • Can be converted into a "B" using the c: wrapper (CHECKSIG).
    • For example pk_h(key) = DUP HASH160 <Hash160(key)> EQUALVERIFY
  • "W" Wrapped expressions:
    • Takes its input from one below the top of the stack.
    • When satisfied, pushes a nonzero value (like B) on top of the stack, or one below the top.
    • When dissatisfied, pushes 0 op top of the stack or one below the top.
    • Every "W" is either s:B (SWAP B) or a:B (TOALTSTACK B FROMALTSTACK).
    • For example sc:pk(key) = SWAP <key> CHECKSIG.

Then there are 5 type modifiers, which guarantee additional properties:

  • "z" Zero-arg: this expression always consumes exactly 0 stack elements.
  • "o" One-arg: this expression always consumes exactly 1 stack element.
  • "n" Nonzero: no satisfaction for this expression requires a top stack element that is zero.
  • "d" Dissatisfiable: a dissatisfaction for this expression can unconditionally be constructed.
  • "u" Unit: when satisfied, this expression will put an exact 1 on the stack (as opposed to any nonzero value).

This tables lists the correctness requirements for each of the Miniscript expressions, and its type properties in function of those of their subexpressions:

MiniscriptRequiresTypeProperties
0Bz; u; d
1Bz; u
pk(key)Ko; n; d; u
pk_h(key)Kn; d; u
older(n), after(n)1 ≤ n < 231Bz
sha256(h)Bo; n; d; u
ripemd160(h)Bo; n; d; u
hash256(h)Bo; n; d; u
hash160(h)Bo; n; d; u
andor(X,Y,Z)X is Bdu; Y and Z are both B, K, or Vsame as Y/Zz=zXzYzZ; o=zXoYoZ or oXzYzZ; u=uYuZ; d=dZ
and_v(X,Y)X is V; Y is B, K, or Vsame as Yz=zXzY; o=zXoY or zYoX; n=nX or zXnY; u=uY
and_b(X,Y)X is B; Y is WBz=zXzY; o=zXoY or zYoX; n=nX or zXnY; d=dXdY; u
or_b(X,Z)X is Bd; Z is WdBz=zXzZ; o=zXoY or zYoX; d; u
or_c(X,Z)X is Bdu; Z is VVz=zXzZ; o=oXzZ
or_d(X,Z)X is Bdu; Z is BBz=zXzZ; o=oXzZ; d=dZ; u=uZ
or_i(X,Z)both are B, K, or Vsame as X/Zo=zXzZ; u=uXuZ; d=dX or dZ
thresh(k,X1,...,Xn)1 < k < n; X1 is Bdu; others are WduBz=all are z; o=all are z except one is o; d; u
thresh_m(k,key1,...,keyn)1 ≤ k ≤ nBn; d; u
a:XX is BWd=dX; u=uX
s:XX is BoWd=dX; u=uX
c:XX is KBo=oX; n=nX; d=dX; u
d:XX is VzBo=zX; n; u; d
v:XX is BVz=zX; o=oX; n=nX
j:XX is BnBo=oX; n; d; u=uX
n:XX is BBz=zX; o=oX; n=nX; d=dX; u

Resource limitations

Various types of Bitcoin Scripts have different resource limitations, either through consensus or standardness. Some of them affect otherwise valid Miniscripts:

  • Scripts over 10000 bytes are invalid by consensus (bare, P2SH, P2WSH, P2SH-P2WSH).
  • Scripts over 520 bytes are invalid by consensus (P2SH).
  • Script satisfactions where the total number of non-push opcodes plus the number of keys participating in all executed thresh_ms, is above 201, are invalid by consensus (bare, P2SH, P2WSH, P2SH-P2WSH).
  • Anything but c:pk(key) (P2PK), c:pk_h(key) (P2PKH), and thresh_m(k,...) up to n=3 is invalid by standardness (bare).
  • Scripts over 3600 bytes are invalid by standardness (P2WSH, P2SH-P2WSH).
  • Script satisfactions with a serialized scriptSig over 1650 bytes are invalid by standardness (P2SH).
  • Script satisfactions with a witness consisting of over 100 stack elements (excluding the script itself) are invalid by standardness (P2WSH, P2SH-P2WSH).
Miniscript makes it easy to verify these properties statically for all possible execution paths.

Security properties

The type system above guarantees that the corresponding Bitcoin Scripts are:

  • consensus and standardness complete: Assuming the resource limits listed in the previous section are not violated, for every set of met conditions that are permitted by the semantics, a witness can be constructed that passes Bitcoin's consensus rules and common standardness rules.
  • consensus sound: It is not possible to construct a witness that is consensus valid for a Script unless the spending conditions are met. Since standardness rules permit only a subset of consensus-valid satisfactions (by definition), this property also implies standardness soundness.

The completeness property has been extensively tested for P2WSH by verifying large numbers of random satisfactions for random Miniscript expressions against Bitcoin Core's consensus and standardness implementation.

In order for these properties to not just apply to script, but to an entire transaction, it's important that the witness commits to all data relevant for verification. In practice this means that scripts whose conditions can be met without any digital signature are insecure. For example, if an output can be spent by simply passing a certain nLockTime (an after(n) fragment in Miniscript) but without any digital signatures, an attacker can modify the nLockTime field in the spending transaction.

Satisfactions and malleability

Basic satisfactions

The following table shows all valid satisfactions and dissatisfactions for every Miniscript, using satisfactions and dissatisfactions of its subexpressions. Multiple possibilities are separated by semicolons. Some options are not actually necessary to produce correct witnesses, and are called non-canonical options. They are listed for completeness, but marked in [grey] below.

MiniscriptDissatisfactions (dsat)Satisfactions (sat)
0-
1-
pk(key)0sig
pk_h(key)0sig key
older(n)-
after(n)-
sha256(h)any 32-byte vector except the preimagepreimage
ripemd160(h)any 32-byte vector except the preimagepreimage
hash256(h)any 32-byte vector except the preimagepreimage
hash160(h)any 32-byte vector except the preimagepreimage
andor(X,Y,Z)dsat(Z) dsat(X); [dsat(Y) sat(X)]sat(Y) sat(X); sat(Z) dsat(X)
and_v(X,Y)[dsat(Y) sat(X)]sat(Y) sat(X)
and_b(X,Y)dsat(Y) dsat(X); [sat(Y) dsat(X)]; [dsat(Y) sat(X)]sat(Y) sat(X)
or_b(X,Z)dsat(Z) dsat(X)dsat(Z) sat(X); sat(Z) dsat(X); [sat(Z) sat(X)]
or_c(X,Z)-sat(X); sat(Z) dsat(X)
or_d(X,Z)dsat(Z) dsat(X)sat(X); sat(Z) dsat(X)
or_i(X,Z)dsat(X) 1; dsat(Z) 0sat(X) 1; sat(Z) 0
thresh(k,X1,...,Xn)All dsats [Sats/dsats with 1 ≤ #(sats) ≠ k]Sats/dsats with #(sats) = k
thresh_m(k,key1,...,keyn)0 0 ... 0 (n+1 times)0 sig ... sig
a:Xdsat(X)sat(X)
s:Xdsat(X)sat(X)
c:Xdsat(X)sat(X)
d:X0sat(X) 1
v:X-sat(X)
j:X0; [nsat(X) (if nonzero top stack)]sat(X)
n:Xdsat(X)sat(X)

The correctness properties in the previous section are based on the availability of satisfactions and dissatisfactions listed above. The requirements include a "d" for every subexpression whose dissatisfaction may be needed in building a satisfaction for the parent expression. The "d" properties themselves rely on the "d" properties of subexpressions.

A conservative simplification is made here, ignoring the existence of always-true expressions like 1. This makes the typing rules incorrect in the following cases:

  • or_b(X,1) and or_b(1,X) are complete when X is dissatisfiable ("d"). In that case, the condition is equivalent to 1 and is always met, and a satisfaction of the form "nsat(X)" exists.
  • or_b(1,1) is complete, as it is equivalent to 1 and "" is a valid satisfaction.
  • and_b(X,1) and and_b(1,X) are dissatisfiable ("d") when X is.
  • andor(1,1,Z) is complete, as it is equivalent to 1 and "" is a valid satisfaction.
  • andor(1,Y,0) is complete when Y is; it also has the same type as Y, despite 0 always being a "B". This is also dissatisfiable when Y is.
  • The 1s items listed above can be replaced with any other always-true expression.
Since all these cases of inaccurate typing involve always-true expressions that can be avoided using strictly simpler/smaller Miniscripts, we don't consider this to be a serious restriction. For Miniscripts excluding always-true expressions (except in the and_v(X,1) code), the typing rules match the definitions above.

Malleability

While following the table above to construct satisfactions is sufficient for meeting the completeness and soundness properties, it does not guarantee non-malleability.

Malleability is the ability for a third party (not a participant in the script) to modify an existing satisfaction into another valid satisfaction. Since Segwit, malleating a transaction no longer breaks the validity of unconfirmed descendant transactions. However, unintentional malleability may still have a number of much weaker undesirable effects. If a witness can be stuffed with additional data, the transaction's feerate will go down, potentially to the point where its ability to propagate and get confirmed is impacted. Additionally, malleability can be exploited to add roundtrips to BIP152 block propagation, by trying to get different miners to mine different versions of the same transaction. Finally, malleability may interfere with the usage of hash locks as a mechanism for publishing preimages.

Because of these reasons, Miniscript is actually designed to permit non-malleable signing. As this guarantee is restricted in non-obvious ways, let's first state the assumptions:

  • The attacker does not have access to any of the private keys of public keys that participate in the Script. Limiting the impact a rogue participant can have is a distinct attack model.
  • The attacker only has access to hash preimages that honest users have access to as well.
  • The attacker gets to see exactly one satisfying witness of any transaction. If he sees multiple, the protections defined here are not necessarily valid anymore.

Malleable satisfactions or dissatisfactions appear whenever options are available to attackers distinct from the one taken by honest users. This can happen for multiple reasons:

  • Two or more options for a satisfaction or dissatisfaction are listed in the table above which are both available to attackers directly. Regardless of which option is used in the honest solution, the attacker can change the solution to the other one.
  • Two ore more options for a satisfaction or dissatisfaction are listed in the table above, only one of which is available to attackers, but the honest solution uses another one. In that case, the attacker can modify the solution to pick the one available to him.
  • The honest users pick a solution that contains a satisfaction which can be turned into a dissatisfaction without invalidating the overall witness. Those are called an overcomplete solutions.

Non-malleable satisfaction algorithm

Because we assume attackers never have access to private keys, we can treat any solution that includes a signature as one that is unavailable to attackers. For others, the worst case is that the attacker has access to every solution the honest users have, but no others: for preimages this is an explicit assumption, while timelock availability is determined by the nLockTime and nSequence fields in the transaction. As long as the overall satisfaction includes at least one signature, those values are fixed, and timelock availability is identical for attackers and honest users.

In order to produce non-malleable satisfactions we make use of a function that returns the optimal satisfaction and dissatisfaction for a given expression (if any exist), or a special DONTUSE value, together with an optional HASSIG marker that tracks whether the solution contains at least one signature. To implement the function:

  • Invoke the function recursively for all subexpressions, obtaining all satisfactions/dissatisfactions for those.
  • Iterate over all the valid satisfactions/dissatisfactions in the table above (including the non-canonical ones), taking into account:
    • The dissatisfactions for sha256, ripemd160, hash256, and hash160 are always malleable, so instead use DONTUSE there.
    • The non-canonical options for and_b, or_b, and thresh are always overcomplete, so instead use DONTUSE there as well (with HASSIG flag if the original non-canonical solution had one).
    • The satisfactions for pk, pk_h, and thresh_m can be marked HASSIG.
    • When constructing solutions by combining results for subexpressions, the result is DONTUSE if any of the constituent results is DONTUSE. Furthermore, the result gets the HASSIG tag if any of the constituents do.
  • If among all valid solutions (including DONTUSE ones) more than one does not have the HASSIG marker, return DONTUSE, as this means there are multiple options for the attacker, so whatever we pick, the attacker could change it to the other option.
  • If instead exactly one does not have the HASSIG marker, return that solution. It means there is exactly one option available to the attacker, so we have to pick it to prevent him from changing another solution to this one.
  • If all valid solutions have the HASSIG marker, but all of them are DONTUSE, return DONTUSE-HASSIG. The HASSIG marker is important because while this represents a choice between multiple options that would cause malleability if used, they are not available to the attacker, and we may be able to avoid them entirely still.
  • Otherwise, return the smallest solution (in terms of witness size) among the not-DONTUSE ones.
To produce an overall satisfaction, invoke the function on the toplevel expression. If no valid satisfaction is returned, or it is DONTUSE, fail. Otherwise, if any timelocking is used in the script but the result does not have the HASSIG flag, also fail, as this represents a situation where an attacker could change the nLockTime or nSequence values to meet additional timelock conditions. Not to mention that solutions without signatures are generally insecure. If the satisfaction is both not DONTUSE and HASSIG, return it.

The above can be used to show that no non-canonical solutions listed in the satisfaction table can occur inside non-malleable satisfactions.

  • Some of the non-canonical options (the or_b, and_b, and thresh ones) are overcomplete, and thus can clearly not appear in non-malleable satisfactions.
  • It can be shown that non-"d" expressions cannot be dissatisfied in valid witnesses, as for those, dissatisfaction implies aborting or propagating failure upwards, ultimately dissatisfying the top-level expression. This rules out the usage of the non-canonical and_v dissatisfaction.
  • "d" expressions are defined to be unconditionally dissatisfiable, which implies that for those a non-HASSIG dissatisfaction must exist. Non-HASSIG solutions must be preferred over HASSIG ones, and when multiple non-HASSIG ones exist, none can be used. This lets us rule out the other non-canonical options in the table:
    • j:X is always "d", its non-HASSIG dissatisfaction "0" always exists, and thus rules out any usage of "dsat(X)".
    • If andor(X,Y,Z) is "d", a non-HASSIG dissatisfaction "dsat(Z) dsat(X)" must exist, and thus rules out any usage of "dsat(Y) sat(X)".
    • If and_b(X,Y) is "d", a non-HASSIG dissatisfaction "dsat(Y) dsat(X)" must exist, and thus rules out any usage of "dsat(Y) sat(X)" and "sat(Y) dsat(X)". Those are also overcomplete.
    • thresh(k,...) is always "d", a non-HASSIG dissatisfaction with just dissatisfactions must exist due to typing rules, and thus rules out usage of the other dissatisfactions. They are also overcomplete.

Guaranteeing non-malleability

Now we have an algorithm that can find the optimal non-malleable satisfaction for any Miniscript, if it exists. But can we statically prove that such a solution always exists? In order to do that, we add additional properties to the type system:

  • "s" Signed: satisfying this expression always requires a signature (predicting whether all satisfactions will be HASSIG).
  • "f" Forced: dissatisfying this expression always requires a signature (predicting whether all dissatisfactions will be HASSIG). This conflicts with "d".
  • "e" Expressive: regardless of what conditions are met, exactly one dissatisfaction without signatures is available (predicting non-DONTUSE dissatisfactions). This implies "d".

The following table lists these additional properties, and the requirements for non-malleability.

MiniscriptRequiresProperties
0s, e
1f
pk(key)s, e
pk_h(key)s, e
older(n)f
after(n)f
sha256(h)
ripemd160(h)
hash256(h)
hash160(h)
andor(X,Y,Z)eX and (sX or sY or sZ)s=sZ and (sX or sY); f=fZ and (sX or fY); e=eZ and (sX or fY)
and_v(X,Y)s=sX or sY; f=sX or fY
and_b(X,Y)s=sX or sY; f=fXfY or sXfX or sYfY; e=eXeYsXsY
or_b(X,Z)eXeZ and (sX or sZ)s=sXsZ; e
or_c(X,Z)eX and (sX or sZ)s=sXsZ; f
or_d(X,Z)eX and (sX or sZ)s=sXsZ; f=fZ; e=eZ
or_i(X,Z)sX or sZs=sXsZ; f=fXfZ; e=eXfZ or eZfX
thresh(k,X1,...,Xn)all are e; at most k are non-ss=at most k-1 are non-s; e=all are s
thresh_m(k,key1,...,keyn)s; e
a:Xs=sX; f=fX; e=eX
s:Xs=sX; f=fX; e=eX
c:Xs; e=eX
d:Xs=sX; e
v:Xs=sX; f
j:Xs=sX; e=fX
n:Xs=sX; f=fX; e=eX

Analyze a Miniscript

Provide a well-typed miniscript expression of type "B".