Skip to content

Tapscript & MuSig2 support, with a miniscript decoder and satisfier#535

Open
odudex wants to merge 9 commits into
ElementsProject:masterfrom
odudex:tapscript-pr
Open

Tapscript & MuSig2 support, with a miniscript decoder and satisfier#535
odudex wants to merge 9 commits into
ElementsProject:masterfrom
odudex:tapscript-pr

Conversation

@odudex

@odudex odudex commented Jun 30, 2026

Copy link
Copy Markdown

This PR adds tapscript (taproot script-path) descriptors, a Script→miniscript decoder
and a witness satisfier, MuSig2 (BIP-327) signing, and the descriptor / PSBT plumbing to
use them end-to-end.

The bulk of this work was authored by @pythcoiner. My own contributions are some bug fixes,
hardening, the amalgamation build integration folded into the relevant commits and a rebase proposed in #533 .

Goals

  • Tapscript descriptorstr() script-path spending: taptree parsing,
    multi_a/sortedmulti_a fragments, BIP-341 merkle-root/leaf hashing, and the
    tree/leaf/control-block accessor APIs.
  • Miniscript — a Script→miniscript decoder and a non-malleable, minimum-weight
    witness satisfier (covering tapscript).
  • MuSig2 (BIP-327) — key aggregation, nonce handling, the full signing pipeline,
    and BIP-328 synthetic-xpub helpers over the secp256k1-zkp musig module.
  • musig() descriptors (BIP-390)tr(musig(...)) parsing, address derivation,
    and participant introspection.
  • PSBT — BIP-371 (taproot) and BIP-373 (PSBT MuSig2) fields, signing, and
    finalization, wired up from descriptors.
  • Keep all of the above available in the single-file amalgamation build, gated so
    consumers building with -DBUILD_STANDARD_SECP (MuSig2 compiled out) still build clean.

Review structure

The original development history (100+ commits) has been reorganized into 9
self-contained commits
, each a coherent milestone with its own tests. The intent is
to make this reviewable and mergeable gradually. Each commit builds, passes its
tests, and can be assessed (or landed) on its own rather than as one monolithic diff.

# Commit What it adds
1 descriptor: add taproot (tapscript) descriptor support tr() script paths: multi_a/sortedmulti_a, taptree parsing & BIP-341 leaf/merkle hashing, script-path address derivation, tree/leaf/control-block accessors
2 miniscript: add Script-to-miniscript decoder Standalone tokenizer + recursive-descent decoder for all fragments, with stack-overflow / non-minimal-push hardening; BIP-379 differential vectors
3 miniscript: add miniscript satisfier Per-fragment non-malleable minimum-weight satisfaction/dissatisfaction with overflow-saturated weights and bounded stack usage
4 musig2: add MuSig2 (BIP-327) core API Key aggregation, nonce gen/agg, signing pipeline, opaque type lifecycle, BIP-328 helpers, bindings; non-aborting handling of malformed cache/session; BIP-327/328 vectors
5 descriptor: add musig() key expressions (BIP-390) musig() as a taproot internal/leaf key, tr(musig(...)) derivation, participant introspection API; BIP-390 vectors
6 psbt: add taproot (BIP-371) and MuSig2 (BIP-373) support Taproot + MuSig2 PSBT fields, script-path sighash & signing, finalizers, descriptor-based population; BIP-373 interop tests
7 fuzz: add fuzzers for musig() descriptor and PSBT MuSig2 fields PSBT MuSig2 fuzzer, musig() descriptor fuzz cases + seed corpus
8 docs: document the MuSig2 API and add a 2-of-2 PSBT example MuSig2 API reference, docs index, CHANGES.md, runnable 2-of-2 MuSig2 PSBT example
9 build: add musig and miniscript sources to the amalgamation Includes the new sources in the single-file build, gated on BUILD_STANDARD_SECP

BIPs implemented

BIP-327 (MuSig2), BIP-328 (xpub for aggregate keys), BIP-341 (Taproot), BIP-371
(PSBT Taproot fields), BIP-373 (PSBT MuSig2 fields), BIP-379 (Miniscript),
BIP-390 (musig() descriptors).

Tests

This branch was integrated in Kern project, where tapscript is under tests.

Musig2 support is not currently being tested in a consumer project. I believe @pythcoiner is baking some ideas and can share a north for practical tests.

pythcoiner and others added 9 commits June 30, 2026 17:03
Adds tr() descriptors with script paths: the WALLY_LEAF_VERSION_TAPSCRIPT
constant, multi_a/sortedmulti_a tapscript fragments, taptree parsing with
BIP-341 merkle-root/leaf hashing, tr() address derivation including the
key-path tweak, and the taproot tree/leaf/control-block/key-enumeration
accessor APIs. Includes the C and Python descriptor test suites.

Co-authored-by: odudex <odudex@proton.me>
Adds a standalone miniscript decoder (miniscript_decode.{c,h}): a Script
tokenizer and recursive-descent decoder for all miniscript fragments
(pk_k/pk_h, hashes, timelocks, multi/multi_a, and_v/and_b, or_b/c/d/i,
andor, thresh, and the wrapper fragments), with stack-overflow and
non-minimal-push hardening. Relocates ms_node and the shared KIND_*
constants into descriptor_int.h so the new translation unit can share
them. Includes the C decoder tests and the BIP-379 differential vectors.

Co-authored-by: odudex <odudex@proton.me>
Adds the witness satisfier (miniscript_satisfy.c): per-fragment
satisfaction/dissatisfaction with non-malleable minimum-weight selection
(satisfaction_best, or_b/c/d/i, andor, thresh with a DP sort) and a
satisfy_node dispatch over the decoded tree, plus the ms_witness /
ms_satisfaction structures and lifecycle helpers. Includes witness_weight
overflow saturation, iterative tree traversal to bound stack usage, and
the geometric-growth / move-not-copy / precomputed-weight optimizations.
Exercised directly by the C satisfy tests.

Co-authored-by: odudex <odudex@proton.me>
Adds the MuSig2 implementation (musig.c) over the secp256k1-zkp musig
module: key aggregation, nonce generation and aggregation, the signing
pipeline (nonce_process, partial_sign, verify, aggregate), and lifecycle
helpers for the opaque keyagg-cache / nonce / session / partial-sig types,
plus BIP-328 synthetic-xpub helpers and the language bindings. Enables the
secp256k1 musig module in the build and routes a malformed parsed
keyagg-cache/session through a non-aborting illegal-argument callback.
Includes the BIP-327/328 vector and protocol test suites.

Co-authored-by: odudex <odudex@proton.me>
Adds the musig() key expression to the descriptor parser (valid only as a
taproot internal key or tapscript leaf key), a format_key_node helper for
serializing key nodes, and the musig() introspection API (participant
count and per-participant keys). This enables parsing and address
derivation for tr(musig(...)) descriptors. Includes the musig() descriptor
parsing / address-generation tests and the BIP-390 vectors.
Adds PSBT support for the new descriptor types. BIP-371 taproot: internal
key, leaf scripts, merkle root and tap bip32 derivation fields; script-path
sighash, signing and descriptor-based taproot population; and the finalizers
(p2wsh, p2tr key/script path, multisig). BIP-373 MuSig2: participant pubkey,
public-nonce and partial-sig fields with nonce generation, partial signing,
aggregation and finalization, plus wally_psbt_populate_musig2_from_descriptor.
Includes the PSBT, MuSig2-PSBT, BIP-373 interop and satisfier differential
test suites.

Co-authored-by: odudex <odudex@proton.me>
Adds a PSBT MuSig2 field fuzzer and musig() cases to the descriptor
fuzzer, with seed corpus for the musig() descriptor parser.
Adds the MuSig2 API reference, wires it into the docs index, notes the
changes in CHANGES.md, and adds a runnable 2-of-2 MuSig2 PSBT signing
example using real randomness and a compressed aggregate key.
Includes musig.c and the miniscript decoder/satisfier in the single-file
amalgamation build, gated on BUILD_STANDARD_SECP for the MuSig2 module.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants