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

Output descriptors/miniscript support #310

Merged
merged 1 commit into from
Feb 10, 2023
Merged

Output descriptors/miniscript support #310

merged 1 commit into from
Feb 10, 2023

Conversation

jgriffiths
Copy link
Contributor

@jgriffiths jgriffiths commented Dec 17, 2021

Based on the excellent work of @k-matsuzawa in #202 (which this PR replaces).

The major changes from the original PR are:

  • Move to a parse-first API to support multiple operations/queries without re-parsing.
  • Use wally_map for variable substitutions.
  • Use the new _n api call variants to work directly from the input string
  • Remove all sub-allocations while parsing
  • Remove script generation allocations completely in most cases, otherwise from 1MB to typically less than 1k.
  • Expose descriptor canonicalization, network and features
  • API support for future ranged descriptors
  • Expose to SWIG targets
  • Extend the test cases
  • Various bug fixes, speedups, simplifications

@k-matsuzawa I would be very grateful if you could review when you have time.

Note: This is rebased on #365, and will be updated until that branch is merged to master. Only the last commit is specific to this PR.

This was referenced Dec 17, 2021
@jgriffiths
Copy link
Contributor Author

Note that the parsing code still allocates a large buffer for each child when generating scripts; I intend to fix this by computing the exact required length up-front in the future. Since this is an internal change, it won't affect the exposed API/ABI.

@jgriffiths jgriffiths force-pushed the descriptor branch 4 times, most recently from 2ea4eef to 5b5ed0b Compare December 17, 2021 07:41
@darosior
Copy link

I still plan to review this. (Sorry for taking so long with #202..)

Copy link
Contributor

@k-matsuzawa k-matsuzawa left a comment

Choose a reason for hiding this comment

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

Thanks! I reviewed it.
I commented on one thing. Other than that it looks good!

Note that the parsing code still allocates a large buffer for each child when generating scripts; I intend to fix this by computing the exact required length up-front in the future. Since this is an internal change, it won't affect the exposed API/ABI.

I'm sorry. 🙏

src/descriptor.c Outdated Show resolved Hide resolved
@jgriffiths
Copy link
Contributor Author

I'm sorry.

The implementation is totally suitable for typical use, please don't apologise! My desire to reduce these allocations is only for the special case of using wally on constrained devices like Jade and the bitbox02, but I don't expect external contributors to have to optimise for that when submitting!

@Sjors
Copy link
Contributor

Sjors commented Dec 20, 2021

Would it make sense to split out Miniscript parsing and do that later / separate? I thought there were still some pending changes to the Miniscript syntax to make it compatible with descriptors? bitcoin/bitcoin#16800 (comment)

@darosior
Copy link

The Miniscript syntax for P2WSH is now final. The pkh() and pk() aliases, as well as the rename of thresh_m() to multi() made the syntax of Miniscript descriptors backward compatible with "legacy" descriptors.

@jgriffiths jgriffiths changed the title Output descriptors/miniscript support WIP: Output descriptors/miniscript support Dec 23, 2021
@jgriffiths
Copy link
Contributor Author

Note: Moving back to WIP, I think we need a couple more features.

src/bip32.c Outdated Show resolved Hide resolved
@LeoComandini
Copy link
Contributor

utACK 99b25e0 (just the first commit "bip32: Allow public path derivation with hardened child elements"), with comment

@Sjors
Copy link
Contributor

Sjors commented Jan 19, 2022

There's a couple of functions that I would find quite useful, for sanity checking a multisig wallet:

  • infer the (sub)descriptor type(s): e.g. it's a witness script hash (KIND_DESCRIPTOR_SH) with a sorted multisig (KIND_DESCRIPTOR_MULTI_S )
  • for multisig: get the threshold value
  • extract xpub(s)
  • know if it's ranged, and if not: which index
  • infer the network (though you can't distinguish testnet, regtest and signet, and you can't tell from a hex public key, so maybe this is not realistic)

All of that currently happens internally in descriptor.c. Could wait for a followup.

Copy link
Contributor

@Sjors Sjors left a comment

Choose a reason for hiding this comment

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

I was able to wrap two of the new functions and run some rudimentary sanity checks on them in Sjors/libwally-swift#57

include/wally_descriptor.h Show resolved Hide resolved
include/wally_descriptor.h Show resolved Hide resolved
@Sjors
Copy link
Contributor

Sjors commented Feb 7, 2022

Similar to what I'm suggesting in bitcoin/bitcoin#24147 (comment), instead of introducing all of Miniscript you could start with the subset that's needed for current descriptor functionality: KIND_MINISCRIPT_PK(_K), KIND_MINISCRIPT_PK(H/_H), and KIND_MINISCRIPT_MULTI. You only need TYPE_B and TYPE_K and none of the PROP_ logic.

That allows you to introduce a bunch of plumbing, without the need for reviewers to understand all of Miniscript. And in combination with my suggestions above, it's already practically useful for building single- and simple multisig wallets in a way that's compatible with what's out there (Specter, ColdCard, etc).

@darosior
Copy link

For the Miniscript part, how about we look into making https://github.com/sipa/miniscript/ a library and exposing a C API?

@jgriffiths
Copy link
Contributor Author

For the Miniscript part, how about we look into making https://github.com/sipa/miniscript/ a library and exposing a C API?

C++ code is not acceptable for wally, and our implementation requirements are stricter than some other implementations; because we need to work on embedded devices and hardware wallets we need to constrain both code size and stack/memory usage for exposed functionality.

@darosior
Copy link

darosior commented May 6, 2022

Is it still WIP? What is left to be done there? Any way i can help this work to move forward?

@jgriffiths
Copy link
Contributor Author

Is it still WIP? What is left to be done there? Any way i can help this work to move forward?

Hi @darosior sorry for the late reply. This PR is still WIP for 2 reasons:

1 - The interface currently only allows generating one script type; I'd like to extend this to be able to generate scriptsigs, scriptpubkeys and witness scripts (as well as tapscript in due course)
2 - The creation of scripts internally currently requires a lot of memory which needs fixing in order to work well on smaller devices like the Jade or Bitbox02

Item 1 is a pre-requisite for merging as it will change the API interface slightly. 2 is not blocker for merging as the internals can be refactored once the interface is stable.

I am currently working on PSBT v2/PSET support for wally, and hope to be able to re-visit this following that. Unfortunately I have rather extreme demands on my time at present.

Loopsbetas2173

This comment was marked as spam.

@jgriffiths
Copy link
Contributor Author

Hi @k-matsuzawa my apologies for taking so long to get back to this PR. I have updated it further, including changing the API, and hope to get this into the upcoming 0.8.8 release.

I hope you have have time to review this new version, and will switch to it in due course. In the event that you will remain with your own implementation for a time, please see the individual commits squashed for this PR at https://github.com/ElementsProject/libwally-core/tree/descriptor_backup_7_feb_23. There are some bug fixes there which you may wish to pick up.

Compared to the initial version, this now uses a parsed representation as the descriptor object rather than passing around the string. This is needed to prevent re-parsing overhead for multiple operations/queries on the descriptor. The large upfront memory allocation for script generation has been removed. In most cases no allocations are now needed to generate. When allocation is required, the length allocated is reduced to the maximum length needed to contain all sub-scripts, which is typically in the order of 1kb or so.

I have also merged in your branches extra fixes as you can see from the link above.

@k-matsuzawa
Copy link
Contributor

@jgriffiths Thanks!
I was able to quickly check out the latest implementation and confirm that it works the same way. I will replace it when it is released 0.8.8!

@jgriffiths
Copy link
Contributor Author

@k-matsuzawa thanks, looking forward to merging this shortly :)

@jgriffiths jgriffiths changed the title WIP: Output descriptors/miniscript support Output descriptors/miniscript support Feb 7, 2023
@jgriffiths
Copy link
Contributor Author

@Sjors FYI

@Sjors
Copy link
Contributor

Sjors commented Feb 7, 2023

Nice! It looks like the first 5 commits could be split off, so review can focus on the actual descriptors / miniscript stuff?

include/wally_descriptor.h Outdated Show resolved Hide resolved
@jgriffiths
Copy link
Contributor Author

Nice! It looks like the first 5 commits could be split off, so review can focus on the actual descriptors / miniscript stuff?

Yes, sorry if it isn't clear, the first commits are a separate and will be merged to master shortly, I'm keeping this PR rebased over master for easier testing by reviewers.

@Sjors
Copy link
Contributor

Sjors commented Feb 7, 2023

Can you update the PR description to point to #365? That seems to be where those commits are living.

@jgriffiths
Copy link
Contributor Author

Can you update the PR description to point to #365?
Done, sorry for the confusion.

Copy link
Contributor

@Sjors Sjors left a comment

Choose a reason for hiding this comment

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

I was able to rebase my LibWallySwift implementation on the latest changes. It uses only a small fraction of the functionality though.

Some features that are still on my wish list, but can wait for a followup:

  1. Determine the descriptor type (e.g. sorted_multi()) and output type (legacy, p2sh segwit, native segwit, taproot)
  2. Obtain the (extended) keys
  3. Obtain the threshold for (sorted_multi()) and other such variables

include/wally_descriptor.h Show resolved Hide resolved
@jgriffiths
Copy link
Contributor Author

infer the network (though you can't distinguish testnet, regtest and signet, and you can't tell from a hex public key, so maybe this is not realistic)

@Sjors its turns out this is required in order to allow address generation from untyped expressions. Please see the latest commits around get_network/set_network for details.

Co-authored-by: Jon Griffiths <jon_p_griffiths@yahoo.com>
@jgriffiths
Copy link
Contributor Author

OK, I've squashed the recent commits in this branch and backed up the un-squashed one in the descriptor_backup tree for review/posterity. This includes a new change to make use of a parsed descriptor thread-safe.

That's all the time I have for this release. AFAICS the API is now sufficient to support future work like multi-path and @Sjors wish list items.

@Sjors Please raise a bug to track your wish list so they aren't lost when this is merged, thanks.

@jgriffiths jgriffiths merged commit b6bf352 into master Feb 10, 2023
@jgriffiths jgriffiths deleted the descriptor branch February 10, 2023 13:00
@Sjors Sjors mentioned this pull request Feb 10, 2023
3 tasks
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.

None yet

6 participants