Although the function of Bitcoin script is limited, its importance is self-evident, and it has ushered in a major upgrade today. Bitcoin core maintainer Pieter Wuille officially announced the Miniscript technology project.
Pieter Wuille introduced:
- Tesla plunges 20%, calling it "Bitcoin in the automotive sector"
- "False" gold disrupts the market, BTC or demand soars
- Currency Logic | Dollar, Bitcoin and Facebook Pattern
- Is the BTC's fixed monetary policy really impeccable?
- Why did the Sino-US trade war have a stock market crash and bitcoin strengthened?
- Bitcoin extortion, revealing underground rivers and lakes that you don’t know
"In short, it's a way to write (some) bitcoin scripts in a structured, combinable way that allows for various static analysis, generic signatures, and policy writing.
Imagine a company wanting to protect their cold storage funds with a 2-of-3 multi-signature strategy. The problem is that one of the executives has their own 2FA two-factor authentication/multi-signature/timelock setting. Why can't the entire setup be one of the multi-signature "participants"?
Much of the current work is focused on extending the functionality of the blockchain itself to support more complex applications, but I feel we have forgotten to use these features in an accessible, combinable, and analyzable way, which is basically today. impossible.
I hope that things like Miniscript and PSBT can reduce some of the barriers between software. Ideally, the executive's 2FA settings can perfectly interact with the cold storage settings, calculate the necessary combined scripts, and still be able to sign.
The project includes a strategy compiler, you can know under what conditions the output should be usable, and what is its relative probability, it will find the most economical Miniscript compatible script for it (limited to some conversions).
For this project, we have been discussing for a long time (including the blockchain activity at Stanford earlier this year). Until we tested a lot of Bitcoin consensus and standardization rules, I was satisfied with the content, and now it is the time of publication.
Since the beginning, we have rewritten the entire design nearly three times, and Andrew Poelstra and sanket1729 continue to discuss additional possibilities, problems and analysis techniques.
As everyone knows, I am not good at naming this thing, so I need to point out that this project has nothing to do with Minisketch (https://github.com/sipa/minisketch), which is Greg Maxwell, tomatodread and me. Written together, the goal is to support efficient set-based transaction relay (Erlay).
It also has nothing to do with our research work on Taproot . Of course, the study of Miniscript does teach us some knowledge about scripts, which can provide a basis for future improvements to scripts, and Miniscript can be extended as needed.
I will include this in Bitcoin Core if needed (I believe this can be very useful), but ideally it will be included in many wallet technologies. Andrew Poelstra and sanket1729 have been developing a Rust library for it:
” The following is a translation (partial) of the Miniscript project technical documentation:
Miniscript is a language that writes a subset of Bitcoin scripts in a structured way and supports functions such as analysis, composition, and general signature.
Bitcoin scripting is a unique stack-based language with many boundary use cases designed to implement spending conditions consisting of various combinations of signatures, hash locks, and time locks. Despite the limited functionality of Bitcoin scripts, it is still very important:
- Consider a combination of spending conditions and find the most economical script to implement it;
- Given two scripts, build a script that implements a combination of its spending conditions (for example, a multi-signature where one "key" is another multi-signature);
- Given a script to find out the allowed spending conditions;
- Given a script and accessing enough private key sets, construct a universally satisfactory witness for it;
- Given a script, you can predict the cost of spending one output;
- Given a script that understands whether specific resource limits (such as operational restrictions) will be affected when they are spent;
Miniscript, as a representation of scripts, makes these operations possible, with a structure that allows for composition. It is very easy to statically analyze various attributes (expenditure conditions, correctness, security, scalability, etc.). It can be the target of expenditure strategy compilers (see below). Finally, compatible scripts can be easily converted to the Miniscript format, avoiding the need for additional metadata such as signature devices that support it.
Currently, Miniscript is actually designed for P2WSH and P2SH-P2WSH embedded scripts. Most of its constructs work fine in p2sh, but some (optional) security attributes rely on Segwit specific rules. In addition, the implemented policy compiler assumes a segregated witness (Segwit) specific cost model.
Miniscript was designed and implemented by Pieter Wuille, Andrew Poelstra and Sanket Kanjalkar of Blockstream Research, but there are others who participated in the discussion.
1. C++ compiler: https://github.com/sipa/miniscript
2, Bitcoin Core compatible C++ implementation: https://github.com/sipa/miniscript/tree/master/bitcoin/script
3. Rust-miniscript implementation: https://github.com/apoelstra/rust-miniscript
4. Miniscript content discussed at the Stanford Blockchain event: http://diyhpl.us/wiki/transcripts/stanford-blockchain-conference/2019/miniscript/ ( Chinese version )
MiniScript compiler strategy
Here you can see a demo of the Miniscript compiler. Write an expense strategy based on the instructions below and see how it affects the constructed Miniscript.
- Pk(NAME): A public key named NAME is required for signature, and the name can be any string of up to 16 characters;
- After(NUM), older(NUM): requires a value of at least NUM for nlocktime/nsequence and "0" for NUM;
- Sha256(HEX), hash256(HEX): requires pre-map (preimage) of 64 characters HEX, special value H can be used as HEX;
- Ripemd160(HEX), hash160(HEX): requires pre-mapping (preimage) of 40 characters HEX, special value H can be used as HEX;
- And(POL, POL): is required to satisfy these two sub-strategies;
- Or([N@]POL,[N@]POL): Requires one of the sub-policies to be met. The number N represents the relative probability of each subexpression (hence 9@, which is 9 times higher than the default);
- Thresh (NUM, POL, POL, …): requires that NUM in the following sub-strategies be satisfied (assuming all combinations are equally likely);
Miniscript output :
Expenditure cost analysis
- Script: 114 WU
- Input (Input) Input: 142.900000 WU
- Total: 256.900000 WU
Generated script structure
OP_CHECKSIG OP_NOTIF OP_CHECKSIG OP_NOTIF OP_CHECKSEQUENCEVERIFY OP_VERIFY OP_ENDIF OP_ENDIF OP_CHECKSIG
Analysis of Miniscript
Here you can analyze the structure of Miniscript expressions and more.
Provide a good miniscript expression of type "B".
Size: 114 bytes script
Generated script structure
OP_CHECKSIG OP_NOTIF OP_CHECKSIG OP_NOTIF OP_CHECKSIG OP_NOTIF OP_CHECKSEQUENCEVERIFY OP_VERIFY OP_ENDIF OP_ENDIF OP_CHECKSIG
This table shows all Miniscript fragments and their associated semantics and Bitcoin scripts. A fragment that does not change its subexpression semantics is called a wrapper. The normal fragment uses the "fragment(arguments,…)" notation, while the wrapper (wrapper class) is written with a prefix, which is separated from the other fragments by a colon. The colon will be deleted between subsequent wrappers; for example, vc:pk(key) is the v: wrapper class of the c: wrapper class applied to the pk fragment of the given key;
|meaning||Miniscript fragment||Bitcoin script|
||DUP HASH160 <HASH160(key)> EQUALVERFIFY|
|nSequence ≥ n (and compatible)||
|nLockTime ≥ n (and compatilbe)||
|Len(x) = 32 and SHA256(x) = h||
||SIZE <32> EQUALVERIFY SHA256 <h> EQUAL|
|Len(x) = 32 and HASH256(x) = h||
||SIZE <32> EQUALVERIFY HASH256 <h> EQUAL|
|Len(x) = 32 and RIPEMD160(x) = h||
||SIZE <32> EQUALVERIFY RIPEMD160 <h> EQUAL|
|Len(x) = 32 and HASH160(x) = h||
||SIZE <32> EQUALVERIFY HASH160 <h> EQUAL|
|( X and Y ) or Z||
||[X] NOTIF [Z] ELSE [Y] ENDIF|
|X and Y||
||[X] [Y] BOOLAND|
||[X] NOTIF 0 ELSE [Y] ENDIF|
|X or Z||
||[X] [Z] BOOLOR|
||[X] NOTIF [Z] ENDIF|
||[X] IFDUP NOTIF [Z] ENDIF|
||IF [X] ELSE [Z] ENDIF|
|X1 + … + Xn = k||
||[X1] [X2] ADD … [Xn] ADD … <k> EQUAL|
|Check(key1) + … + check(keyn) = k||
||<k> <key1> … <keyn> <n> CHECKMULTISIG|
||TOALTSTACK [X] FROMALTSTACK|
||DUP IF [X] ENDIF|
|| [X] VERIFY (or
VERIFY version of last opcode in
||SIZE 0NOTEQUAL IF [X] ENDIF|
||IF 0 ELSE [X] ENDIF|
||IF [X] ELSE 0 ENDIF|
and_n fragment and
u: wrapper are syntactic sugars for other Miniscripts, as shown in the above table. In the following, they will no longer be included because their properties can be derived by looking at their extensions.
Not every Miniscript expression can be combined with each other. Some return results by putting "true" or "false" values on the stack, while others can only abort or continue. Some need subexpressions that use a completely known number of arguments, while others require subexpressions with non-zero top stack elements to satisfy. To model all of these properties, we defined a correct type system for Miniscript.
Each ministcript expression has one of four basic types:
- " B " basic expression: These expressions get input from the top of the stack. When satisfied, they push a non-zero value of up to 4 bytes onto the stack. When not satisfied, they push a precise 0 onto the stack (it is entirely possible if not satisfied). This type is used for most expressions and is required for top-level expressions. An example is older(n) = CHECKSEQUENCEVERIFY;
- " V " validation expressions: Like "B" expressions, they take input from the top of the stack. However, once they are met, they can continue to work without pushing anything. They cannot be unsatisfied without stopping. A "V" expression can be obtained using v: wrapper on a "B" expression, or by combining other "V" expressions with
andor. An example is vc:pk(key) = CHECKSIGVERIFY;
- " K " Key expressions: They also take input from the top of the stack, but do not directly validate the condition, but push the public key onto the stack. For the stack, a signature is still needed to satisfy the expression. You can convert a "K" expression to a "B" expression using c:wrapper(CHECKSIG). For example pk_h(key) = DUP HASH160 <Hash160(key)> EQUALVERIFY;
- " W " wrapper expressions: they take input from the top of the stack and push non-zero (when satisfied) or zero (when not satisfied) to the top of the stack or one below. For example, a 3-input "W" expression would convert the stack "ABCDEF" to "ABF 0" or "AB 0 F" (if not satisfied), and if so, convert to "ABF n" or "AB n F "(n is a non-zero value). Each "W" expression is either s:B (SWAP B) or a:B (TOALTSTACK B FROMALTSTACK). Examples are sc:pk(key) = SWAP CHECKSIG;
Then there are 5 type modifiers that are responsible for guaranteeing additional properties:
- "z"zero arg: This expression always consumes only 0 stack elements;
- "o" One-arg: This expression always consumes only one stack element;
- "n" non-zero: This expression always uses at least one stack element. The dissatisfaction of this expression requires the top input stack element to be zero;
- "d" is not satisfied: the dissatisfaction of this expression can be constructed unconditionally. This means that dissatisfaction cannot include any signature or hash pre-map preimage, nor can it rely on a satisfied time lock;
- "u" unit: When satisfied, this expression will place exactly one on the stack (instead of any non-zero value);
The following table lists the correctness requirements for each Miniscript expression and the type attributes in its subexpressions:
Different types of bitcoin scripts have different resource limitations, whether through consensus or standards. Some of them affect other valid Miniscripts:
- Scripts over 10,000 bytes are invalid due to consensus (bare, P2SH, P2WSH, P2SH-P2WSH);
- Scripts over 520 bytes are invalid due to consensus (P2SH);
- The script satisfies the operation, in which the total number of non-push opcodes plus the number of keys participating in all executed
thresh_msis greater than 201, which is invalid due to consensus (bare, P2SH, P2WSH, P2SH-P2WSH);
- Any content other than c: pk (key) (P2PK), c: pk_h (key) (P2PKH) and thresh_m (k, …) n = 3 is invalid in the standard;
- Scripts over 3600 bytes are invalid due to standards (P2WSH, P2SH-P2WSH);
- Serialized script signature (scriptSig) script with more than 1650 bytes, invalid because of standard (P2SH);
- The witness script consisting of more than 100 stack elements is invalid due to the standard (P2WSH, P2SH-P2WSH);
The existence of MiniScript makes it easy to verify that the script is not affected by these restrictions. Note that this is not the same as verifying that the limit can never be reached (this is also possible). For example, consider
or_b(X,Y) where x and y both need to execute a large number of thresh_ms to satisfaction. In this case, satisfying one of x or y does not exceed the operational limit, and both will be exceeded. Because neither of these needs to be met, the restriction does not prevent satisfaction.
The above type system guarantees that the corresponding bitcoin script is:
- Consensus and standardization completion: Assuming that the resource constraints listed in the previous section are not violated, for each set of semantics to satisfy the conditions, a witness through the Bitcoin consensus rules and the general standard rules can be constructed;
- Consensus sound: Unless the spending conditions are met, it is not possible to build a consensus witness that is valid for the script. Since the standard rules only allow a subset of the consensus to be effectively met, this attribute also implies the sanity of the standard.
By comparing the consensus and standard implementation of Bitcoin Core and verifying the randomness of a large number of random Miniscript expressions, the integrity properties of P2WSH have been extensively tested. Soundness can be inferred by considering all possible execution paths in each fragment script.
In order for these attributes to apply not only to scripts but also to the entire transaction, the witness must submit all data related to the validation. In fact, this means that scripts that satisfy their conditions without any digital signature are not secure. For example, if an output can be used by simply passing an
after（n） fragment in Miniscript
after（n） without any digital signature, the attacker can modify the
nLockTime field in the expense transaction.
For more information, readers can visit: http://bitcoin.sipa.be/miniscript/