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

Basic Taproot signing support for descriptor wallets #21365

Merged
merged 12 commits into from
Jun 17, 2021

Conversation

sipa
Copy link
Member

@sipa sipa commented Mar 4, 2021

Builds on top of #22051, adding signing support after derivation support.

Nothing is changed in descriptor features. Signing works for key path and script path spending, through the normal sending functions, and PSBT-based RPCs. However, PSBT usability is rather low as no extensions have been defined to convey Taproot-specific information, so all script information must be known to the signing wallet.

@DrahtBot
Copy link
Contributor

DrahtBot commented Mar 4, 2021

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

Conflicts

Reviewers, this pull request conflicts with the following ones:

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.

@sipa
Copy link
Member Author

sipa commented Mar 6, 2021

Rebased on top of #21246, renamed "inner key" everywhere in this PR to "internal key" as well, and added very basic PSBT support (you can have a watchonly tr(KEY) wallet now, and get it signed by an offline version that has the private key).

@Sjors
Copy link
Member

Sjors commented Mar 7, 2021

Concept ACK 🎉

@roconnor-blockstream
Copy link
Contributor

roconnor-blockstream commented Mar 8, 2021

TBH, I find your "must be specified in depth-first search order" somewhat confusing. As an alternative syntax I suggest simply using some notation for a binary or. For example.

  • tr(KEY): only taproot output key known (or is it an internal pubkey with common NUMS tweak??)
  • tr(KEY,A): internal pubkey with a single script A at level 0.
  • tr(KEY,[A|B]): internal pubkey with a two scripts, A and B at level 1.
  • tr(KEY.[[A|B]|C]): internal pubkey with a two scripts, A and B at level 2, and a third script at level 1.
  • tr(KEY.[A|[B|C]]): same as above except for A is at level 1 and C is at level 2 instead.
  • tr(KEY.[[B|C]|A]): equal to the above descriptor.
  • tr(KEY.[[A|B]|[C|D]]): internal pubkey with a four scripts, all at level 2.
  • tr(KEY.[[A|C]|[B|D]]): same four scripts as above, again all at level 2 but now associated differently which makes for a different tweak.
  • tr(KEY.[A|[[B|C]|D]]): same four scripts as above, but no longer all at the same level.

My claimed advantages are the implicitness of the depth, and clearer notation indicating how the scripts are associated amongst each other.

The notation above is just an example and don't care too much about the specific syntax.


Other things to include in the list of features to be added later, a MerkleRoot descriptor for fixed but unknown branches.

Sjors added a commit to jonathanbier/forkmonitor that referenced this pull request Mar 8, 2021
Fixes #147.

We relied on an abandoned Bitcoin Core PR for creating regtest networks with custom consensus rules. We switch to using the Taproot soft fork instead to create invalid block.

We can switch back to Bitcoin Core master once bitcoin/bitcoin#21365 is merged.

After taproot wallet support is merged and released, we can switch back to downloading a Bitcoin Core binary rather than self compiling.

The test framework also underwent some changes, in particular (dis)connect_nodes now takes a numerical argument, and wallets have to manually created.
@sipa
Copy link
Member Author

sipa commented Mar 8, 2021

@roconnor-blockstream Yeah, I considered something like that, and am open to it. Both notations are equivalent in that you can fairly easily convert in both directions between them (counting bracing level in yours corresponds to the depth specified explicitly in mine, with leaves ultimately listed in the same order). The advantage I see to the current one is that I think it's a bit more readable, avoiding the need to go count braces to see relative depth of leaves. Yours is perhaps a bit easier to write (but maybe that's not something we really expect humans to do much for nontrivial cases).

There are many more variations possible. One extreme would be permitting arbitrary weights for every node, and have the descriptor expansion convert it to a Huffman tree. I'd prefer not to do this because it's adding information that can't be inferred back from the script tree it is converted to.

@roconnor-blockstream
Copy link
Contributor

Right. My concern with explicit levels is that it is so easy to violate the invariant that the sum of the weights has to add up to 1, and you cannot really splice together subexpressions to compose a descriptor from fragments without going through and globally adjusting all the levels to be sane.

@sipa
Copy link
Member Author

sipa commented Jun 12, 2021

Rebased.

@achow101
Copy link
Member

re-ACK 458a345

@Sjors
Copy link
Member

Sjors commented Jun 16, 2021

I'll try to review this today. Having this feature in the signet / testnet wallet makes it easier for people to test taproot before it activates, and potentially find problems (bugs, many eyes, shallow, etc). So it seems worth merging even slightly after the feature freeze.

@michaelfolkson
Copy link
Contributor

michaelfolkson commented Jun 16, 2021

Concept ACK, Approach ACK. Updated syntax choice makes sense to me under the readability assumption that use cases that need more than say 2 levels will be rarer. Ignoring readability, the updated syntax choice seems superior due to arguments given.

Agree with @Sjors on importance of getting this in for 22.0. Two weeks for any bug fixes from today should be ok and protections in place (e.g. #22156) regardless to prevent users from spending mainnet Taproot outputs pre-activation (November).

@Sjors
Copy link
Member

Sjors commented Jun 16, 2021

Very light testing using this PR rebased onto #22260: I'm still able to spend from my tr(xpub...) and tr(xpub1, pk(xpub2)) signet descriptor wallets in the GUI.

Copy link
Contributor

@fjahr fjahr left a comment

Choose a reason for hiding this comment

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

Code review ACK 458a345

I don't consider any of my comments blocking. Will test over the next days.

sigdata.taproot_key_path_sig = sig;
}
}
if (sigdata.taproot_key_path_sig.size()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: this could go before the block with sigdata.taproot_key_path_sig.size() == 0 to return early and then that other block would not need that other if statement

Copy link
Member Author

Choose a reason for hiding this comment

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

I tried this for a bit, but I don't find the result cleaner.

@@ -50,11 +52,13 @@ struct FlatSigningProvider final : public SigningProvider
std::map<CKeyID, CPubKey> pubkeys;
std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> origins;
std::map<CKeyID, CKey> keys;
std::map<XOnlyPubKey, TaprootSpendData> tr_spenddata; /** Map from output key to spend data. */
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: m_tr_spenddata?

Copy link
Member Author

Choose a reason for hiding this comment

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

It's a struct, not a class, so the developer notes don't require that (it's inconsistently applied through the codebase, in some cases using m_ prefixes for struct members too, but I don't think that's enough reason to do so here).

src/script/standard.h Show resolved Hide resolved
src/script/sign.h Show resolved Hide resolved
src/script/sign.cpp Show resolved Hide resolved
src/script/sign.cpp Show resolved Hide resolved
Copy link
Member

@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.

ACK 458a345

src/script/interpreter.cpp Show resolved Hide resolved
src/script/interpreter.cpp Show resolved Hide resolved
Copy link
Contributor

@S3RK S3RK left a comment

Choose a reason for hiding this comment

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

two comments

src/key.h Show resolved Hide resolved
src/key.h Show resolved Hide resolved
@meshcollider meshcollider merged commit 5c2e2af into bitcoin:master Jun 17, 2021
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Jun 18, 2021
merkle_root = other.merkle_root;
}
for (auto& [key, control_blocks] : other.scripts) {
scripts[key].merge(std::move(control_blocks));
Copy link
Member

@hebasto hebasto Jun 23, 2021

Choose a reason for hiding this comment

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

On macOS Mojave 10.14.6 (18G9216) system clang fails to compile this line due to the lack of std::set::merge support.

$ clang --version
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.7.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

See: https://stackoverflow.com/questions/50444908/clang-6-not-supporting-unordered-mapmerge

Reported by @S3RK.

Copy link
Member

Choose a reason for hiding this comment

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

I presume this would also make it harder to compile on https://packages.debian.org/stretch/gcc

Copy link
Member

Choose a reason for hiding this comment

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

Since #20413 we support gcc 7+ only, right?

Copy link
Member

Choose a reason for hiding this comment

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

In theory there is https://packages.debian.org/stretch/clang-7, but I don't think anyone was able to compile master with system packages on stretch recently.

Copy link
Member

Choose a reason for hiding this comment

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

Confirmed (for fun) that the issue exists with libc++7.

# make V=1
Making all in src
make[1]: Entering directory '/bitcoin/src'
make[2]: Entering directory '/bitcoin/src'
make[3]: Entering directory '/bitcoin'
make[3]: Leaving directory '/bitcoin'
/usr/bin/ccache clang++-7 -stdlib=libc++ -std=c++17 -DHAVE_CONFIG_H -I. -I../src/config   -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -I. -I./secp256k1/include  -DBOOST_SP_USE_STD_ATOMIC -DBOOST_AC_USE_STD_ATOMIC -I/usr/include -I./leveldb/include -I./leveldb/helpers/memenv -I./univalue/include -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS -DPROVIDE_FUZZ_MAIN_FUNCTION -fdebug-prefix-map=/bitcoin/src=. -Wstack-protector -fstack-protector-all -Wall -Wextra -Wgnu -Wformat -Wformat-security -Wvla -Wshadow-field -Wswitch -Wthread-safety -Wrange-loop-analysis -Wredundant-decls -Wunused-variable -Wunused-member-function -Wdate-time -Wconditional-uninitialized -Wsign-compare -Woverloaded-virtual -Wunreachable-code-loop-increment -Wno-unused-parameter -Wno-self-assign -Wno-unused-local-typedef -Wno-implicit-fallthrough    -fPIE -g -O2 -MT script/libbitcoin_common_a-standard.o -MD -MP -MF script/.deps/libbitcoin_common_a-standard.Tpo -c -o script/libbitcoin_common_a-standard.o `test -f 'script/standard.cpp' || echo './'`script/standard.cpp
script/standard.cpp:410:22: error: no member named 'merge' in 'std::__1::set<std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >, ShortestVectorFirstComparator, std::__1::allocator<std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > >'
        scripts[key].merge(std::move(control_blocks));
        ~~~~~~~~~~~~ ^
1 error generated.
Makefile:7756: recipe for target 'script/libbitcoin_common_a-standard.o' failed
make[2]: *** [script/libbitcoin_common_a-standard.o] Error 1
make[2]: Leaving directory '/bitcoin/src'
Makefile:15489: recipe for target 'all-recursive' failed
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory '/bitcoin/src'
Makefile:820: recipe for target 'all-recursive' failed
make: *** [all-recursive] Error 1

Copy link
Member

Choose a reason for hiding this comment

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

See #22339.

hebasto added a commit to hebasto/bitcoin that referenced this pull request Jun 25, 2021
Since bitcoin#21365 was merged, the support of P0083R3 proposal is required.
hebasto added a commit to hebasto/bitcoin that referenced this pull request Jun 25, 2021
Since bitcoin#21365 was merged, the support of P0083R3 proposal is required.
hebasto added a commit to hebasto/bitcoin that referenced this pull request Jun 25, 2021
Since bitcoin#21365 was merged, the support of P0083R3 proposal is required.
maflcko pushed a commit to maflcko/bitcoin-core that referenced this pull request Jun 26, 2021
6cf4ea7 Avoid the use of P0083R3 std::set::merge (Pieter Wuille)

Pull request description:

  This use was introduced in bitcoin#21365, but as pointed out in bitcoin#22339, this causes compatibility problems.

  Just avoid its use for now.

ACKs for top commit:
  jonatack:
    re-ACK 6cf4ea7
  benthecarman:
    ACK 6cf4ea7
  hebasto:
    ACK 6cf4ea7, successfully compiled on the following systems:

Tree-SHA512: 2b3fdcadb7de98963ebb0b192bd956aa68526457fe5b374c74a69ea10d5b68902763148f11abbcc471010bcdc799e0804faef5f8e8ff8a509b3a053c0cb0ba39
hebasto added a commit to hebasto/bitcoin that referenced this pull request Jun 26, 2021
Since bitcoin#21365 was merged, the support of P0083R3 proposal is required.
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Jun 27, 2021
6cf4ea7 Avoid the use of P0083R3 std::set::merge (Pieter Wuille)

Pull request description:

  This use was introduced in bitcoin#21365, but as pointed out in bitcoin#22339, this causes compatibility problems.

  Just avoid its use for now.

ACKs for top commit:
  jonatack:
    re-ACK 6cf4ea7
  benthecarman:
    ACK 6cf4ea7
  hebasto:
    ACK 6cf4ea7, successfully compiled on the following systems:

Tree-SHA512: 2b3fdcadb7de98963ebb0b192bd956aa68526457fe5b374c74a69ea10d5b68902763148f11abbcc471010bcdc799e0804faef5f8e8ff8a509b3a053c0cb0ba39
fanquake added a commit that referenced this pull request Aug 23, 2021
08f57a0 Assert that IsComplete() in GetSpendData() (Pieter Wuille)
d8f4b97 Remove default nHashTypeIn arguments to MutableTransactionSignatureCreator (Pieter Wuille)
c7048aa Simplify SignTransaction precomputation loop (Pieter Wuille)
addb9b5 Improve comments in taproot signing logic (Pieter Wuille)

Pull request description:

  This addresses a few review comments from #21365 that were left at the time of merge (as well as some from #22166 applying to the commit it shared with #21365).

  I do not think any are blockers for a 22.0 release.

ACKs for top commit:
  theStack:
    re-ACK 08f57a0 🌴
  Zero-1729:
    crACK 08f57a0
  jonatack:
    Code review ACK 08f57a0 per `git range-diff e9d6eb1 9336670 08f57a0` followed by re-code review per commit to swap context back into memory and debug build/run unit tests + feature_taproot.py as a sanity check

Tree-SHA512: 968750109ba8d6faf3016035a38f81c6aefb724c632a3cab0bbf43cf31b9d187b6b0fddd8772768f57338df11eb07ab9c4c6dacdf3cf35b71f29699c67a301ea
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Aug 23, 2021
08f57a0 Assert that IsComplete() in GetSpendData() (Pieter Wuille)
d8f4b97 Remove default nHashTypeIn arguments to MutableTransactionSignatureCreator (Pieter Wuille)
c7048aa Simplify SignTransaction precomputation loop (Pieter Wuille)
addb9b5 Improve comments in taproot signing logic (Pieter Wuille)

Pull request description:

  This addresses a few review comments from bitcoin#21365 that were left at the time of merge (as well as some from bitcoin#22166 applying to the commit it shared with bitcoin#21365).

  I do not think any are blockers for a 22.0 release.

ACKs for top commit:
  theStack:
    re-ACK 08f57a0 🌴
  Zero-1729:
    crACK 08f57a0
  jonatack:
    Code review ACK 08f57a0 per `git range-diff e9d6eb1 9336670 08f57a0` followed by re-code review per commit to swap context back into memory and debug build/run unit tests + feature_taproot.py as a sanity check

Tree-SHA512: 968750109ba8d6faf3016035a38f81c6aefb724c632a3cab0bbf43cf31b9d187b6b0fddd8772768f57338df11eb07ab9c4c6dacdf3cf35b71f29699c67a301ea
gwillen pushed a commit to ElementsProject/elements that referenced this pull request Jun 1, 2022
This introduces Taproot wallet support. I fixed all the merge conflicts
and ensured that the tests pass, but this is still using the old sighash
(before Russell/Sanket/I redid it) so is not actually production ready.
Will be fixed when we bring Elements #1002 in.
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Aug 16, 2022
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.

None yet