Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Miniscript support in Output Descriptors #24148

Merged
merged 5 commits into from
Jul 14, 2022

Conversation

darosior
Copy link
Member

@darosior darosior commented Jan 25, 2022

This adds Miniscript support for Output Descriptors without any signing logic (yet). See the OP of #24147 for a description of Miniscript and a rationale of having it in Bitcoin Core.
On its own, this PR adds "watchonly" support for Miniscript descriptors in the descriptor wallet. A follow-up adds signing support.

A minified corpus of Miniscript Descriptors for the descriptor_parse fuzz target is available at bitcoin-core/qa-assets#92.
The Miniscript descriptors used in the unit tests here and in #24149 were cross-tested against the Rust implementation at https://github.com/rust-bitcoin/rust-miniscript.

This PR contains code and insights from Pieter Wuille.

@DrahtBot
Copy link
Contributor

DrahtBot commented Jan 26, 2022

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #25540 (miniscript: avoid wasteful computation, prevent memory blowup when fuzzing by darosior)
  • #24361 (Descriptor unit tests and simplifications by sipa)
  • #23480 (Add rawtr() descriptor for P2TR with specified (tweaked) output key by sipa)
  • #22838 (descriptors: Be able to specify change and receiving in a single descriptor string by achow101)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

@darosior
Copy link
Member Author

Rebased.

@darosior
Copy link
Member Author

Rebased.

@darosior darosior force-pushed the miniscript_wallet_watchonly branch from be0ce4b to 957452d Compare March 18, 2022 13:51
laanwj added a commit that referenced this pull request Apr 5, 2022
2da94a4 fuzz: add a fuzz target for Miniscript decoding from Script (Antoine Poinsot)
f836999 Miniscript: ops limit and stack size computation (Pieter Wuille)
2e55e88 Miniscript: conversion from script (Pieter Wuille)
1ddaa66 Miniscript: type system, script creation, text notation, tests (Pieter Wuille)
4fe2936 script: expose getter for CScriptNum, add a BuildScript helper (Antoine Poinsot)
f4e289f script: move CheckMinimalPush from interpreter to script.h (Antoine Poinsot)
31ec6ae script: make IsPushdataOp non-static (Antoine Poinsot)

Pull request description:

  Miniscript is a language for writing (a subset of) Bitcoin Scripts in a structured way.

  Miniscript permits:
  - To safely extend the Output Descriptor language to many more scripting features thanks to the typing system (composition).
  - Statical analysis of spending conditions, maximum spending cost of each branch, security properties, third-party malleability.
  - General satisfaction of any correctly typed ("valid" [0]) Miniscript. The satisfaction itself is also analyzable.
  - To extend the possibilities of external signers, because of all of the above and since it carries enough metadata.

  Miniscript guarantees:
  - That for any statically-analyzed as "safe" [0] Script, a witness can be constructed in the bounds of the consensus and standardness rules (standardness complete).
  - That unless the conditions of the Miniscript are met, no witness can be created for the Script (consensus sound).
  - Third-party malleability protection for the satisfaction of a sane Miniscript, which is too complex to summarize here.

  For more details around Miniscript (including the specifications), please refer to the [website](https://bitcoin.sipa.be/miniscript/).

  Miniscript was designed by Pieter Wuille, Andrew Poelstra and Sanket Kanjalkar.
  This PR is an updated and rebased version of #16800. See [the commit history of the Miniscript repository](https://github.com/sipa/miniscript/commits/master) for details about the changes made since September 2019 (TL;DR: bugfixes, introduction of timelock conflicts in the type system, `pk()` and `pkh()` aliases, `thresh_m` renamed to `multi`, all recursive algorithms were made non-recursive).

  This PR is also the first in a series of 3:
  - The first one (here) integrates the backbone of Miniscript.
  - The second one (#24148) introduces support for Miniscript in Output Descriptors, allowing for watch-only support of Miniscript Descriptors in the wallet.
  - The third one (#24149) implements signing for these Miniscript Descriptors, using Miniscript's satisfaction algorithm.

  Note to reviewers:
  - Miniscript is currently defined only for P2WSH. No Taproot yet.
  - Miniscript is different from the policy language (a high-level logical representation of a spending policy). A policy->Miniscript compiler is not included here.
  - The fuzz target included here is more interestingly extended in the 3rd PR to check a script's satisfaction against `VerifyScript`. I think it could be further improved by having custom mutators as we now have for multisig (see #23105). A minified corpus of Miniscript Scripts is available at bitcoin-core/qa-assets#85.

  [0] We call "valid" any correctly-typed Miniscript. And "safe" any sane Miniscript, ie one whose satisfaction isn't malleable, which requires a key for any spending path, etc..

ACKs for top commit:
  jb55:
    ACK 2da94a4
  laanwj:
    Light code review ACK 2da94a4 (mostly reviewed the changes to the existing code and build system)

Tree-SHA512: d3ef558436cfcc699a50ad13caf1e776f7d0addddb433ee28ef38f66ea5c3e581382d8c748ccac9b51768e4b95712ed7a6112b0e3281a6551e0f325331de9167
@darosior
Copy link
Member Author

darosior commented Apr 5, 2022

I (slightly) messed up the last rebase here, but there was some updates in the meantime so i'll push a fix along with those soon.

A nit, but was helpful when writing unit tests for Miniscript parsing
@darosior darosior force-pushed the miniscript_wallet_watchonly branch from 49ac002 to d30202c Compare July 14, 2022 10:04
darosior and others added 2 commits July 14, 2022 12:11
Miniscript descriptors are defined under P2WSH context (either `wsh()`
or `sh(wsh())`).
Only sane Miniscripts are accepted, as insane ones (although valid by
type) can have surprising behaviour with regard to malleability
guarantees and resources limitations.
As Miniscript descriptors are longer and more complex than "legacy"
descriptors, care was taken in error reporting to help a user determine
for what reason a provided Miniscript is insane.

Co-authored-by: Pieter Wuille <pieter.wuille@gmail.com>
@darosior darosior force-pushed the miniscript_wallet_watchonly branch from d30202c to ffc79b8 Compare July 14, 2022 10:12
@darosior
Copy link
Member Author

darosior commented Jul 14, 2022

Thanks @w0xlt, good catch: it's the unit test for FindInsaneSub i just added that i had only tested on the tip of the branch. I've swapped the two first commits now and they all individually compile and pass the tests.

@Sjors

interestingly even BOOST_CHECK(ms_ins); fails.

i was missing an a: wrapper for the second after, i've added a check that the Miniscript is valid (which does not change the test since an invalid sub is necessarily insane):

diff --git a/src/test/miniscript_tests.cpp b/src/test/miniscript_tests.cpp
index f5bea00e1e..bc5c49ef63 100644
--- a/src/test/miniscript_tests.cpp
+++ b/src/test/miniscript_tests.cpp
@@ -299,10 +299,10 @@ BOOST_AUTO_TEST_CASE(fixed_tests)
     // 2. It contains timelock mixes
     // We'll report the timelock mix error, as it's "deeper" (closer to be a leaf node) than the "no 's' property"
     // error is.
-    const auto ms_ins = miniscript::FromString("or_i(and_b(after(1),after(10000000)),pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204))", CONVERTER);
-    BOOST_CHECK(ms_ins && !ms_ins->IsSane());
+    const auto ms_ins = miniscript::FromString("or_i(and_b(after(1),a:after(1000000000)),pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204))", CONVERTER);
+    BOOST_CHECK(ms_ins && ms_ins->IsValid() && !ms_ins->IsSane());
     const auto insane_sub = ms_ins->FindInsaneSub();
-    BOOST_CHECK(insane_sub && *insane_sub->ToString(CONVERTER) == "and_b(after(1),after(10000000))");
+    BOOST_CHECK(insane_sub && *insane_sub->ToString(CONVERTER) == "and_b(after(1),a:after(1000000000))");
 
     // Timelock tests
     Test("after(100)", "?", TESTMODE_VALID | TESTMODE_NONMAL); // only heightlock

I also took the opportunity to fix the two nits @Sjors had previously.

@Sjors
Copy link
Member

Sjors commented Jul 14, 2022

It's still a bit weird that a BOOST_CHECK doesn't just fail but hits a "no mapping at fault address" when the miniscript can't be parsed. You're following the same pattern as other tests though.

@Sjors
Copy link
Member

Sjors commented Jul 14, 2022

re-utACK ffc79b8

Copy link
Contributor

@w0xlt w0xlt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reACK ffc79b8

Copy link
Member

@instagibbs instagibbs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only reviewed the tests and interface, non blocking nits only

)

self.log.info("Testing we detect funds sent to one of them")
addr = self.ms_wo_wallet.getnewaddress()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imo makes more sense to do the rest of the test against an address we explicitly checked was derived correctly(instead of the 3rd one)

)
self.ms_wo_wallet = self.nodes[0].get_wallet_rpc("ms_wo")

# Sanity check we wouldn't let an insane Miniscript descriptor in
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, what I mean is "assuming this is merged, as a reader of the test, what is this?" i.e. should include link or self contained definition

@achow101
Copy link
Member

ACK ffc79b8

@achow101 achow101 merged commit 85b601e into bitcoin:master Jul 14, 2022
achow101 added a commit that referenced this pull request Jul 15, 2022
d751beb Release notes for Miniscript support in P2WSH descriptors (Antoine Poinsot)

Pull request description:

  Changelog for #24148.

ACKs for top commit:
  Sjors:
    ACK d751beb
  achow101:
    ACK d751beb
  w0xlt:
    ACK d751beb

Tree-SHA512: 5ecdc8501fdacca35b33f7425dbc192860e3e061bc9287b682c55d6da210cc5c0ff7154629e453a9a8d528bad518c35c49de31d114acab77bf27449940e9ca04
Copy link
Contributor

@sanket1729 sanket1729 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

post-merge ACK. Did not review the tests. I like the error reporting by looking for inner insane sub.

sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Jul 18, 2022
ffc79b8 qa: functional test Miniscript watchonly support (Antoine Poinsot)
bfb0367 Miniscript support in output descriptors (Antoine Poinsot)
4a08288 qa: better error reporting on descriptor parsing error (Antoine Poinsot)
d25d58b miniscript: add a helper to find the first insane sub with no child (Antoine Poinsot)
c38c7c5 miniscript: don't check for top level validity at parsing time (Antoine Poinsot)

Pull request description:

  This adds Miniscript support for Output Descriptors without any signing logic (yet). See the OP of bitcoin#24147 for a description of Miniscript and a rationale of having it in Bitcoin Core.
  On its own, this PR adds "watchonly" support for Miniscript descriptors in the descriptor wallet. A follow-up adds signing support.

  A minified corpus of Miniscript Descriptors for the `descriptor_parse` fuzz target is available at bitcoin-core/qa-assets#92.
  The Miniscript descriptors used in the unit tests here and in bitcoin#24149 were cross-tested against the Rust implementation at https://github.com/rust-bitcoin/rust-miniscript.

  This PR contains code and insights from Pieter Wuille.

ACKs for top commit:
  Sjors:
    re-utACK ffc79b8
  achow101:
    ACK ffc79b8
  w0xlt:
    reACK bitcoin@ffc79b8

Tree-SHA512: 02d919d38bb626d3c557eca3680ce71117739fa161b7a92cfdb6c9c432ed88870b1ed127ba24248574c40c7428217d7e9bdd986fd8cd7c51fae8c776e1271fb9
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Jul 18, 2022
…scriptors

d751beb Release notes for Miniscript support in P2WSH descriptors (Antoine Poinsot)

Pull request description:

  Changelog for bitcoin#24148.

ACKs for top commit:
  Sjors:
    ACK d751beb
  achow101:
    ACK d751beb
  w0xlt:
    ACK bitcoin@d751beb

Tree-SHA512: 5ecdc8501fdacca35b33f7425dbc192860e3e061bc9287b682c55d6da210cc5c0ff7154629e453a9a8d528bad518c35c49de31d114acab77bf27449940e9ca04
@Sjors
Copy link
Member

Sjors commented Jul 19, 2022

Opening a miniscript-descriptor wallet in an older version of Bitcoin Core causes a crash. (How) did we handle backward compatibility for tr() descriptors? Do we want to set a flag each time? Of have some generic mechanism to abort loading a wallet that has unrecognised descriptor / miniscript fragment?

assert(m_out);
Key key = m_keys.size();
auto pk = ParsePubkey(key, {&*begin, &*end}, ParseScriptContext::P2WSH, *m_out, m_key_parsing_error);
if (!pk) return {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get two compiler warnings on master from this, gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0:

script/descriptor.cpp: In function ‘miniscript::NodeRef<Key> miniscript::internal::DecodeScript(I&, I, const Ctx&) [with Key = unsigned int; Ctx = {anonymous}::KeyParser; I = __gnu_cxx::__normal_iterator<std::pair<opcodetype, std::vector<unsigned char> >*, std::vector<std::pair<opcodetype, std::vector<unsigned char> > > >]’:
script/descriptor.cpp:1243:17: warning: ‘<anonymous>’ may be used uninitialized in this function [-Wmaybe-uninitialized]
 1243 |         return {};
      |                 ^
script/descriptor.cpp: In function ‘miniscript::NodeRef<Key> miniscript::internal::Parse(Span<const char>, const Ctx&) [with Key = unsigned int; Ctx = {anonymous}::KeyParser]’:
script/descriptor.cpp:1208:26: warning: ‘<anonymous>’ may be used uninitialized in this function [-Wmaybe-uninitialized]
 1208 |         if (!pk) return {};

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that's a known false positive in certain GCC versions.

@bitcoin bitcoin deleted a comment Jul 20, 2022
@bitcoin bitcoin deleted a comment Jul 20, 2022
@bitcoin bitcoin deleted a comment Jul 20, 2022
@S3RK
Copy link
Contributor

S3RK commented Jul 21, 2022

Opening a miniscript-descriptor wallet in an older version of Bitcoin Core causes a crash. (How) did we handle backward compatibility for tr() descriptors? Do we want to set a flag each time? Of have some generic mechanism to abort loading a wallet that has unrecognised descriptor / miniscript fragment?

Not sure how it was done with tr() descriptors. But before CWallet::SetMinVersion was used to handle wallet compatibility with various software versions.

sipa added a commit to sipa/miniscript that referenced this pull request Feb 16, 2023
930e2f2 Update the Miniscript fuzz tests from Bitcoin Core master (Antoine Poinsot)
f62abcf Update the Miniscript unit tests from Bitcoin master (Antoine Poinsot)
fd9bed0 miniscript: update the source from Bitcoin Core master branch (Antoine Poinsot)

Pull request description:

  The PRs introducing Miniscript to Bitcoin Core contained the most up to date version of the code (bitcoin/bitcoin#24148 and bitcoin/bitcoin#24149).

  Now they are both merged, update the code here to reflect the updates made in these PRs.

ACKs for top commit:
  sipa:
    ACK 930e2f2. Matches the Bitcoin Core master branch code, and compiles.

Tree-SHA512: 0ef587d005dc472cada86b99e62f37aa6bbfa09a8d1db95726a0103c0e827880d557ebce39c87eeb281c952b77fcf462c9a779008eed5b2d0f2d63478f4fab4c
@bitcoin bitcoin locked and limited conversation to collaborators Sep 8, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.