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

When are sanity_checks supposed to be run? #734

Open
apoelstra opened this issue Aug 31, 2024 · 2 comments
Open

When are sanity_checks supposed to be run? #734

apoelstra opened this issue Aug 31, 2024 · 2 comments

Comments

@apoelstra
Copy link
Member

On the Miniscript type, which users are not really supposed to use directly, we run sanity checks (e.g. all branches are "safe") on from_str. You have to use from_str_ext or from_str_insane to override this.

However, we don't do the same for Descriptor::from_str, which simply parses a tree then calls Miniscript::from_tree, bypassing all sanity checks. So this seems backward -- by default for the users-shouldn't-use-this type we have sanity checks, while for descriptors, you have to manually call the sanity_check method.

However^2, for Taproot descriptors we do run the sanity checks on parsing, because we have bizarre special-purpose parsing logic which actually calls Miniscript::from_str for individual tapbranches rather than calling Miniscript::from_tree.

You can see this with the following unit test

commit 966524264e948b3e68f68a10eba513692f2839e6
Author: Andrew Poelstra <apoelstra@wpsoftware.net>
Date:   Sat Aug 31 20:36:29 2024 +0000

    f unit test

diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs
index 0b0bd02b..72f533f7 100644
--- a/src/descriptor/mod.rs
+++ b/src/descriptor/mod.rs
@@ -2003,6 +2003,12 @@ pk(03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8))";
         Descriptor::<DescriptorPublicKey>::from_str("wsh(andor(pk(tpubDEN9WSToTyy9ZQfaYqSKfmVqmq1VVLNtYfj3Vkqh67et57eJ5sTKZQBkHqSwPUsoSskJeaYnPttHe2VrkCsKA27kUaN9SDc5zhqeLzKa1rr/0'/<0;1;2;3>/*),older(10000),pk(tpubD8LYfn6njiA2inCoxwM7EuN3cuLVcaHAwLYeups13dpevd3nHLRdK9NdQksWXrhLQVxcUZRpnp5CkJ1FhE61WRAsHxDNAkvGkoQkAeWDYjV/8/<0;1;2>/*)))").unwrap_err();
     }

+    #[test]
+    fn taproot_s_check() {
+        Descriptor::<DescriptorPublicKey>::from_str("sh(or_i(pk(0202baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a66a),1))").unwrap();
+        Descriptor::<DescriptorPublicKey>::from_str("tr(02baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a66a,1)").unwrap_err();
+    }
+
     #[test]
     fn test_context_pks() {
         let comp_key = bitcoin::PublicKey::from_str(

We have the identical policy expressed as a sh (can parse no problem) or as a tr (will not parse, complains about the sigless `1( branch).

@apoelstra
Copy link
Member Author

Related #653 -- we should not have sanity_check methods that you have to know/remember to call.

Related #723 -- we should drop the Ctx parameter, unify our sanity checking, and carry these checks along with objects.

@apoelstra
Copy link
Member Author

Curiously, in the bare segwitv0 and sh modules we call Ctx::top_level_checks to check that the top-level expression is B. The Tr context does not even define this method. Instead the B check is done explicitly in Miniscript::from_str_ext (and also explicitly in Miniscript::parse_ext which duplicates a bunch of logic). Madness.

apoelstra added a commit to apoelstra/rust-miniscript that referenced this issue Sep 1, 2024
…= 12.x

See rust-bitcoin#734 for
discussion of this. Meanwhile, add a unit test so we can determine when
the behavior changes.
apoelstra added a commit that referenced this issue Sep 3, 2024
1259375 miniscript: make display prefer 'u' over 'l' in the fragment l:0 (Andrew Poelstra)
67fdc50 descriptor: reject strings of the form "tr(<key>,)" (Andrew Poelstra)
00cac40 descriptor: add unit test demonstrating sanity-checking behavior in <= 12.x (Andrew Poelstra)

Pull request description:

  This PR has three changes which are mostly unrelated except that they were all found when fuzzing my "rewrite expression parser to be nonrecursive" branch against 12.x.

  * First is a unit test demonstrating #734. It doesn't fix anything, just tests the current behavior.
  * Second is a fix for #736 (backported in #735).
  * Third tweaks the new `Display` code from #722 to change how the ambiguous `l:0`/`u:0` is serialized, to match 12.x.

ACKs for top commit:
  sanket1729:
    ACK 1259375

Tree-SHA512: 921d65a1efd49bda0f9db488a2854b004e14518f584d832497a9dbc13a845ceec99544375385570c6ac42d4985277e8dcbb3aa8654de93235cf9067ba601f91d
apoelstra added a commit to apoelstra/rust-miniscript that referenced this issue Sep 3, 2024
The `expression::Tree` type now parses expression trees containing both
{} and () as children. It marks which is which, and it is the
responsibility of FromTree implementors to make sure that the correct
style is used.

This eliminates a ton of ad-hoc string parsing code from
descriptor/tr.rs, including 8 instances of Error::Unexpected. It is also
the first change that preserves (sorta) a pubkey parsing error rather
than converting it to a string.

Because of rust-bitcoin#734
this inserts a call to `Descriptor::sanity_check` when parsing a string,
specifically for Taproot descriptors. This is weird and wrong but we
want to address it separately from this PR, whose goal is to preserve
all behavior.

This also introduces the new `error` module and `ParseError` enum, which
we will move all string-parsing errors into. For now we just stick it in
the big global enum, but eventually it will live on its own.
apoelstra added a commit to apoelstra/rust-miniscript that referenced this issue Nov 13, 2024
The `expression::Tree` type now parses expression trees containing both
{} and () as children. It marks which is which, and it is the
responsibility of FromTree implementors to make sure that the correct
style is used.

This eliminates a ton of ad-hoc string parsing code from
descriptor/tr.rs, including 8 instances of Error::Unexpected. It is also
the first change that preserves (sorta) a pubkey parsing error rather
than converting it to a string.

Because of rust-bitcoin#734
this inserts a call to `Descriptor::sanity_check` when parsing a string,
specifically for Taproot descriptors. This is weird and wrong but we
want to address it separately from this PR, whose goal is to preserve
all behavior.
apoelstra added a commit to apoelstra/rust-miniscript that referenced this issue Nov 13, 2024
The `expression::Tree` type now parses expression trees containing both
{} and () as children. It marks which is which, and it is the
responsibility of FromTree implementors to make sure that the correct
style is used.

This eliminates a ton of ad-hoc string parsing code from
descriptor/tr.rs, including 8 instances of Error::Unexpected. It is also
the first change that preserves (sorta) a pubkey parsing error rather
than converting it to a string.

Because of rust-bitcoin#734
this inserts a call to `Descriptor::sanity_check` when parsing a string,
specifically for Taproot descriptors. This is weird and wrong but we
want to address it separately from this PR, whose goal is to preserve
all behavior.
apoelstra added a commit to apoelstra/rust-miniscript that referenced this issue Nov 23, 2024
The `expression::Tree` type now parses expression trees containing both
{} and () as children. It marks which is which, and it is the
responsibility of FromTree implementors to make sure that the correct
style is used.

This eliminates a ton of ad-hoc string parsing code from
descriptor/tr.rs, including 8 instances of Error::Unexpected. It is also
the first change that preserves (sorta) a pubkey parsing error rather
than converting it to a string.

Because of rust-bitcoin#734
this inserts a call to `Descriptor::sanity_check` when parsing a string,
specifically for Taproot descriptors. This is weird and wrong but we
want to address it separately from this PR, whose goal is to preserve
all behavior.
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

No branches or pull requests

1 participant