Miniscript: How Blockstream Engineers Are Making Bitcoin Programming Easy(er)
Bitcoin offered the first smart contract programming language the world had ever seen. Script, as this language is called, lets users encode different conditions under which coins can be spent. But while this was a revolutionary concept, it’s not easy to use, especially for more complex spending conditions. Both writing a complex contract as well as verifying that the contract does what it is supposed to do are prone to human error. Yet, especially with money at stake, correct interpretation of the conditions is of the utmost importance.
Over the past year, three blockchain engineers have set out to improve this. By stripping down Script to its bare essentials, their “new” programming language — “Miniscript” — abstracts away the complexity and should make programming on Bitcoin easier and safer for everyone involved.
“Miniscript is, in a theoretical sense, more limiting than script,” Blockstream director of research and Miniscript co-designer Andrew Poelstra told Bitcoin Magazine. “But it can do everything that people actually use script for.”
Let’s start from the beginning.
Every Bitcoin transaction consists of two main parts: inputs and outputs, both of which consist of pieces of code. The inputs “unlock” coins and the outputs “lock them up” again, specifying under which conditions they can be unlocked in a subsequent transaction input. Such requirements usually include a valid cryptographic signature, but there are more possibilities; for example, perhaps a certain amount of time must have passed before a coin can be spent or a specific secret number must be included.
This code in transactions is created with Script, a programming language specifically designed for Bitcoin. Script was inspired by Forth, a programming language invented in the 1960s that was originally designed to operate radio telescopes. Script is adjusted, however, to make it more suited for Bitcoin.
For example, Script doesn’t have an opcode (an instruction) that makes “loops”: the language doesn’t support performing the same computation an unbounded number times. In Bitcoin, there is no need to perform the same computation an unbounded number of times because Bitcoin nodes don’t actually compute transactions — they validate transactions. (For a more technical explanation of why this is the case, see this post by Blockstream engineer Russell O’Connor.)
Script is also “untyped.” This means that outcomes of computations can be interpreted and used in different ways. For example, the outcome of a valid signature can be “true,” but “true” can, in turn, be interpreted and used as a number “1” and subsequently used in math equations: “true” plus “true” would add up to “2,” which could, for example, mean that enough signatures were provided if a minimum of two valid signatures is required.
This brings us to the most important property of Script in the context of this article: it is hard to “reason about.” This essentially means that the results of computations can be interpreted in many ways. Even if a signature is invalid, for example, the Script can be written such that the transaction is still valid for some other reason.
“There are opcodes in Bitcoin Script which do really absurd things,” Poelstra explained. “Like, interpret a signature as a true/false value, branch on that; convert that boolean to a number and then index into the stack, and rearrange the stack based on that number. And the specific rules for how it does this are super nuts.”
This can make Script tricky to work with. Especially if requirements to spend (“unlock”) coins become more complex, the author of a transaction may unintentionally include something in the code that allows the coins to be spent under different conditions than intended. Conversely, the recipient of a transaction may fail to notice such a quirk and lose his coins to an attacker who does notice.
A CONCRETE EXAMPLE OF A PROBLEM
Here is a concrete example of how these problems limit Script’s usefulness.
The Blockstream Green wallet has a standard “cosigning” setup. The wallet user controls one of two keys, and Blockstream controls the other. The funds can be spent in two ways. First, whenever the user wants to spend a coin, they sign the transaction and request that Blockstream signs it as well. Blockstream would usually do this, though this might require that the user confirms they really want to make the transaction through a secondary means, like an email confirmation. But something might go wrong on Blockstream’s end — perhaps the company disappears or loses its key, or it cannot sign for some other reason. In that case, the user still has a fallback solution to spend their bitcoin: After a timelock has expired, they can create a valid transaction after some predetermined time has passed. Perhaps a month.
This works fine, but it’s also limited. The user cannot use any more of Bitcoin’s smart contract potential, even though they may want to add more flexibility on their end of the setup.
“Right now Green has a fixed script that it uses for all customers, which is basically just a simple multi-signature,” Poelstra said. “But really, we shouldn’t care what the Script says. What we care about is: before some timeout, is it impossible for the coins to be spent without our signature? If the user wants to use some crazy policy with us, we should be able to support it, as long as that one condition we care about is met.”
The user may, for example, want to allow their loved ones to spend the coin after a year has passed, in case they pass away. Or maybe the user is actually a company, and it wants to create a multisig setup where any two out of three board members can together spend the coins (in combination with Blockstream).
Currently, this could technically be possible with Bitcoin Script. However, it would require that the user designs a custom setup, and Blockstream would need to partake in this custom setup.
“But if the user gives us an arbitrary script, it’s impossible for us to tell whether that one condition we care about it [is] met, because the total set of all script behaviors is really complicated,” Poelstra explained. “For example, if a script seems to take a signature, we need to think about what happens if the user gives a non-signature. Can it be tricked into letting the coins be spent?”
Over the past year, Miniscript was designed by Poelstra, Blockstream Core tech engineer Pieter Wuille and Blockstream intern Sanket Kanjalkar. (Miniscript is not officially a Blockstream product, however.)
In short, Miniscript is a “stripped down” version of Script: a selection of “tools” from the “Script toolkit” that makes it easier to use and easier to verify by humans. The tools are carefully selected to enable practically anything that can be done with Script — there are only some fringe exceptions that no one actually makes use of anyway. So while a line of Miniscript is still a valid line of Script, it essentially avoids human error by preventing unexpected, perhaps unintended, outcomes of the code.
Taking the example of a problem above, with Miniscript, a user can easily design a setup in such a way that Blockstream can trivially check that its one condition is met. Specifically, Blockstream can see that the funds can only ever be spent if it signs or if a month has passed — no matter which other conditions are included on the user’s end of the setup, be it extra timelocks or multisigs or anything else. With Miniscript, there can be no unexpected quirks that would override Blockstream’s end.
Miniscript is so straightforward and predictable, in fact, that the setup can be turned into a decision tree: a visualization (“pictural encoding”) of the setup, which is very easy to reason about.
The visualization below, for example, shows a setup where the user’s end includes the company board members, in which two out of three need to sign. Blockstream’s end is the same as always: It either needs to sign or wait for some length of time to pass.
Visualization of Miniscript
“With Miniscript it is easy for Blockstream to participate in more complex setups — we decode the script into a tree, then we check every leaf of the tree, asking (a) does this leaf have a timeout condition on it?; or (b) does this leaf require one of our signatures?” Poelstra said.
If the answer is yes to both questions, Blockstream can participate.
MINISCRIPT IN USE
While Miniscript is a work in progress, early versions of it have been released and are ready to be used.
To make the process of writing Miniscript even easier, Wuille also designed a “policy language.” The policy language is really like a programming language of its own. After programming the conditions under which a coin can be spent in this policy language, it can be compiled (“translated”) into Miniscript, and therefore into valid Script, to be included in a Bitcoin transaction output.
A big added benefit of this policy language is that it automatically compiles into the best, most efficient version of Miniscript possible, depending on what the Script actually encodes.
“The thing about Miniscript is that it’s basically Script … you have a ton of different ways to write ‘or,’ a ton of ways to write ‘and’ and some are more efficient than others,” Poelstra said. “The policy language only has one ‘or,’ one ‘and’ and so forth, and Pieter [Wuille] has written this super optimized compiler which will convert that to Miniscript for you, and do it in the optimal way.”
This isn’t just a theoretical concept. Even though the current version of Miniscript and the compiler are not final versions, Blockstream is using it internally for the development branch of its Liquid sidechain functionary software. (Poelstra pointed out that use of Wuille’s optimizing compiler saved Blockstream 22 bytes versus its original, “hand-rolled” Script.) Wuille hosts a demo version of a policy language to the Miniscript compiler for anyone to use on http://bitcoin.sipa.be/miniscript/.