Basic Taproot signing support for descriptor wallets #21365

pull sipa wants to merge 12 commits into bitcoin:master from sipa:202102_taproot_sign changing 28 files +594 −65
  1. sipa commented at 10:49 pm on March 4, 2021: member

    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.

  2. sipa force-pushed on Mar 4, 2021
  3. DrahtBot commented at 11:19 pm on March 4, 2021: member

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

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #22166 (Add support for inferring tr() descriptors by sipa)
    • #21702 (Implement BIP-119 Validation (CheckTemplateVerify) by JeremyRubin)
    • #21576 (rpc: bumpfee signer support by Sjors)
    • #21283 (Implement BIP 370 PSBTv2 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.

  4. DrahtBot added the label Consensus on Mar 5, 2021
  5. DrahtBot added the label Docs on Mar 5, 2021
  6. DrahtBot added the label RPC/REST/ZMQ on Mar 5, 2021
  7. DrahtBot added the label Scripts and tools on Mar 5, 2021
  8. DrahtBot added the label Utils/log/libs on Mar 5, 2021
  9. DrahtBot added the label Wallet on Mar 5, 2021
  10. sipa force-pushed on Mar 5, 2021
  11. DrahtBot added the label Needs rebase on Mar 5, 2021
  12. sipa force-pushed on Mar 6, 2021
  13. sipa commented at 3:17 am on March 6, 2021: member
    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).
  14. DrahtBot removed the label Needs rebase on Mar 6, 2021
  15. Sjors commented at 6:34 pm on March 7, 2021: member
    Concept ACK 🎉
  16. roconnor-blockstream commented at 2:32 pm on March 8, 2021: contributor

    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.

  17. Sjors referenced this in commit 771c1345d6 on Mar 8, 2021
  18. sipa commented at 6:17 pm on March 8, 2021: member

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

  19. roconnor-blockstream commented at 6:25 pm on March 8, 2021: 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.
  20. roconnor-blockstream commented at 7:44 pm on March 8, 2021: contributor
    Additionally I understand that it is the case that checking that the weights add up to 1 is not a sufficient condition to check that a string is a well-formed descriptor as it doesn’t imply that the arrangement is in some DFS order. Thus I argue that my proposed scheme is better because the well-formedness of the descriptor format is a relatively simple syntactic check (that would pass LANGSEC scrutiny).
  21. JeremyRubin commented at 7:50 pm on March 8, 2021: contributor

    +1 @roconnor-blockstream a bracketed version seems superior to me.

    Some BIP-level (assuming this would eventually become a BIP) thoughts on things that might be useful in a descriptor:

    1. it might make sense to have a node that’s a Need-To-Know-Basis Subtree. N2KBS have some danger – e.g., could be hiding a backdoor key – but also can be used to simplify a descriptor for e.g. inter-operating with an Escrow service that you don’t want to share the whole program with.
    2. I think with brackets it’s also easier to attach a subtree modifier. E.g., we could spec the lexing to recognize [a-zA-Z0-9_]*( or something similar as a subtree modifier. E.g., [A,B] -> huffman[A@p,B@q].

    The advantage of having this baked in is great for readability – often times you might want to express a tree as:

    [Thing that should happen most likely, in the unlikely event that doesn’t happen, Other thing that could happen, other thing we might want to do in unlikely event]

    which would – if depth order is required – look like:

    [Thing that should happen most likely, Other thing that could happen, in the unlikely event that doesn’t happen, other thing we might want to do in unlikely event]

    Which reminds me of exception throwing where the error handling source code can be very far away from the source of the error (bad).

    (something like huffman does require that we also use a sorted Tree to select clauses lexicographically after probability). 3. An annotation/tag interface that would allow you to do something like [A#{escrow, alice, bob}, B#{alice, bob}] which when ‘blind_for(“escrow”)’ is called creates the [A, N2KB] descriptor. 4. Maybe a more general way of inserting extended structured metadata that Core will store/pass along but might not understand for supporting things like 3… 5. a opaque[] annotation, which compiles a subtree opaquely to any parent modifiers. This would be useful in contexts where a subtree might be known entirely already by another party and we want to preserve that structure

  22. sipa commented at 8:01 pm on March 8, 2021: member

    @JeremyRubin I agree that there is a potential use for omitted subtrees, but I don’t think there is much of a difference between the two approaches there? In the current approach you’d just specify the Merkle root of the omitted subtree as a “leaf” at the depth that root exists at.

    All the other things you mention seem to be things for a policy language that get compiled to descriptors rather than descriptors themselves. Descriptors are for encoding information potentially needed by signers; I can’t imagine a need for information of that kind that applies a specific subtree (rather than applying to individual leaves, or to the entire tree).

    That said, it’s not hard to implement a nested-bracket approach; I’ll try to implement that too so people can experiment with both.

    My main reason for having a slight preference for the explicit depth style is that I think tr(KEY,[A|[[B|C]|D]]) is much harder to interpret by a human than tr(KEY,1:A,3:B,3:C,2:D). In the latter it’s immediately clear that B and C are the least likely paths, and A is the most likely one, for example. I agree with the argument that it’s harder to write, but I’m not sure that’s a big problem; for nontrivial things I suspect they’ll either be written once as a template, or generated from a higher-level specification.

  23. JeremyRubin commented at 8:49 pm on March 8, 2021: contributor

    I think it just depends on what descriptors are for v.s. what people end up using them for. Of that I am unsure, but it’s worthwhile to note that this is a human readable format so presumably at some point a human may look at it or want to edit it.

    w.r.t. annotations for “descriptor blinding”, I actually think this is somewhat relevant. One could imagine a scenario where a branch is used both as a pre-image for a has revelation and as a script. In that case, you might not want to reveal the entire tree to someone as it might give them the ability to spend from that branch as well.

    w.r.t. brackets or list, it might be interesting to make the descriptors a NEWICK format subset, which seems to be the most commonly used tree format (and is a bracket based format). Then you could dump descriptors into standard tree viewing software easily. I couldn’t find a good reference for tree file formats using a list/depth approach.

    edit: w.r.t omitted subtrees, as noted, this was just some BIP-level feedback as we look to expanding what can go into a tr descriptor, less so comments on implementation.

  24. sipa commented at 2:55 am on March 9, 2021: member
    @JeremyRubin @roconnor-blockstream Added a commit to switch to “tr(KEY,{S,{{S,S},S}})” notation. I prefer {} over [] because the latter is already in use for key origin information.
  25. sipa force-pushed on Mar 9, 2021
  26. JeremyRubin commented at 6:57 pm on March 9, 2021: contributor

    Thanks!

    Is there any reason not to use parens ()? NEWICK compatibility would be fun.

  27. in src/util/spanparsing.cpp:37 in 12bfa0b79e outdated
    33@@ -34,11 +34,11 @@ Span<const char> Expr(Span<const char>& sp)
    34     int level = 0;
    35     auto it = sp.begin();
    36     while (it != sp.end()) {
    37-        if (*it == '(') {
    38+        if (*it == '(' || *it == '{') {
    


    JeremyRubin commented at 7:12 pm on March 9, 2021:

    don’t we need to trace what type we are in if it is a paren or a {?

    E.g., (} would pass here no?


    sipa commented at 7:13 pm on March 9, 2021:
    That doesn’t matter. This function is just for finding the end of the current expression. If that expression is unbalanced, the actual parsing code will fail.

    JeremyRubin commented at 8:22 pm on March 9, 2021:

    I see.

    bikeshed: I think something like this could be a little clearer:

     0// This function finds the end of the current expression and returns pair(expr_span, rest_span)
     1// trailing comma is included in the expr_span, e.g.
     2// `<expr A>,<expr B>` would return `<expr A>,` `<expr B>` 
     3std::pair<Span<const char>, Span<const char>> Expr(const Span<const char>& sp)
     4{
     5    size_t left = 0;
     6    size_t right = 0;
     7    auto it = sp.begin();
     8    while (it != sp.end()) {
     9        switch (*it) {
    10        case '(':
    11        case '{':
    12            ++left;
    13            break;
    14        case '}':
    15        case ')':
    16            ++right;
    17            break;
    18        }
    19        bool balanced = right == left;
    20        // completed a Expr by matching nonzero left/right
    21        if (balanced && right > 0) break;
    22        // we have a balanced term as a comma separated entity
    23        if (balanced && *it == ',') break;
    24        ++it;
    25    }
    26    return std::make_pair(sp.first(it-sp.begin), sp.subspan(it-sp.begin()));
    27}
    

    That said it seem peculiar to me that , is left on the string – maybe preferable to trim it here?


    sipa commented at 9:34 pm on March 11, 2021:
    That’s how the current span-based parsing works. I think discussions about improvements to that are interesting but out of scope for this PR.
  28. in src/script/descriptor.cpp:1162 in 12bfa0b79e outdated
    1172+                        return nullptr;
    1173+                    }
    1174+                    continue;
    1175+                }
    1176+                auto sarg = Expr(expr);
    1177+                auto subdesc = ParseScript(key_exp_index, sarg, ParseScriptContext::P2TR, out, error);
    


    JeremyRubin commented at 8:29 pm on March 9, 2021:
    you could directly emplace_back the subdesc and then nullcheck/return nullptr, rather than needing to std::move it.

    sipa commented at 0:27 am on March 12, 2021:
    Done.
  29. in src/script/descriptor.cpp:1173 in 12bfa0b79e outdated
    1183+                        error = strprintf("tr(): expected '}' after script expression");
    1184+                        return nullptr;
    1185+                    }
    1186+                    counts.pop_back();
    1187+                }
    1188+                if (counts.size()) {
    


    JeremyRubin commented at 8:36 pm on March 9, 2021:

    Behavior Note:

    I believe this would be more robust to go before the while loop – it shouldn’t come up, because we’re in a binary tree maybe – but it would be better to explicitly check:

    0if (counts.size() && counts.back() == false) 
    

    or something like

    0if (counts.size() && counts.back() <= 2)
    

    if we want to limit how many terms in a nest are allowed.

    right now this invariant is implicitly checked by the order of the code and it is non-obvious to me.


    sipa commented at 0:26 am on March 12, 2021:
    I’ve restructured things a bit, and added a bunch of comments. Does this look better?
  30. in src/script/descriptor.cpp:839 in d5a0dce8fe outdated
    847+    std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript> scripts, FlatSigningProvider& out) const override
    848+    {
    849+        TaprootBuilder builder;
    850+        assert(m_depths.size() == scripts.size());
    851+        for (size_t pos = 0; pos < m_depths.size(); ++pos) {
    852+            builder.Add(m_depths[pos], scripts[pos], TAPROOT_LEAF_TAPSCRIPT);
    


    JeremyRubin commented at 8:46 pm on March 9, 2021:
    given that this is just iterating in order, it seems like we should be able to directly feed the TaprootBuilder from the tr parsing code and do away with the intermediate vectors?

    sipa commented at 8:47 pm on March 9, 2021:
    No, unfortunately this happens at a different layers. TaprootBuilder can only be invoked with concrete keys; those don’t exist yet at parsing time.

    JeremyRubin commented at 8:52 pm on March 9, 2021:

    I see :(

    I guess I’ll mark it as a personal todo (no need to upset a working internal representation) to brainstorm if there’s a more obvious way to represent the tree than the ordered depth annotations (even though it’s equivalent and unambiguous, I still find it unintuitive)


    sipa commented at 9:05 pm on March 9, 2021:
    I like linear data structures that don’t need recursion to process, when unnecessary.

    JeremyRubin commented at 0:41 am on March 10, 2021:

    Yeah, I agree with that 100%. What I was thinking was a vec where each bit represents a left/right on the tree. That way you can do a de-duplicate check to make sure each node is only occupied once. Then the order of the vec itself is no longer significant; it should not need to be preserved because you can sort them by value (if I’m not wrong) to get the insertion order. Depth could be computed by looking at your nearest neighbor and computing the “divergence” (or by encoding an extra byte to make it explicit – unclear what is best here).

    With a sorted vec, this also enables some nicesties such as using std:: binary searches for O(log(n)) lookup by tree position.


    JeremyRubin commented at 1:08 am on March 10, 2021:

    @roconnor-blockstream may also like that this format should be:

    1. concatenative, where concat has operations that are O(n)
      • merge sort then either deduplicate check or run through and add an appropriate offset to branches
    2. “normalizable” (shifting things down to the smallest next number) and “prefixable” (adding a constant to all numbers to preserve structure.

    sipa commented at 1:14 am on March 10, 2021:
    Why do the properties of this internal, temporary, data structure that purely used during the construction of a scriptPubKey matter? It does what it is supposed to do. If we ever need other operations on it, something else can be considered.

    JeremyRubin commented at 6:54 am on March 10, 2021:

    I find the code really hard to understand. Conceivably if we want people to review/audit it, a conceptually simpler representation & set of algorithms should be easier to understand? The above suggestion may or may not be it, but I haven’t been able to fully work out how the algorithms this PR is currently using work even after staring at them for a bit.

    the properties matter less.


    sipa commented at 7:12 am on March 10, 2021:
    I’ll try to add more comments.

    sipa commented at 0:26 am on March 12, 2021:
    Added a big comment explaining the data structure in script/standard.h. WDYT?

    sipa commented at 1:58 am on May 24, 2021:
    Marking this as resolved. Feel free to comment if the result still isn’t clear.
  31. in src/script/descriptor.cpp:944 in d5a0dce8fe outdated
    935@@ -886,6 +936,13 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const S
    936                     error = "Uncompressed keys are not allowed";
    937                     return nullptr;
    938                 }
    939+            } else if (data.size() == 32 && ctx == ParseScriptContext::P2TR) {
    940+                unsigned char fullkey[33] = {0x02};
    941+                std::copy(data.begin(), data.end(), fullkey + 1);
    942+                pubkey.Set(std::begin(fullkey), std::end(fullkey));
    943+                if (pubkey.IsFullyValid()) {
    944+                    return MakeUnique<ConstPubkeyProvider>(key_exp_index, pubkey);
    


    fanquake commented at 5:48 am on March 11, 2021:
    nit: in d5a0dce8fe40153b07ec6e882e8b1568b05e3905: we can use std::make_unique in new code.

    sipa commented at 0:26 am on March 12, 2021:
    Done.
  32. sipa force-pushed on Mar 12, 2021
  33. sipa commented at 0:27 am on March 12, 2021: member
    Switched to the tr(KEY,{{S1,S2},{{...) style descriptors.
  34. sipa force-pushed on Mar 12, 2021
  35. DrahtBot added the label Needs rebase on Mar 12, 2021
  36. Sjors commented at 8:24 am on March 12, 2021: member

    Switched to the tr(KEY,{{S1,S2},{{...) style descriptors.

    Can you clarify this, maybe with a simple example, in the PR description or in descriptors.md?

  37. sipa force-pushed on Mar 12, 2021
  38. sipa commented at 9:23 am on March 12, 2021: member

    @Sjors Rebased, and

    Can you clarify this, maybe with a simple example, in the PR description or in descriptors.md?

    done.

  39. DrahtBot removed the label Needs rebase on Mar 12, 2021
  40. in doc/descriptors.md:95 in ee7b9831ad outdated
    90     - Optionally followed by a single `/*` or `/*'` final step to denote all (direct) unhardened or hardened children.
    91     - The usage of hardened derivation steps requires providing the private key.
    92 
    93+`TREE` expressions:
    94+- any `SCRIPT` expression
    95+- An open brace `{`, a `TREE` expression, a comma `,`, a `TREE` expression, and a closing brace `}`
    


    Sjors commented at 9:49 am on March 12, 2021:

    So this means you can do, tr(KEY,{A}), tr(KEY,{A,B}), tr(KEY,{A,{B,C}}), etc? So a TREE must have two elements, unless it’s at the top?

    Maybe also point out that some taproot features have yet to be implemented, so the only script you can put in a TREE element is pk(KEY).


    sipa commented at 4:46 pm on March 12, 2021:

    No, {A} is not a valid TREE. A is.

    So you’d use tr(KEY,A) but tr(KEY,{A,B}). There is nothing special about the top level.

  41. DrahtBot commented at 4:49 pm on March 15, 2021: member

    🕵️ @achow101 @harding have been requested to review this pull request as specified in the REVIEWERS file.

  42. in src/script/descriptor.cpp:862 in 8aaa61bfb7 outdated
    850+    {
    851+        for (size_t pos = 0; pos < m_depths.size(); ++pos) {
    852+            ret += strprintf(",%i:", m_depths[pos]);
    853+            std::string tmp;
    854+            if (!m_subdescriptor_args[pos]->ToStringHelper(arg, tmp, priv, normalized)) return false;
    855+            ret += std::move(tmp);
    


    achow101 commented at 9:25 pm on March 15, 2021:

    In commit 8aaa61bfb7590e95eb7ca5106dfe3561f1c7a7c8 “Add tr() descriptor (derivation only, no signing)”

    This looks like the original syntax and not the syntax with the braces.

    Since this function is used as part of writing the descriptor to the wallet, any wallet containing a descriptor with scripts will currently have an invalid descriptor and cannot be loaded again.


    sipa commented at 6:02 am on March 16, 2021:
    Nice catch. Fixed, and added tests for roundtrippability of the descriptors.
  43. in src/script/descriptor.cpp:713 in 8aaa61bfb7 outdated
    709+            return Vector(GetScriptForRawPubKey(keys[0]));
    710+        }
    711+    }
    712 public:
    713-    PKDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), "pk") {}
    714+    PKDescriptor(std::unique_ptr<PubkeyProvider> prov, bool xonly) : DescriptorImpl(Vector(std::move(prov)), "pk"), m_xonly(xonly) {}
    


    achow101 commented at 9:27 pm on March 15, 2021:

    In commit 8aaa61bfb7590e95eb7ca5106dfe3561f1c7a7c8 “Add tr() descriptor (derivation only, no signing)”

    I think it would be better to default xonly = false so that existing callers don’t need to change, and this is kind of what is expected for this descriptor. Then nested taproot things can set xonly as they need.


    sipa commented at 6:29 pm on March 16, 2021:
    Done.
  44. in src/psbt.cpp:238 in 1d3cc29fb8 outdated
    250-        COutPoint prevout = tx.vin[index].prevout;
    251-        if (prevout.n >= input.non_witness_utxo->vout.size()) {
    252-            return false;
    253+    bool have_all_spent_outputs = true;
    254+    std::vector<CTxOut> utxos(tx.vin.size());
    255+    for (size_t idx = 0; idx < tx.vin.size(); ++idx) {
    


    achow101 commented at 10:11 pm on March 15, 2021:

    In 1d3cc29fb86b614b76b4d97a5adab7f903fc3020 “Construct and use PrecomputedTransactionData in PSBT signing”

    We should avoid iterating all of the PSBT inputs again when we are signing each input. Instead we should compute the PrecomputedTransactionData in the caller and pass that into SignPSBTInput.

    This can be done in CWallet::FillPSBT where we are iterating the inputs anyways to get their previous txs from the wallet. We could be constructing the PrecomputedTransactionData at that time and then passing it into FIllPSBT which then passes it to SignPSBTInput.


    sipa commented at 10:48 pm on March 16, 2021:
    Done.
  45. sipa force-pushed on Mar 16, 2021
  46. sipa force-pushed on Mar 16, 2021
  47. sipa force-pushed on Mar 16, 2021
  48. sipa force-pushed on Mar 17, 2021
  49. DrahtBot added the label Needs rebase on Mar 17, 2021
  50. sipa force-pushed on Mar 30, 2021
  51. sipa commented at 1:20 am on March 30, 2021: member
    Rebased.
  52. DrahtBot removed the label Needs rebase on Mar 30, 2021
  53. Sjors commented at 3:24 pm on April 1, 2021: member

    I created a descriptor like so, to test scriptpath spending: tr(tpub1/0/*,pk(tprv2/0/*)). This way the wallet won’t have private keys for the keypath spend (tpub1), so it has to use the script pk(tprv2/0*).

    This seems to work on Signet, but the transaction does not enter the mempool. testmempoolaccept complains min relay fee not met, so perhaps there’s a fee calculation bug? Once I bump the fee it goes through.

    I used the GUI to create (and bump) the transactions. With a regular tr(tpriv1/0/*) descriptor wallet I don’t see this problem.

    Here’s the original raw transaction:

    00200000000010114a5df5cf5084ea003c2f021c4e22846238a61c89894fa786e556395048ed40c0000000000fdffffff0292a0070000000000225120a287e30feacc24a25a9a3e689ad0107223cb247fb9a14880cddf3e62f1453a6420a1070000000000160014bf15b210d9f53e5c7b002fe904af042098706c3a03406c11b79f9a417f9702a6117e3bb170956ece09af6b584b2d92e1e0e5cfcad725250c91e29db77751af36970c0e5676622cfcf6fb1c81a316dfcd349b1ea59d412220d0586a7eba2e92a38f31ba8a7d82c05b9bb101bf3a649f205bd8e1df7c428b6eac21c07a35becbfeccf3ed5367ff117681497d040e6996eeae3dd21acb123e8525729d227b0000
    
  54. sipa commented at 5:05 pm on April 1, 2021: member

    @Sjors That’s expected, and I don’t really know how to deal with this. The problem is that we determine the fee for spending an output by using the dummy signer, but the dummy signer can’t know which private keys will and won’t be available. Since tr spending cost differs based on what is available, this will give an inaccurate result (it’ll always just use the key path).

    A possibility is extending descriptors with markers to indicate that certain keys won’t ever be available. It’s good to point this out, but I think the scope will take us a bit too far for this PR.

  55. Sjors commented at 8:43 pm on April 1, 2021: member

    This is definitely a new can of worms that will need some thought, but I agree it can wait (if documented).

    The main point of having leaves is to be able to spend when the main keypath key material isn’t available. But we currently have no way of indicating that.

    I don’t think descriptors are the right place for this. Let’s say I have 3 hardware wallets. The keypath is a 3-of-3 musig, and the script(s) are 2-of-3. Normally I would sign with all 3 in order to save fees and preserve privacy. But after a minor boating accident I only have two. I wouldn’t necessarily want to create a new wallet, so nothing about the descriptors changes.

    The natural place to indicate which leaf to use is probably the send command (especially fun for the GUI…). From there on it should be straight forward to use the right dummies.

  56. MarcoFalke removed the label Docs on Apr 2, 2021
  57. fjahr commented at 11:43 am on April 2, 2021: member
    Concept ACK 🚀
  58. DrahtBot added the label Needs rebase on Apr 13, 2021
  59. in src/script/signingprovider.h:55 in 2fbaf0fcf4 outdated
    51@@ -50,11 +52,13 @@ struct FlatSigningProvider final : public SigningProvider
    52     std::map<CKeyID, CPubKey> pubkeys;
    53     std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> origins;
    54     std::map<CKeyID, CKey> keys;
    55+    std::map<XOnlyPubKey, TaprootSpendData> tr_spenddata;
    


    S3RK commented at 7:45 am on April 19, 2021:
    IIUC the key in the map is output key, but it’s not immediately obvious. Maybe add a comment here and for GetTaprootSpendData?

    sipa commented at 6:54 pm on June 9, 2021:
    Done (here using a comment, and in function calls by changing the parameter name to output_key).
  60. Sjors commented at 4:52 pm on April 20, 2021: member
    All prerequisites have been merged! Needs rebase.
  61. in src/script/descriptor.cpp:847 in 2fbaf0fcf4 outdated
    846@@ -847,7 +847,9 @@ class TRDescriptor final : public DescriptorImpl
    847         XOnlyPubKey xpk(keys[0]);
    848         if (!xpk.IsFullyValid()) return {};
    849         builder.Finalize(xpk);
    850-        return Vector(GetScriptForDestination(builder.GetOutput()));
    851+        WitnessV1Taproot output = builder.GetOutput();
    852+        out.tr_spenddata[output].Merge(builder.GetSpendData());
    


    S3RK commented at 7:29 am on April 21, 2021:
    Is this just an assignment? We don’t need to merge here, do we?

    sipa commented at 3:07 pm on June 9, 2021:

    Well, not right now.

    But like several things in this PR it’s sort of written to accomodate future PSBT extensions that expose some of the TaprootSpendData. And one of the possbilities is only including information about a subset of leaves. In that case, you do want a merge operation when combining.

  62. in src/script/descriptor.cpp:844 in 2fbaf0fcf4 outdated
    846@@ -847,7 +847,9 @@ class TRDescriptor final : public DescriptorImpl
    847         XOnlyPubKey xpk(keys[0]);
    848         if (!xpk.IsFullyValid()) return {};
    


    S3RK commented at 7:30 am on April 21, 2021:
    What about the case with unspendable internal key?

    sipa commented at 3:08 pm on June 9, 2021:
    There is no real difference. An unspendable internal pubkey is just a pubkey with no known private key. You still need to actually select one.

  63. in src/pubkey.h:243 in b6f6c58ef7 outdated
    239@@ -234,7 +240,14 @@ class XOnlyPubKey
    240 
    241     const unsigned char& operator[](int pos) const { return *(m_keydata.begin() + pos); }
    242     const unsigned char* data() const { return m_keydata.begin(); }
    243-    size_t size() const { return m_keydata.size(); }
    244+    static constexpr size_t size() { return uint256::size(); }
    


    MarcoFalke commented at 2:35 pm on April 22, 2021:

    in commit b6f6c58ef7e64f699102beef063b3b9f91c71ecd:

    Could use decltype(m_keydata)::size() for self-documenting code?


    sipa commented at 8:41 pm on May 21, 2021:
    Done.
  64. MarcoFalke removed the label Scripts and tools on Apr 22, 2021
  65. MarcoFalke removed the label Utils/log/libs on Apr 22, 2021
  66. MarcoFalke added the label Descriptors on Apr 22, 2021
  67. in doc/descriptors.md:73 in 3f6aad2d68 outdated
    73 - `combo(KEY)` (top level only): an alias for the collection of `pk(KEY)` and `pkh(KEY)`. If the key is compressed, it also includes `wpkh(KEY)` and `sh(wpkh(KEY))`.
    74-- `multi(k,KEY_1,KEY_2,...,KEY_n)` (anywhere): k-of-n multisig script.
    75-- `sortedmulti(k,KEY_1,KEY_2,...,KEY_n)` (anywhere): k-of-n multisig script with keys sorted lexicographically in the resulting script.
    76+- `multi(k,KEY_1,KEY_2,...,KEY_n)` (not inside `tr`): k-of-n multisig script.
    77+- `sortedmulti(k,KEY_1,KEY_2,...,KEY_n)` (not inside `tr`): k-of-n multisig script with keys sorted lexicographically in the resulting script.
    78+- `tr(XKEY)` or `tr(KEY,TREE)` (top level only): P2TR output with the specified key as internal key, and optionally a tree of script paths.
    


    fjahr commented at 8:39 pm on May 2, 2021:

    in 3f6aad2d687b5435e93bf6c99cbe1cabc9c1ff16:

    Probably should be tr(KEY) instead of tr(XKEY).


    sipa commented at 8:41 pm on May 21, 2021:
    Fixed.
  68. in src/script/interpreter.cpp:1436 in 474536660d outdated
    1430@@ -1431,8 +1431,8 @@ void PrecomputedTransactionData::Init(const T& txTo, std::vector<CTxOut>&& spent
    1431     }
    1432 
    1433     // Determine which precomputation-impacting features this transaction uses.
    1434-    bool uses_bip143_segwit = false;
    1435-    bool uses_bip341_taproot = false;
    1436+    bool uses_bip143_segwit = force;
    1437+    bool uses_bip341_taproot = force;
    1438     for (size_t inpos = 0; inpos < txTo.vin.size(); ++inpos) {
    


    fjahr commented at 10:52 pm on May 2, 2021:

    in 474536660d:

    May as well skip this for loop altogether if force is true, if I see that correctly.


    sipa commented at 8:41 pm on May 21, 2021:
    Done.
  69. sipa force-pushed on May 21, 2021
  70. sipa commented at 8:41 pm on May 21, 2021: member
    Rebased, and addressed comments.
  71. DrahtBot removed the label Needs rebase on May 21, 2021
  72. MarcoFalke commented at 5:32 am on May 22, 2021: member
    0 node0 stderr bitcoin-node: script/interpreter.cpp:1714: virtual bool GenericTransactionSignatureChecker<CMutableTransaction>::CheckSchnorrSignature(Span<const unsigned char>, Span<const unsigned char>, SigVersion, const ScriptExecutionData &, ScriptError *) const [T = CMutableTransaction]: Assertion `this->txdata' failed. 
    
  73. in src/uint256.h:78 in a2edabe432 outdated
    74@@ -75,7 +75,7 @@ class base_blob
    75         return &m_data[WIDTH];
    76     }
    77 
    78-    unsigned int size() const
    79+    static constexpr unsigned int size()
    


    Sjors commented at 2:04 pm on May 22, 2021:
    a2edabe43261ef2859742015a53c34ee8a5d1ee3 This along with the corresponding changes in pubkey.h might be worth a separate commit.

    sipa commented at 1:42 am on May 24, 2021:
    Done, added commit “Make XOnlyPubKey act like byte container”.
  74. in src/script/standard.h:156 in a2edabe432 outdated
    154+ *  * WitnessV1Taproot: TxoutType::WITNESS_V1_TAPROOT destination (P2TR)
    155+ *  * WitnessUnknown: TxoutType::WITNESS_UNKNOWN destination (P2W???)
    156  *  A CTxDestination is the internal data type encoded in a bitcoin address
    157  */
    158-using CTxDestination = std::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown>;
    159+using CTxDestination = std::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown, WitnessV1Taproot>;
    


    Sjors commented at 2:07 pm on May 22, 2021:
    a2edabe43261ef2859742015a53c34ee8a5d1ee3 shouldn’t we keep WitnessUnknown at the end? That would also keep it consistent with the ordering in e.g. descriptor.cpp.

    sipa commented at 1:43 am on May 24, 2021:
    I didn’t do that because the descriptor code’s GetOutputType depended on the exact order of elements in this variant. I’ve addressed that in a new commit “Avoid dependence on CTxDestination index order”, and then implemented your suggestion of placing WitnessUnknown last.
  75. in src/script/standard.cpp:247 in a2edabe432 outdated
    241@@ -242,8 +242,13 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
    242         addressRet = hash;
    243         return true;
    244     }
    245-    case TxoutType::WITNESS_UNKNOWN:
    246     case TxoutType::WITNESS_V1_TAPROOT: {
    247+        WitnessV1Taproot tap;
    248+        std::copy(vSolutions[1].begin(), vSolutions[1].end(), tap.begin());
    


    Sjors commented at 2:15 pm on May 22, 2021:

    This raises the question: why are we putting the witness version in vSolutions? That seems to have been copied from WITNESS_UNKNOWN handling, whereas we don’t do it for SegWit v0.

    https://github.com/bitcoin/bitcoin/blob/be4171679b8eab8205e04ff86140329bd67878a0/src/script/standard.cpp#L161-L174

    I don’t see an explanation in e9a021d7e6a454d610a45cb9b3995f0d96a5fbb6 (I might have missed it in #19953’s wall of text).


    sipa commented at 1:45 am on May 24, 2021:

    The explanation is just that this was easier, because while TxoutType did have a separation between unknown and taproot, CTxDestination did not. Making them both produce {version, program} as vSolutions was easier because it allowed handling them identically in ExtractDestination.

    I’ve addressed this by first changing the Solver output for taproot outputs to be just {program}, like P2WPKH/P2WSH, in a new commit “c1445ee0d56e56d824bc79a0c9f1db26105ade1f”, and then used that in the later commits.

  76. in src/test/script_standard_tests.cpp:446 in f86a68c0b1 outdated
    442+    BOOST_CHECK(builder.IsValid() && !builder.IsComplete());
    443+    builder.Add(1, script_1, 0xc0);
    444+    BOOST_CHECK(builder.IsValid() && builder.IsComplete());
    445+    builder.Finalize(key_inner);
    446+    BOOST_CHECK(builder.IsValid() && builder.IsComplete());
    447+    BOOST_CHECK_EQUAL(EncodeDestination(builder.GetOutput()), "bc1pj6gaw944fy0xpmzzu45ugqde4rz7mqj5kj0tg8kmr5f0pjq8vnaqgynnge");
    


    Sjors commented at 5:20 pm on May 22, 2021:
    f86a68c0b1a6ed9e0c7e6f0ee81989a6129abe30: as an additional sanity check, you could generate the same same address in feature_taproot.py

    sipa commented at 8:54 pm on June 3, 2021:
    wallet_taproot.py already tests address derivation against the python implementation (which is also used by feature_taproot.py, so I think that’s unnecessary.
  77. in src/script/standard.h:325 in f86a68c0b1 outdated
    282+    /** Return whether there were either no leaves, or the leaves form a Huffman tree. */
    283+    bool IsComplete() const { return m_valid && (m_branch.size() == 0 || (m_branch.size() == 1 && m_branch[0].has_value())); }
    284+    /** Compute scriptPubKey (after Finalize()). */
    285+    WitnessV1Taproot GetOutput();
    286+    /** Check if a list of depths is legal (will lead to IsComplete()). */
    287+    static bool ValidDepths(const std::vector<int>& depths);
    


    Sjors commented at 5:26 pm on May 22, 2021:
    f86a68c0b1a6ed9e0c7e6f0ee81989a6129abe30: note to other reviewers: this is used in d9d928198be28923d37434a924788b284a4e3760 to sanity check the descriptor parser (and made more sense to me after reviewing the {{A,B},C} parsing code).
  78. in src/script/standard.cpp:443 in f86a68c0b1 outdated
    438+
    439+TaprootBuilder& TaprootBuilder::Add(int depth, const CScript& script, int leaf_version)
    440+{
    441+    assert((leaf_version & ~TAPROOT_LEAF_MASK) == 0);
    442+    if (!IsValid()) return *this;
    443+    /* Construct NodeInfo object with leaf hash and - if desired - leaf information. */
    


    Sjors commented at 6:30 pm on May 22, 2021:
    f86a68c0b1a6ed9e0c7e6f0ee81989a6129abe30: what do you mean by “if desired - leaf information”?

    sipa commented at 1:46 am on May 24, 2021:
    That comment didn’t make sense in the commit where it was in. I’ve moved it, and made it a bit clearer.
  79. Sjors commented at 6:52 pm on May 22, 2021: member

    Reviewed down to f86a68c, will continue later…

    The refactoring of consensus code in 5ecaca05a299d3da9cdfdf8e9c6860500a73587d (CheckTapTweak) deserves its own commit (which also makes the diff slightly easier to read).

  80. sipa force-pushed on May 24, 2021
  81. sipa commented at 2:03 am on May 24, 2021: member

    Fixed the bug referred to here by @MarcoFalke, and addressed most of @Sjors’s comments.

    Note: if this is too much, it’d be totally reasonable to split this PR in two, splitting after “Add tr() descriptor (derivation only, no signing)”. Everything up to there is preparation & descriptor derivation. Everything after is signing logic.

  82. Sjors commented at 10:24 am on May 24, 2021: member

    Being able to make watch-only taproot wallet seems like a nice natural point to split this PR.

    tACK up to c08bb8badb8ab030590fe1bea160078785ecdafe

    In order to test descriptors with script paths, without having signing code, it would be useful to expand getaddressinfo and have it show the TREE of scripts.

  83. sipa force-pushed on May 24, 2021
  84. sipa force-pushed on May 24, 2021
  85. sipa commented at 10:09 pm on May 24, 2021: member
    I split off the derivation part in #22051.
  86. DrahtBot added the label Needs rebase on May 30, 2021
  87. laanwj referenced this in commit c7dd9ff71b on Jun 3, 2021
  88. sipa force-pushed on Jun 3, 2021
  89. sipa commented at 8:36 pm on June 3, 2021: member
    Rebased after #22051 merge.
  90. DrahtBot removed the label Needs rebase on Jun 3, 2021
  91. meshcollider added this to the milestone 22.0 on Jun 3, 2021
  92. sipa removed the label Consensus on Jun 4, 2021
  93. in src/script/standard.cpp:403 in e4c8ae471d outdated
    395@@ -386,6 +396,17 @@ bool IsValidDestination(const CTxDestination& dest) {
    396     return ret;
    397 }
    398 
    399+void TaprootSpendData::Merge(TaprootSpendData other)
    400+{
    401+    if (internal_key.IsNull() && !other.internal_key.IsNull()) {
    


    achow101 commented at 6:18 pm on June 7, 2021:

    In e4c8ae471d38e878a69fa8099d75d447319f4037 “Add TaprootSpendData data structure, equivalent to script map for P2[W]SH”

    Since we are expecting that the TaprootSpendData being merged is for the same scriptPubKey, I think this should assert that the internal_key and merkle_root match if both have them.


    sipa commented at 10:01 pm on June 7, 2021:
    Asserting sounds dangerous, as I’d expect TaprootSpendData to at some point be untrusted data (e.g. gathered from PSBT). I agree it’s a bit ugly to just prefer one over the other in case of a contradiction, and I also don’t have a better solution. Would you be ok with just a TODO here?
  94. in src/script/standard.cpp:407 in e4c8ae471d outdated
    395@@ -386,6 +396,17 @@ bool IsValidDestination(const CTxDestination& dest) {
    396     return ret;
    397 }
    398 
    399+void TaprootSpendData::Merge(TaprootSpendData other)
    400+{
    401+    if (internal_key.IsNull() && !other.internal_key.IsNull()) {
    402+        internal_key = other.internal_key;
    403+        merkle_root = other.merkle_root;
    


    achow101 commented at 6:19 pm on June 7, 2021:

    In e4c8ae471d38e878a69fa8099d75d447319f4037 “Add TaprootSpendData data structure, equivalent to script map for P2[W]SH”

    Should this merkle_root be guarded with its own if instead being combined with the internal_key? It looks like in the current usage it doesn’t matter, but perhaps in the future it might?


    sipa commented at 11:58 pm on June 7, 2021:
    Done.
  95. in src/script/standard.h:287 in e4c8ae471d outdated
    283@@ -261,6 +284,7 @@ class TaprootBuilder
    284 
    285     XOnlyPubKey m_internal_key;  //!< The internal key, set when finalizing.
    286     XOnlyPubKey m_output_key; //!< The output key, computed when finalizing. */
    287+    bool m_parity;            //!< The tweak parity, computed when finalizing. */
    


    achow101 commented at 6:22 pm on June 7, 2021:

    In e4c8ae471d38e878a69fa8099d75d447319f4037 “Add TaprootSpendData data structure, equivalent to script map for P2[W]SH”

    nit:, extra */

    0    bool m_parity;            //!< The tweak parity, computed when finalizing. */
    1    bool m_parity;            //!< The tweak parity, computed when finalizing.
    

    sipa commented at 11:58 pm on June 7, 2021:
    Done. Also realigned.
  96. in src/test/key_tests.cpp:322 in ebf44e081b outdated
    321+        unsigned char sig64[64];
    322+
    323+        // Run the untweaked test vectors above, comparing with exact expected signature.
    324+        CKey key;
    325+        key.Set(sec.begin(), sec.end(), true);
    326+        XOnlyPubKey pubkey(key.GetPubKey());
    


    achow101 commented at 6:39 pm on June 7, 2021:

    In ebf44e081ba0ef7b8810497dee47717bb661cbdb “Add CKey::SignSchnorr function for BIP 340/341 signing”

    Why not compare the pubkey against the pubkeys specified in the BIP 340 test vectors?


    sipa commented at 11:58 pm on June 7, 2021:
    Done.
  97. in src/psbt.cpp:252 in 8d073d0afe outdated
    248+            }
    249+        }
    250+        if (!input.witness_utxo.IsNull()) {
    251+            utxos[idx] = input.witness_utxo;
    252+            continue;
    253+        }
    


    achow101 commented at 6:55 pm on June 7, 2021:

    In 8d073d0afe285bd04542907428298d377f49c8f9 “Construct and use PrecomputedTransactionData in PSBT signing”

    GetInputUTXO does all of this already, so I think you can just use that instead of reimplementing the utxo fetching. If you need to do the hash checking, it would probably be worthwhile to implement that in GetInputUTXO too.


    sipa commented at 11:59 pm on June 7, 2021:
    Changed to use GetInputUTXO. I’ve added a commit to add the prevout hash check to GetInputUTXO itself.
  98. in src/psbt.cpp:236 in 8d073d0afe outdated
    232+{
    233+    const CMutableTransaction& tx = *psbt.tx;
    234+    bool have_all_spent_outputs = true;
    235+    std::vector<CTxOut> utxos(tx.vin.size());
    236+    for (size_t idx = 0; idx < tx.vin.size(); ++idx) {
    237+        if (psbt.inputs.size() <= idx) {
    


    achow101 commented at 6:56 pm on June 7, 2021:

    In 8d073d0afe285bd04542907428298d377f49c8f9 “Construct and use PrecomputedTransactionData in PSBT signing”

    This case shouldn’t happen because every input in the tx must have a corresponding PSBTInput, even if it is empty. So I think this should be an assert as it would be a bug.


    sipa commented at 0:03 am on June 8, 2021:
    Gone by rewriting.
  99. in src/script/sign.cpp:164 in ea07a01b0c outdated
    156+{
    157+    auto lookup_key = std::make_pair(pubkey, leaf_hash);
    158+    auto it = sigdata.taproot_script_sigs.find(lookup_key);
    159+    if (it != sigdata.taproot_script_sigs.end()) {
    160+        sig_out = it->second;
    161+    }
    


    achow101 commented at 7:17 pm on June 7, 2021:

    In ea07a01b0cf7256304b17508debf74d94cccdac7 “Basic Taproot signing logic in script/sign.cpp”

    Can you explain what this is doing and why?


    sipa commented at 11:55 pm on June 7, 2021:

    It’s the equivalent of SignatureData::signatures, but for Taproot/Schnorr signatures. They’re not indexed by CKeyId, but by full (untweaked) pubkey and leaf they occur in.

    This is redundant for now, as there is no multiparty signing whatsoever, and no way of communicating partial signatures (needs PSBT extension), but this is pretty much what it would look like. I could drop it for this PR.

  100. achow101 commented at 7:38 pm on June 7, 2021: member
    Since #22166 uses “Add TaprootSpendData data structure, equivalent to script map for P2[W]SH” from here but with scripts as a multimap, in the interest of reducing future merge conflicts, I think this PR should use the same commit and use the multimap too. I expect that the change to using a multimap might cause some problems with the later commits in this PR.
  101. sipa force-pushed on Jun 7, 2021
  102. sipa commented at 0:03 am on June 8, 2021: member
    Addressed most of your comments, and also updated the TaprootSpendData commit to use multiset semantics. Instead of using multiset<key, control_block>, I’ve changed it to map<key, set<control_block>>, which also has multiset semantics that should be compatible with #22166’s inference code, but also with the signing logic in this PR (which should prefer the smallest control block for a given script if multiple are available).
  103. sipa force-pushed on Jun 8, 2021
  104. achow101 commented at 4:23 pm on June 8, 2021: member
    ACK 4ad2a1d4adfcff715fed3ec37433ffd507c98b84
  105. S3RK commented at 8:00 am on June 9, 2021: member
    I have a few simple questions, please help me understand.
  106. sipa force-pushed on Jun 9, 2021
  107. sipa force-pushed on Jun 9, 2021
  108. in src/key.h:139 in 7e68f9eae7 outdated
    134+     * aux.
    135+     *
    136+     * When merkle_root is not nullptr, this results in a signature with a modified key as
    137+     * specified in BIP341:
    138+     * - If merkle_root->IsNull(): key + H_TapTweak(pubkey)*G
    139+     * - Otherwise:                key + H_TapTweak(pubkey || *merkle_root)
    


    S3RK commented at 7:09 am on June 10, 2021:
    0     * - Otherwise:                key + H_TapTweak(pubkey || *merkle_root)*G
    

    sipa commented at 11:42 pm on June 17, 2021:
    It’s referring to the operation applied to the private key, not the public one.

    sipa commented at 8:36 pm on June 18, 2021:
    Oh, so the the line before is wrong. I see. Added in #22275.
  109. achow101 commented at 6:50 pm on June 10, 2021: member
    re-ACK c69ad7e8097fb6a6cfdd28f135e666ba40405b61
  110. DrahtBot added the label Needs rebase on Jun 12, 2021
  111. Add TaprootSpendData data structure, equivalent to script map for P2[W]SH
    This data structures stores all information necessary for spending a taproot
    output (the internal key, the Merkle root, and the control blocks for every
    script leaf).
    
    It is added to signing providers, and populated by the tr() descriptor.
    dbb0ce9fbf
  112. Use HandleMissingData also in CheckSchnorrSignature e77a2839b5
  113. Add CKey::SignSchnorr function for BIP 340/341 signing a91d532338
  114. Add precomputed txdata support to MutableTransactionSignatureCreator
    This provides a means to pass in a PrecomputedTransactionData object to
    the MutableTransactionSignatureCreator, allowing the prevout data to be
    passed into the signature hashers. It is also more efficient.
    e841fb503d
  115. Permit full precomputation in PrecomputedTransactionData
    At verification time, the to be precomputed data can be inferred from
    the transaction itself. For signing, the necessary witnesses don't
    exist yet, so just permit precomputing everything in that case.
    ce9353164b
  116. Don't nuke witness data when signing fails 5d2e22437b
  117. Construct and use PrecomputedTransactionData in SignTransaction 5cb6502ac5
  118. Construct and use PrecomputedTransactionData in PSBT signing fd3f6890f3
  119. Make GetInputUTXO safer: verify non-witness UTXO match 49487bc3b6
  120. Basic Taproot signing logic in script/sign.cpp a2380127e9
  121. tests: check spending of P2TR c0f0c8eccb
  122. Add support for SIGHASH_DEFAULT in RPCs, and make it default
    For non-Taproot signatures, this is interpreted as SIGHASH_ALL.
    458a345b05
  123. sipa force-pushed on Jun 12, 2021
  124. sipa commented at 8:47 pm on June 12, 2021: member
    Rebased.
  125. DrahtBot removed the label Needs rebase on Jun 12, 2021
  126. achow101 commented at 7:24 pm on June 14, 2021: member
    re-ACK 458a345b0590fd2fa04c7d8d70beb8d57e34bbc8
  127. Sjors commented at 9:30 am on June 16, 2021: member
    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.
  128. michaelfolkson commented at 2:38 pm on June 16, 2021: contributor

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

  129. Sjors commented at 8:08 pm on June 16, 2021: member
    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.
  130. in src/script/sign.cpp:207 in a2380127e9 outdated
    202+        if (sigdata.taproot_key_path_sig.size() == 0) {
    203+            if (creator.CreateSchnorrSig(provider, sig, spenddata.internal_key, nullptr, &spenddata.merkle_root, SigVersion::TAPROOT)) {
    204+                sigdata.taproot_key_path_sig = sig;
    205+            }
    206+        }
    207+        if (sigdata.taproot_key_path_sig.size()) {
    


    fjahr commented at 9:13 pm on June 16, 2021:
    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

    sipa commented at 11:28 pm on June 17, 2021:
    I tried this for a bit, but I don’t find the result cleaner.
  131. in src/script/signingprovider.h:55 in dbb0ce9fbf outdated
    51@@ -50,11 +52,13 @@ struct FlatSigningProvider final : public SigningProvider
    52     std::map<CKeyID, CPubKey> pubkeys;
    53     std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> origins;
    54     std::map<CKeyID, CKey> keys;
    55+    std::map<XOnlyPubKey, TaprootSpendData> tr_spenddata; /** Map from output key to spend data. */
    


    fjahr commented at 9:41 pm on June 16, 2021:
    nit: m_tr_spenddata?

    sipa commented at 11:30 pm on June 17, 2021:
    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).
  132. in src/script/standard.h:243 in dbb0ce9fbf outdated
    238     struct NodeInfo
    239     {
    240         /** Merkle hash of this node. */
    241         uint256 hash;
    242+        /** Tracked leaves underneath this node (either from the node itself, or its children).
    243+         *  The merkle_branch field for each is the partners to get to *this* node. */
    


    fjahr commented at 9:54 pm on June 16, 2021:
    nit: reads a bit strange but maybe I am not reading it right. I would write it as “The merkle_branch field of each are the partners to get to this node.”

    sipa commented at 0:48 am on June 18, 2021:
    Done in #22275.
  133. in src/script/sign.h:47 in e841fb503d outdated
    39@@ -40,9 +40,11 @@ class MutableTransactionSignatureCreator : public BaseSignatureCreator {
    40     int nHashType;
    41     CAmount amount;
    42     const MutableTransactionSignatureChecker checker;
    43+    const PrecomputedTransactionData* m_txdata;
    44 
    45 public:
    46     MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn = SIGHASH_ALL);
    47+    MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData* txdata, int nHashTypeIn = SIGHASH_ALL);
    


    fjahr commented at 10:41 pm on June 16, 2021:
    Why isn’t SIGHASH_DEFAULT the default here now as well?

    sipa commented at 0:48 am on June 18, 2021:
    Fixed in #22275; just removed the default argument value.
  134. in src/script/sign.cpp:654 in 458a345b05
    649+    }
    650+    if (have_all_spent_outputs) {
    651+        txdata.Init(txConst, std::move(spent_outputs), true);
    652+    } else {
    653+        txdata.Init(txConst, {}, true);
    654+    }
    


    fjahr commented at 11:46 pm on June 16, 2021:

    nit: I think this loop could be simplified a little

     0    for (CTxIn& txin : mtx.vin) {
     1        auto coin = coins.find(txin.prevout);
     2        if (coin == coins.end() || coin->second.IsSpent()) {
     3            txdata.Init(txConst, {}, true);
     4            break;
     5        }
     6        spent_outputs.emplace_back(CTxOut(coin->second.out.nValue, coin->second.out.scriptPubKey));
     7    }
     8    if (mtx.vin.size() == spent_outputs.size()) {
     9        txdata.Init(txConst, std::move(spent_outputs), true);
    10    }
    

    sipa commented at 0:48 am on June 18, 2021:
    Done in #22275.
  135. in src/script/sign.cpp:60 in a2380127e9 outdated
    55+{
    56+    assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT);
    57+
    58+    CKey key;
    59+    {
    60+        // For now, use the old full pubkey-based key derivation logic. As it indexed by
    


    fjahr commented at 0:04 am on June 17, 2021:
    As it is indexed by

    sipa commented at 0:48 am on June 18, 2021:
    Done in #22275.
  136. fjahr commented at 0:49 am on June 17, 2021: member

    Code review ACK 458a345b0590fd2fa04c7d8d70beb8d57e34bbc8

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

  137. in src/script/interpreter.cpp:1436 in ce9353164b outdated
    1430@@ -1431,9 +1431,9 @@ void PrecomputedTransactionData::Init(const T& txTo, std::vector<CTxOut>&& spent
    1431     }
    1432 
    1433     // Determine which precomputation-impacting features this transaction uses.
    1434-    bool uses_bip143_segwit = false;
    1435-    bool uses_bip341_taproot = false;
    1436-    for (size_t inpos = 0; inpos < txTo.vin.size(); ++inpos) {
    1437+    bool uses_bip143_segwit = force;
    1438+    bool uses_bip341_taproot = force;
    1439+    for (size_t inpos = 0; inpos < txTo.vin.size() && !(uses_bip143_segwit && uses_bip341_taproot); ++inpos) {
    


    Sjors commented at 3:23 pm on June 17, 2021:

    ce9353164bdb6215a62b2b6dcb2121d331796f60: suggest moving if (uses_bip341_taproot && uses_bip143_segwit) break; // No need to scan further if we already need all. up to the start of the loop instead.

    In the commit message maybe replace “just permit precomputing” with “just precompute”.


    sipa commented at 0:48 am on June 18, 2021:
    Too late.
  138. in src/script/interpreter.cpp:1481 in ce9353164b outdated
    1477@@ -1478,8 +1478,8 @@ PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo)
    1478 }
    1479 
    1480 // explicit instantiation
    1481-template void PrecomputedTransactionData::Init(const CTransaction& txTo, std::vector<CTxOut>&& spent_outputs);
    1482-template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo, std::vector<CTxOut>&& spent_outputs);
    1483+template void PrecomputedTransactionData::Init(const CTransaction& txTo, std::vector<CTxOut>&& spent_outputs, bool force);
    


    Sjors commented at 3:29 pm on June 17, 2021:

    Suggested comment:

    0// * [@param](/bitcoin-bitcoin/contributor/param/)[in]   force   Precompute data for both bip341 taproot and bip143 segwit signatures regardless of the actual inputs.
    

    sipa commented at 0:48 am on June 18, 2021:
    Done in #22275.
  139. Sjors approved
  140. Sjors commented at 6:27 pm on June 17, 2021: member
    ACK 458a345b0590fd2fa04c7d8d70beb8d57e34bbc8
  141. in src/key.h:136 in 458a345b05
    127@@ -128,6 +128,18 @@ class CKey
    128      */
    129     bool SignCompact(const uint256& hash, std::vector<unsigned char>& vchSig) const;
    130 
    131+    /**
    132+     * Create a BIP-340 Schnorr signature, for the xonly-pubkey corresponding to *this,
    133+     * optionally tweaked by *merkle_root. Additional nonce entropy can be provided through
    134+     * aux.
    135+     *
    136+     * When merkle_root is not nullptr, this results in a signature with a modified key as
    


    S3RK commented at 8:20 pm on June 17, 2021:

    What’s the use case for non-tweaked signatures?

    nit: The difference between merkle_root == nullptr and merkel_root->IsNull() is subtle enough that one might be mistaken, especially when passing the aux parameter and not reading the comments closely. Could this be a potential problem in the future?


    sipa commented at 11:44 pm on June 17, 2021:

    Signatures with OP_CHECKSIG and friends aren’t tweaked; only key-path signatures are.

    So we do need both a way to ask for untweaked, and for tweaked with nothing. I agree it’s confusing, though we need a way to ask for both. Getting it wrong is exceedingly likely to cause validation failures, though.


    Sjors commented at 3:51 pm on June 18, 2021:

    Yikes, I missed that subtlety too. In #22275 you could expand the comment bit, e.g.:

    0* When merkle_root is nullptr, as distinct from merkle_root->IsNull(), the signature is not tweaked.
    1* This is the case for e.g. OP_CHECKSIG in a tapscript.
    2* For key-path signatures on the other hand the signature is always tweaked (merkle_root is not nullptr) as follows:
    

    sipa commented at 8:28 pm on June 18, 2021:
    Added something like that to #22275.
  142. S3RK commented at 8:44 pm on June 17, 2021: member
    two comments
  143. meshcollider merged this on Jun 17, 2021
  144. meshcollider closed this on Jun 17, 2021

  145. sidhujag referenced this in commit 59b8f7b14a on Jun 18, 2021
  146. in src/script/standard.cpp:410 in 458a345b05
    405+    }
    406+    if (merkle_root.IsNull() && !other.merkle_root.IsNull()) {
    407+        merkle_root = other.merkle_root;
    408+    }
    409+    for (auto& [key, control_blocks] : other.scripts) {
    410+        scripts[key].merge(std::move(control_blocks));
    


    hebasto commented at 10:24 am on June 23, 2021:

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

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

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

    Reported by @S3RK.


    MarcoFalke commented at 10:39 am on June 23, 2021:
    I presume this would also make it harder to compile on https://packages.debian.org/stretch/gcc

    hebasto commented at 10:48 am on June 23, 2021:
    Since #20413 we support gcc 7+ only, right?

    MarcoFalke commented at 11:47 am on June 23, 2021:
    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.

    MarcoFalke commented at 12:23 pm on June 23, 2021:

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

     0# make V=1
     1Making all in src
     2make[1]: Entering directory '/bitcoin/src'
     3make[2]: Entering directory '/bitcoin/src'
     4make[3]: Entering directory '/bitcoin'
     5make[3]: Leaving directory '/bitcoin'
     6/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
     7script/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> > > >'
     8        scripts[key].merge(std::move(control_blocks));
     9        ~~~~~~~~~~~~ ^
    101 error generated.
    11Makefile:7756: recipe for target 'script/libbitcoin_common_a-standard.o' failed
    12make[2]: *** [script/libbitcoin_common_a-standard.o] Error 1
    13make[2]: Leaving directory '/bitcoin/src'
    14Makefile:15489: recipe for target 'all-recursive' failed
    15make[1]: *** [all-recursive] Error 1
    16make[1]: Leaving directory '/bitcoin/src'
    17Makefile:820: recipe for target 'all-recursive' failed
    18make: *** [all-recursive] Error 1
    

    hebasto commented at 4:24 am on June 25, 2021:
    See #22339.
  147. hebasto referenced this in commit 440b3c0abe on Jun 25, 2021
  148. hebasto referenced this in commit be414e99b0 on Jun 25, 2021
  149. hebasto referenced this in commit 4ffb9f4a7d on Jun 25, 2021
  150. MarcoFalke referenced this in commit 9c3751a0c9 on Jun 26, 2021
  151. hebasto referenced this in commit 22c398f129 on Jun 26, 2021
  152. sidhujag referenced this in commit 654f89678e on Jun 27, 2021
  153. fanquake referenced this in commit e826b22da2 on Aug 23, 2021
  154. sidhujag referenced this in commit a77514a7e9 on Aug 23, 2021
  155. gwillen referenced this in commit 0afcb6d978 on Jun 1, 2022
  156. DrahtBot locked this on Aug 16, 2022

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-07-03 13:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me