Repository navigation
Conversation
A following commit changes how `compile --type tr` builds its script tree (bitcoindevkit#244). Add integration tests for the behavior that must stay the same, so that commit shows them passing unchanged: - a single key, a conjunction and a k-of-n threshold with k > 1 still compile to one leaf - a key reused across branches is rejected, as it is for wsh - a branch that is not safe on every spending path is rejected - the internal key is the randomized unspendable key, never a key from the policy, and `r` is returned These pass on the current code.
`compile --type tr` put the whole policy in a single leaf, so spending any branch of an `or` revealed all the others on-chain. Compile each root-level `or` and `thresh(1, ..)` branch into its own leaf and arrange the leaves as a Huffman tree weighted by branch probability, so only the spent path is revealed. rust-miniscript's `compile_tr` is not used. It takes the internal key from the policy whenever a key can satisfy it alone, which would replace the randomized unspendable key `H + rG` and make `r` prove nothing (bitcoindevkit#218). It also fails on `pk(A)` in miniscript 12.3.7 and 12.3.8 (rust-miniscript#902). Build the tree here from public APIs and leave the internal key as it is. Validate the whole policy before splitting it, as `Concrete::compile` does. Checking branches one at a time would accept a key reused across two of them. Reject policies that compile to more than 1024 leaves, miniscript's limit. Conjunctions, `thresh(k, n)` with k > 1 and single keys still compile to one leaf. Expanding `thresh(k, n)` is left out. Cover it with unit tests for the tree shape and integration tests for the split and the leaf limit. Closes bitcoindevkit#244
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #378 +/- ##
==========================================
+ Coverage 60.00% 60.74% +0.74%
==========================================
Files 25 25
Lines 4736 4828 +92
==========================================
+ Hits 2842 2933 +91
- Misses 1894 1895 +1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Contributor
Author
|
@vadim-anfv, can you please check this out when you are free? |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
compile --type trput the whole policy into a single taproot leaf, so spending any branch of anorrevealed every other branch on-chain. This gives each spending path of a disjunctive policy (or,thresh(1,..)) its own leaf, arranged as a probability-weighted Huffman tree, so only the path that is used is revealed.The internal key is unchanged: it is still the randomized unspendable key
H + rG, andris still returned (#218, #225). Conjunctions,thresh(k,n)withk > 1, and single keys still compile to one leaf.or(pk(A),pk(B))before and after:Closes #244.
Notes to the reviewers
Why not
compile_tr? Itsextract_key()picks a policy key as the internal key whenever one can satisfy the policy alone, and the unspendable key is only a fallback. Foror(pk(A),pk(B))it returnstr(A,pk(B)), sorwould no longer prove anything about the internal key. It also returnsEmpty Miniscript compilationforpk(A)on miniscript 12.3.7 and 12.3.8 (rust-miniscript#902). So the tree is built here from public miniscript APIs, and only the internal key comes from the existing code.How it works.
compile_tap_treeinsrc/utils/descriptors.rs:is_valid()plus the safety checks, ascompile_trdoes).orandthresh(1,..)into(probability, branch)pairs.compile::<Tap>()and runssanity_check().TapTree::combine, capped at 1024 leaves (miniscript's limit). Ties break deterministically.The only change to
compile_policyis the line that built the single leaf. It works for bothDescriptorPublicKeyandString(--placeholders) keys.Whole-policy validation matters. Validating each branch alone accepted
or(pk(A),and(pk(A),pk(B))), whichwshrejects with "duplicate keys". Step 1 prevents that, and there is a test for it.Trade-off. Unlike stock
compile_tr,or(A,B)gets no key-path spend through A. That follows from keeping the verifiable unspendable key required by #218.Commits. There are two:
Testing.
thresh(2)stay one leaf, a key reused across branches is rejected, an unsafe branch is rejected, and the internal key is the unspendable key withrreturned.or, splitthresh(1), 1024 leaves accepted, 1025 rejected) and 8 unit tests on tree shape, including that a likely branch sits nearer the root.test_compile_*tests are untouched and pass.Not covered.
Descriptor::new_trshould reject it.thresh(k,n)intoandleaves (thecompile_tr_private_experimentalbehavior) is left for a follow-up.r. Since this PR does not touch the internal key, it should compose with refactor(compile): extract unspendable key derivation #368. I'll add a test that runsverify_unspendable_internal_keyon split trees once refactor(compile): extract unspendable key derivation #368 merges.Checklists
All Submissions:
cargo fmtandcargo clippybefore committingNew Features: