When Two Keys Are Both Correct

You've just set up a two-of-three multisig with a single-key timelock fallback. Six months pass. The timelock clears. Now both spending paths are valid, both produce a witness Bitcoin's consensus rules will accept without complaint, and your wallet has to pick one. It's spending your money. Which path does it choose?

That is the problem Miniscript's satisfier algorithm exists to solve. Not "can this script be satisfied?" but "given everything valid, which satisfaction costs the least?" The distinction matters more than most people expect.

What the Satisfier Is Actually Doing

Miniscript is a structured language for writing Bitcoin Script. Its satisfier is the component that, given a Miniscript expression and a set of available cryptographic material (signatures, preimages, timelocks that have passed), produces a concrete witness stack for a transaction input.

The key insight: Miniscript treats satisfaction as an optimization problem, not just a validity check. A script might have three or four internally consistent spending paths. Each path has a different witness size, measured in weight units under SegWit accounting. Smaller witnesses mean lower fees. The satisfier's job is to find the minimum-cost valid witness from the full set.

It works through a recursive bottom-up analysis of the script tree.

Every sub-expression gets assigned a satisfaction and a dissatisfaction, each carrying a cost estimate in witness weight. For a simple `pk(K)` fragment, a satisfaction is a 73-byte DER-encoded signature (72 bytes plus a push opcode). A dissatisfaction is a single zero byte. Those two numbers propagate upward through the tree.

When the satisfier hits a combinator like `and_v(X, Y)`, it needs both X and Y satisfied, so it adds their satisfaction costs. When it hits `or_b(X, Y)`, it needs exactly one satisfied and one dissatisfied. It evaluates both options, satisfy X and dissatisfy Y, or satisfy Y and dissatisfy X, then picks the cheaper branch. This is where multiple valid witnesses enter the picture. An `or` node genuinely offers a choice, and the satisfier quantifies that choice in weight units.

A Worked Example Worth Actually Following

Take this policy:

`or(and(pk(Alice), pk(Bob)), and(pk(Carol), older(1008)))`

Spend either with Alice and Bob together, or with Carol alone after 1008 blocks (roughly one week). Two valid paths.

Suppose all three parties are available and the timelock has passed. The satisfier evaluates both branches.

Branch 1, Alice and Bob: Two signatures. Each witness stack element runs 73 weight units (1-byte length prefix plus 72 bytes). Total satisfaction cost: approximately 146 wu, plus script overhead.

Branch 2, Carol plus timelock: One signature at 73 wu. The `older` fragment adds no witness data at all because it's enforced by the transaction's `nSequence` field, not by witness bytes. Witness cost: approximately 73 wu.

Branch 2 is cheaper by roughly one signature's worth of weight. The satisfier picks Carol's path, even though Alice and Bob are available and willing.

Now change the scenario: the timelock has not passed. Carol's path is no longer valid. The satisfier marks the `older(1008)` fragment as unsatisfiable under current conditions, propagates that unsatisfiability upward through Branch 2, and falls back to Alice and Bob. No ambiguity. One valid path remains.

The Malleability Constraint Nobody Mentions

A valid witness is not automatically a safe witness.

Bitcoin transactions can be malleated by third parties if the witness contains elements that could be replaced with different-but-valid data. Miniscript tracks a property called non-malleability for each satisfaction. The satisfier will not select a malleable satisfaction, even if it's the cheapest option, because a malleable witness can be altered by a miner or relay node to change the transaction ID, breaking dependent unconfirmed transactions and causing downstream grief that is genuinely difficult to untangle.

Sometimes the cheapest witness gets discarded and the second-cheapest non-malleable witness is used instead. If no non-malleable witness exists for any branch, the satisfier signals an error rather than producing a dangerous transaction. This is a deliberate design choice in the reference implementation developed by Pieter Wuille and Andrew Poelstra, and it's exactly the right call.

The malleability analysis runs in parallel with the cost analysis. Every node carries a flag: does the satisfaction for this node have a unique, canonical form? A `pk(K)` satisfaction is non-malleable because a valid signature for key K is either present or absent, no wiggle room. A `threshold(2, pk(A), pk(B), pk(C))` satisfaction can be malleable if the witness doesn't canonically specify which two keys signed, because a third party could swap in a different valid combination. Miniscript's type system enforces canonical orderings to prevent this.

The full selection criterion, then: minimum weight among all non-malleable valid witnesses.

When the Algorithm Must Choose Without Full Information

A wallet running the satisfier doesn't always have everything it needs at signing time. Consider a hardware wallet holding Alice's key. It's asked to sign a transaction, but it doesn't yet know whether Bob will co-sign in time. Does it commit to Branch 1 or Branch 2?

The satisfier handles partial availability through a two-phase model. First pass: collect all available material, mark fragments as satisfiable or not. Second pass: run the cost-minimization recursion over only the satisfiable subtrees.

In practice, a signing coordinator (software that orchestrates a multi-party signing session, such as the role played by tools like Liana or Sparrow Wallet's multisig coordinator) calls the satisfier with the signatures collected so far. The satisfier returns either a complete witness or an indication of what additional material is needed. If two valid paths are both completable with current material, the cheaper one wins.

This is why Miniscript is genuinely useful for complex spending policies and not just an academic curiosity. Think of it as a chess engine for witness construction: you describe the position, it finds the best move, and it does so without needing you to have memorized the endgame tables. The satisfier removes the human judgment call entirely. You don't have to reason about which branch to use.

What People Get Wrong About "Optimal"

The satisfier optimizes witness weight, not total transaction fee. Related, but not identical. Fee rate is negotiated per virtual byte (vbyte), where 4 weight units equal 1 vbyte. The witness discount introduced by SegWit means witness data costs one-quarter the weight of non-witness data, and the satisfier's weight-minimization already accounts for that correctly.

What it does not account for is the difference in overhead between transaction types at the policy level. A Taproot script-path spend has different overhead than a Taproot key-path spend. Miniscript for Taproot (formalized in BIP 379, sometimes called Tapscript Miniscript) introduces the concept of choosing between script-path and key-path spends as an outer optimization layer on top of the Miniscript satisfier. The satisfier handles the inner problem; the wallet policy layer handles the outer one.

The misconception that frustrates most practitioners: assuming the satisfier will always find the globally optimal solution. It will find the optimal solution among the spending paths expressible in Miniscript. If you've written a policy that compiles to an inefficient script, the satisfier cannot compensate. Garbage in, optimally-spent garbage out.

Found your policy spending the expensive path when a cheaper one existed? Check whether your signing coordinator is passing the satisfier a complete picture of available material. The algorithm can only optimize over what it can see.

The Fee Differential Is Real

Consider two users, Marcus and Priya, who both set up identical two-of-three multisig policies with a single-key timelock fallback, using the same Miniscript template. Marcus's wallet software doesn't implement the satisfier correctly and always uses the two-of-three path. Priya's wallet uses the full satisfier and, when the timelock has passed, automatically selects the single-key path.

Over dozens of spends, Priya consistently pays for one signature's worth of witness data instead of two. At 73 weight units per signature, that's roughly 19 extra vbytes per transaction. At a fee rate of 20 sat/vbyte (a moderate illustrative figure), that's 380 satoshis saved per transaction. Trivial individually. Across a treasury wallet making fifty transactions a year, it accumulates. More relevantly, it signals that the wallet software actually understands what it built.

The satisfier isn't magic. It's the part of the stack that turns a well-designed policy into a well-executed spend, every time, without requiring whoever pushes the button to understand the script internals. Bitcoin script is unforgiving, fees are real, and malleability is a subtle knife. The satisfier handles all three by treating witness selection as the optimization problem it always was. That it does so invisibly is not a minor convenience. It's the whole point.