The PR branch HEAD was ec72f35 at the time of this review club meeting.
This is a 2-part Review Club. Since we haven’t covered Miniscript before, we first take some time to get familiar with the general concepts before diving into the actual PR.
In the first part, we’ll disregard output descriptors and look at Miniscript in general and some of the changes this PR introduces. We’ll focus on the first 6 commits from “miniscript: remove a workaround for a GCC 4.8 bug” to “miniscript: split ValidSatisfactions from IsSane”.
In the second part, we’ll look at the Miniscript output descriptor implementation. We’ll focus on the last 9 commits from “miniscript: tiny doc fixups” to “qa: functional test Miniscript watchonly support”.
Some of the questions refer to changes introduced in the PR’s predecessor #24147 which introduced the bulk of the Miniscript logic into Bitcoin Core, so it may be helpful to review that PR too. It also contains a more detailed overview of the various PRs involved in merging Miniscript into the Bitcoin Core codebase.
Miniscript is a language for writing (a subset of) Bitcoin Scripts in a structured way, enabling analysis, composition, generic signing and more. It is not to be confused with the policy language on top of Miniscript which looks similar to Miniscript, but is out of scope for this PR. Andrew Poelstra has a helpful video on “Getting Started with Miniscript”.
Output script descriptors are strings that contain all the information necessary to allow a wallet or other program to track payments made to or spent from a particular script or set of related scripts (i.e. an address or a set of related addresses such as in an HD wallet).
Descriptors combine well with Miniscript in allowing a wallet to handle tracking and signing for a larger variety of scripts. Since Bitcoin Core 23.0 descriptor wallets have become the default wallet type.
Which type of analysis enabled by Miniscript would be helpful for which use case or application?
What would be a valid Miniscript for a spending policy that unconditionally locks the UTXO for 21 blocks, and then requires a 2-of-3 multisig from Alice, Bob, or Carol? (Note: the Miniscript homepage and https://min.sc/ have easy-to-use tooling available to construct Miniscripts)
What does it mean when a node is “sane” or “valid”? Do they mean the same thing?
What does it mean for an expression to be non-malleably satisfiable? After SegWit, why do we still need to worry about malleability?
Why does Compare now use a non-recursive algorithm, whereas previously the Node::operator== operator was recursive? What is the largest size that queue in Compare can ever grow?
How do we keep track of a Node’s type and type properties? Why don’t we just declare them as regular class members? Can we instantiate a Node with multiple type properties at once?
<stickies-v> welcome everyone! This and next week we're looking at #24148 (https://bitcoincore.reviews/24148) which introduces Miniscript support for Output Descriptors. Today we're focusing on general Miniscript concepts, and some of the changes introduced in #24148.
<stickies-v> disclaimer: I picked tis PR just because I'm excited about the potential Miniscript brings, but I'm still relatively new to this codebase - so please keep me honest and I welcome all of your input/corrections :-)
<darosior> For instance, the analysis of the maximum witness size is helpful for "second layer" protocols to assign fee bumping reserves, since they can then estimate the worst case size of the transaction (if not signed with exotic sighash types).
<stickies-v> OliverOffing: yeah absolutely, one of the (hand-crafted) transaction templates used on LN (I believe it's the commitment tx?) was found to be slightly suboptimal thanks to Miniscript analysis
<darosior> OliverOffing: in general, since Miniscript is only a subset of Script some policies tend to be more optimizable "by hand". But then you lose all the guarantees given by Miniscript for just a few witness units. :)
<stickies-v> personally I think composition is really interesting, where multiple parties (e.g. in an advanced kind of multi-sig) can provide complex subexpressions without everyone having to understand the other party's spending conditions
<darosior> __gotcha: from the website, "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. "
<darosior> __gotcha: then to not lock yourself out of your funds you also want completeness "consensus and standardness complete: Assuming the resource limits listed in the previous section are not violated and there is no timelock mixing, for every set of met conditions that are permitted by the semantics, a witness can be constructed that passes Bitcoin's
<stickies-v> michaelfolkson: it was (intentionally) a bit of a trick question, but that's *policy* you posted instead of Miniscript. We use both in the discussion here, but just wanted to highlight that there is a difference. Does everyone understand the difference?
<OliverOffing> Found this on StackExchange: "We use the term valid for any correctly typed Miniscript. And we use the term safe for any sane Miniscript, ie one whose satisfaction isn't malleable, which requires a key for any spending path, etc."
<OliverOffing> darosior: I understand most resource limitation points there, but what is this one? "Anything but pk(key) (P2PK), pkh(key) (P2PKH), and multi(k,...) up to n=3 is invalid by standardness (bare)."
<theStack> is there any plan to implement the equivalent of an "inline assembler" expression, e.g. something like "bare_script(OP_FOO OP_BAR...)"? (not that i can think of a good use-case, just a random thought :D)
<stickies-v> I guess to summarize sanity, it needs to be valid, consensus and standardness-compliant (e.g. number of operations and script size), have non-malleable solutions, not mix different timelock units (block/time), and not have duplicate keys
<stickies-v> alright time for the next question, we've already spoken about malleability a bit. What does it mean for an expression to be non-malleably satisfiable? After SegWit, why do we still need to worry about malleability?
<stickies-v> oh I missed __gotcha 's answer, yes exactly having a different witness can affect transaction size, and since the absolute fee amount is fixed (that part is not malleable), that would afffect the tx's fee rate - and thus it's ability to get propagated and priority to get mined into a block, which can be problematic
<darosior> What if a transaction spends a Miniscript which contains a hash using another path? It needs to "dissatisfy" this hash. Once this transaction is broadcast, what can a node on the network do if they want to be a pain?
<stickies-v> to make it specific, I think an example of a policy of which the Miniscript does not have a satisfaction that's guaranteed to be non-malleable is `or(and(older(21), pk(A)), thresh(2, pk(A), pk(B)))`
<stickies-v> thank you all for bringing your A game today. Unfortunately we're out of time for this session, but there's more Miniscript joy next week. Same place, same time! Thank you again to darosior and sipa for guiding us all through this.