Support output descriptors in scantxoutset #13697

pull sipa wants to merge 9 commits into bitcoin:master from sipa:201806_minedesc changing 10 files +994 −131
  1. sipa commented at 11:18 pm on July 17, 2018: member

    As promised, here is an implementation of my output descriptor concept (https://gist.github.com/sipa/e3d23d498c430bb601c5bca83523fa82) and integration within the scantxoutset RPC that was just added through #12196.

    It changes the RPC to use descriptors for everything; I hope the interface is simple enough to encompass all use cases. It includes support for P2PK, P2PKH, P2WPKH, P2SH, P2WSH, multisig, xpubs, xprvs, and chains of keys - combined in every possible way.

  2. in src/script/descriptor.cpp:5 in a6eba6912b outdated
    0@@ -0,0 +1,615 @@
    1+#include <script/descriptor.h>
    


    Empact commented at 3:26 am on July 18, 2018:
    Missing copyright block

    sipa commented at 3:30 am on July 18, 2018:
    Fixed.
  3. sipa force-pushed on Jul 18, 2018
  4. in src/script/sign.h:40 in 422d08c1f9 outdated
    35+    std::map<CKeyID, CKey> keys;
    36+
    37+    bool GetCScript(const CScriptID& scriptid, CScript& script) const override;
    38+    bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override;
    39+    bool GetKey(const CKeyID& keyid, CKey& key) const override;
    40+
    


    promag commented at 1:17 pm on July 18, 2018:
    nit, remove empty line.

    sipa commented at 7:26 pm on July 18, 2018:
    Done.
  5. in src/script/sign.h:57 in 1b7c0c7fbd outdated
    38+    bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override;
    39+    bool GetKey(const CKeyID& keyid, CKey& key) const override;
    40+
    41+};
    42+
    43+FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b);
    


    promag commented at 1:22 pm on July 18, 2018:
    At the moment this is only used in tests, move there?

    sipa commented at 7:26 pm on July 18, 2018:
    I’d rather not. I think more things will be able to use this.
  6. laanwj added the label RPC/REST/ZMQ on Jul 18, 2018
  7. in src/script/descriptor.cpp:82 in 1b7c0c7fbd outdated
    77+        if (!m_provider->ToPrivateString(arg, sub)) return false;
    78+        ret = OriginString() + ":" + std::move(sub);
    79+        return true;
    80+    }
    81+};
    82+#endif
    


    promag commented at 1:33 pm on July 18, 2018:
    nit, add // KEY_ORIGIN_SUPPORT.

    sipa commented at 7:26 pm on July 18, 2018:
    Done.
  8. in src/script/descriptor.cpp:29 in 1b7c0c7fbd outdated
    24+////////////////////////////////////////////////////////////////////////////
    25+
    26+typedef std::vector<uint32_t> KeyPath;
    27+
    28+/** Interface for public key objects in descriptors. */
    29+struct PubkeyProvider {
    


    promag commented at 1:34 pm on July 18, 2018:
    nit, { in new line.

    sipa commented at 7:26 pm on July 18, 2018:
    Done.
  9. in src/script/descriptor.cpp:30 in 1b7c0c7fbd outdated
    25+
    26+typedef std::vector<uint32_t> KeyPath;
    27+
    28+/** Interface for public key objects in descriptors. */
    29+struct PubkeyProvider {
    30+    virtual ~PubkeyProvider(){};
    


    promag commented at 1:34 pm on July 18, 2018:
    nit, = default?

    sipa commented at 7:26 pm on July 18, 2018:
    Done, here and elsewhere.
  10. promag commented at 1:38 pm on July 18, 2018: member
    Few nits, it this for 0.17? Is scantxoutset considered API experimental?
  11. in src/script/descriptor.cpp:422 in 1b7c0c7fbd outdated
    452+std::vector<Span<const char>> Split(const Span<const char>& sp, char sep)
    453+{
    454+    std::vector<Span<const char>> ret;
    455+    auto it = sp.begin();
    456+    auto start = it;
    457+    while (it != sp.end()) {
    


    promag commented at 3:34 pm on July 18, 2018:

    Add

    0if (it == sp.end()) return ret;
    

    for when string is empty.


    sipa commented at 7:26 pm on July 18, 2018:
    Why?

    promag commented at 8:25 pm on July 18, 2018:
    The statement ret.emplace_back(start, it); below should not be executed if sp is empty.

    promag commented at 8:25 pm on July 18, 2018:
    I mean, should Split("", sep) equal []?

    sipa commented at 8:30 pm on July 18, 2018:
    No, it should be [""]. Callers can rely on there always being at least one argument in the result.

    promag commented at 8:35 pm on July 18, 2018:
    In that case 👍
  12. in src/script/descriptor.cpp:617 in 1b7c0c7fbd outdated
    612+
    613+std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out)
    614+{
    615+    Span<const char> sp(descriptor.data(), descriptor.size());
    616+    auto ret = ParseScript(sp, ParseScriptContext::TOP, out);
    617+    if (sp.size() == 0 && ret) return std::move(ret);
    


    promag commented at 4:01 pm on July 18, 2018:

    Compiler warning:

    0script/descriptor.cpp:617:39: warning: moving a local object in a return statement prevents copy elision [-Wpessimizing-move]
    1    if (sp.size() == 0 && ret) return std::move(ret);
    2                                      ^
    3script/descriptor.cpp:617:39: note: remove std::move call here
    4    if (sp.size() == 0 && ret) return std::move(ret);
    5                                      ^~~~~~~~~~  
    

    sipa commented at 7:27 pm on July 18, 2018:
    Fixed.
  13. in src/rpc/blockchain.cpp:2012 in 1b7c0c7fbd outdated
    2055-            "            \"pubkey\" : \"<pubkey\">,         (string, required) HEX encoded public key\n"
    2056-            "            \"script_types\" : [ ... ],      (array, optional) Array of script-types to derive from the pubkey (possible values: \"P2PK\", \"P2PKH\", \"P2SH-P2WPKH\", \"P2WPKH\")\n"
    2057-            "          }\n"
    2058+            "2. \"scanobjects\"                  (array, required) Array of scan objects\n"
    2059+            "    [                             Every scan object is either a string descriptor or an object:\n"
    2060+            "        \"descriptor\",             (string, optional) An output descriptor\n"
    


    promag commented at 4:20 pm on July 18, 2018:
    Why give both options? [{ "desc": "...", ...}, ...] is enough?

    sipa commented at 7:27 pm on July 18, 2018:
    It’s pretty verbose to use the long form, and probably not all that often needed.

    promag commented at 8:41 pm on July 18, 2018:

    It’s pretty verbose

    On the client side I think it doesn’t matter (unless it’s a human making the call).


    sipa commented at 10:33 pm on July 18, 2018:
    Yes, the reason to support the short notation is to simplify things for humans.
  14. in src/rpc/blockchain.cpp:2070 in 1b7c0c7fbd outdated
    2115+            int range = 1000;
    2116+            if (scanobject.isStr()) {
    2117+                desc_str = scanobject.get_str();
    2118+            } else if (scanobject.isObject()) {
    2119+                UniValue desc_uni = find_value(scanobject, "desc");
    2120+                if (desc_uni.isNull()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor needs to be provided in scan object");
    


    promag commented at 4:20 pm on July 18, 2018:
    Could have a test.

    sipa commented at 7:27 pm on July 18, 2018:
    Yes, will add when there’s sufficient agreement about RPC.
  15. in src/rpc/blockchain.cpp:2074 in 1b7c0c7fbd outdated
    2120+                if (desc_uni.isNull()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor needs to be provided in scan object");
    2121+                desc_str = desc_uni.get_str();
    2122+                UniValue range_uni = find_value(scanobject, "range");
    2123+                if (!range_uni.isNull()) {
    2124+                    range = range_uni.get_int();
    2125+                    if (range < 1 || range > 1000000) throw JSONRPCError(RPC_INVALID_PARAMETER, "range out of range");
    


    promag commented at 4:21 pm on July 18, 2018:
    Could have a test.
  16. in src/script/descriptor.cpp:495 in 1b7c0c7fbd outdated
    548+{
    549+    auto expr = Expr(sp);
    550+    if (Func("pk", expr)) {
    551+        auto pubkey = ParsePubkey(expr, ctx != ParseScriptContext::P2WSH, out);
    552+        if (pubkey) return MakeUnique<SingleKeyDescriptor>(std::move(pubkey), P2PKGetScript, "pk");
    553+    }
    


    promag commented at 4:31 pm on July 18, 2018:
    Why not return nullptr in these cases?

    sipa commented at 7:28 pm on July 18, 2018:
    Done, here and elsewhere.
  17. in src/script/descriptor.h:100 in 1b7c0c7fbd outdated
     95+};
     96+
     97+/** Parse a descriptor string. Included private keys are put in out. Returns nullptr if parsing fails. */
     98+std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out);
     99+
    100+#endif
    


    promag commented at 4:32 pm on July 18, 2018:
    0#endif // BITCOIN_SCRIPT_DESCRIPTOR_H
    

    sipa commented at 7:28 pm on July 18, 2018:
    Done.
  18. in src/script/descriptor.cpp:189 in 1143a75c2f outdated
    184+    bool ToPrivateString(const SigningProvider& arg, std::string& out) const override
    185+    {
    186+        CExtKey key;
    187+        if (!GetExtKey(arg, key)) return false;
    188+        out = EncodeExtKey(key);
    189+        for (auto entry : m_path) {
    


    achow101 commented at 7:11 pm on July 18, 2018:
    Can the path writing be separated out into another function to avoid code duplication and so that we can use it elsewhere?

    sipa commented at 10:41 pm on July 18, 2018:
    Fixed.
  19. sipa force-pushed on Jul 18, 2018
  20. sipa commented at 7:31 pm on July 18, 2018: member

    Rebased, removed future “key origin” support (not needed right now, ignore).

    Also, I’d like people to first review the descriptor language itself (a very short summary is in the RPC help, more of a reference in script/descriptor.h, and a design document in https://gist.github.com/sipa/e3d23d498c430bb601c5bca83523fa82). In particular, I’m not convinced about the “old” function (which maps pubkeys to P2PK,P2PKH,P2WPKH, and P2SH-PWPKH) - it’s very useful for dealing with existing Core wallets, but “old” doesn’t really convey the right meaning. However, I don’t see what else to use. “legacy” is confusing (as it would have a different meaning than legacy in -addresstype). “default” sounds like it could change over time (the intention is that it wouldn’t). “sk” or “singlekey” sounds like it’s encouraged and/or could change over time, …

  21. achow101 commented at 8:06 pm on July 18, 2018: member
    Concept ACK
  22. promag commented at 8:56 pm on July 18, 2018: member
    Regarding the “old” function, I think it could receive the version, for instance default(args..., version) — it should validate if args are compatible with the version specified (at the moment args should be only a pubkey).
  23. sipa commented at 9:06 pm on July 18, 2018: member
    @promag There is no intention of ever adding another of those “defaults”. Future versions should work with specific derivations (only P2WSH directly for example, rather than a collection). It’s only for compatibility with the backward way we used to derive what scriptPubKeys are ours that such a default is useful.
  24. sipa force-pushed on Jul 18, 2018
  25. sipa force-pushed on Jul 18, 2018
  26. laanwj added this to the milestone 0.17.0 on Jul 19, 2018
  27. jonasschnelli requested review from jonasschnelli on Jul 19, 2018
  28. sipa force-pushed on Jul 20, 2018
  29. in src/rpc/blockchain.cpp:2018 in 79b8939362 outdated
    2062+            "          \"desc\": \"descriptor\",   (string, required) An output descriptor\n"
    2063+            "          \"range\": n,             (numeric, optional) Up to what child index HD chains should be explored (default: 1000)\n"
    2064             "        },\n"
    2065-            "      ]\n"
    2066+            "        ...\n"
    2067+            "    ]\n"
    


    Sjors commented at 6:01 pm on July 21, 2018:

    Don’t forget to add a command-line example here, it look me a while to figure out the syntax from reading the above.

    I suggest an example with a typical BIP44 (legacy) xpub + BIP49 xpub:

    0["pkh(legacy_xpub/0/*)", "pkh(legacy_xpub/1/*)", "sh(wpkh(segwit_xpub/0/*)", "sh(wpkh(segwit_xpub/1/*)"]
    

    A second example could show the root master xpriv and then do hardened derivation of xpriv/44’/0’/0’/0/* or xpriv/49’/0’/0’/1/*


    sipa commented at 8:02 pm on July 21, 2018:
    Done.
  30. in src/rpc/blockchain.cpp:1999 in 79b8939362 outdated
    2033+            "\nScans the unspent transaction output set for entries that match certain output descriptors.\n"
    2034+            "Examples of output descriptors are:\n"
    2035+            "    addr(<address>)                      Outputs whose scriptPubKey corresponds to the specified address (does not include P2PK)\n"
    2036+            "    raw(<hex script>)                    Outputs whose scriptPubKey equals the specified hex scripts\n"
    2037+            "    old(<pubkey>)                        P2PK, P2PKH, P2WPKH, and P2SH-P2WPKH outputs for the given pubkey\n"
    2038+            "    pkh(<pubkey>)                        P2PKH outputs for the given pubkey\n"
    


    Sjors commented at 6:02 pm on July 21, 2018:
    sh(wpkh()) is probably quite common, so maybe add that here?

    sipa commented at 8:02 pm on July 21, 2018:
    Done.
  31. Sjors approved
  32. Sjors commented at 6:34 pm on July 21, 2018: member
    Concept ACK, and lightly tested on Ledger (P2SH-P2WPKH) and Blockchain (P2PKH) xpubs.
  33. sipa force-pushed on Jul 21, 2018
  34. sipa force-pushed on Jul 21, 2018
  35. DrahtBot commented at 1:27 pm on July 22, 2018: member
  36. flack commented at 1:30 pm on July 25, 2018: contributor
    @sipa since you write that old is a “combination of P2PK, P2PKH, P2WPKH, and P2SH-P2WPKH for public key P.”, you could call it combo instead. Or ck if you want it to look more like the other things that use P. And potentially prepend bc to indicate it’s for backwards compatibility (but in that case someone should check urban dictionary to make sure bcck isn’t some newfangled swear word :-))
  37. sipa force-pushed on Jul 26, 2018
  38. sipa commented at 7:11 pm on July 26, 2018: member
    @flack Oh, I like that. Changed to “combo”.
  39. MarcoFalke commented at 8:08 pm on July 26, 2018: member

    Travis failure:

    0test_framework.authproxy.JSONRPCException: Invalid descriptor 'old(03abcaca414970a8d36624011628045855cb01cded780445a33688a0b905a47224)' (-5)
    
  40. sipa force-pushed on Jul 26, 2018
  41. jonasschnelli commented at 8:47 am on July 27, 2018: contributor

    First two findings:

    • I think supporting h as equivalent to ' in extended key derivation range would be great (JSON / shell / GUI). I suggest pulling in https://github.com/bitcoin/bitcoin/commit/203b4c94ce83981b0d76dd2adb778719ba868db1.

    • I think the IsHardened() function is not correct. Using 0'/0'/* will derive all hardened keys at the * level (same as 0'/0'/*'). Seems to be currently impossible to derive unhardened key with the asterisk.

  42. in src/rpc/blockchain.cpp:2086 in 852cf1f078 outdated
    2145+            auto desc = Parse(desc_str, provider);
    2146+            if (!desc) {
    2147+                throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid descriptor '%s'", desc_str));
    2148+            }
    2149+            if (!desc->IsRange()) range = 1;
    2150+            for (int i = 0; i < range; ++i) {
    


    jonasschnelli commented at 9:13 am on July 27, 2018:
    Seems like that results in an off-by-one issue (misses last key): Suggest pulling fix: https://github.com/bitcoin/bitcoin/commit/875431149745cc517350c0620baa887c748d0b35

    sipa commented at 6:02 pm on July 27, 2018:
    BIP32 indexes are 0-based, so I think the current code is correct. If you expand 1000 keys, those will be indices 0 through 999.
  43. jonasschnelli changes_requested
  44. jonasschnelli commented at 9:14 am on July 27, 2018: contributor
    Additional xpub range tests can be pulled from here: https://github.com/bitcoin/bitcoin/commit/cc1ffb324b53855f7eae81a6b0df10bd677ca1a5
  45. Sjors commented at 9:24 am on July 27, 2018: member

    +1 on h alias

    I only tested unhardened derivation (xpub/0/*) because most wallets only export account level xpubs. @jonasschnelli maybe add a test for the '/*) pattern to cc1ffb3? It currently only checks '/*')

  46. jonasschnelli commented at 9:54 am on July 27, 2018: contributor
    @Sjors: A test for '/* would currently fail,… but it’s simple to add (keys are already prepared in https://github.com/bitcoin/bitcoin/commit/cc1ffb324b53855f7eae81a6b0df10bd677ca1a5), just waiting for a fix for the hardened/non-hardened mix.
  47. jonasschnelli commented at 9:58 am on July 27, 2018: contributor
    Suggest pulling in the experimental warning for scantxoutset here: https://github.com/bitcoin/bitcoin/commit/3ec0958ea746d0db5edefff9f423cd2caab32857
  48. sipa commented at 6:09 pm on July 27, 2018: member
    @jonasschnelli Good catch; the derivation for “/*” with a parent hardened step is incorrect. Fixing.
  49. Add more methods to Span class
    This introduces a rudimentary begin(), end(), operator[], and subspan to Span.
    29943a904a
  50. Add simple FlatSigningProvider e54d76044b
  51. Output descriptors module fe8a7dcd78
  52. Descriptor tests 0652c3284f
  53. Swap in descriptors support into scantxoutset 151600bb49
  54. [QA] Add xpub range tests in scantxoutset tests 1af237faef
  55. [QA] Extend tests to more combinations 6495849bfd
  56. Add experimental warning to scantxoutset fddea672eb
  57. sipa force-pushed on Jul 27, 2018
  58. sipa commented at 6:53 pm on July 27, 2018: member
    @jonasschnelli Ok, fixed the derivation, changed the range to be max index rather than count, included your tests and experimental warning, and added a number of tests myself (generated with an independent BIP32 implementation).
  59. Support h instead of ' in hardened descriptor paths f6b7fc349c
  60. sipa commented at 7:23 pm on July 27, 2018: member
    @jonasschnelli Added support for h instead of ’ in hardened descriptor paths as well (plus some tests for it). Serializing a descriptor will always use ‘.
  61. jonasschnelli approved
  62. jonasschnelli commented at 7:19 am on July 30, 2018: contributor
    Tested ACK f6b7fc349ccf9cfbeb7e91e19c20e2a2fcc9026f
  63. MarcoFalke commented at 3:52 pm on August 1, 2018: member
    Concept ACK f6b7fc3
  64. laanwj commented at 4:51 pm on August 1, 2018: member
    utACK f6b7fc349ccf9cfbeb7e91e19c20e2a2fcc9026f
  65. laanwj merged this on Aug 1, 2018
  66. laanwj closed this on Aug 1, 2018

  67. laanwj referenced this in commit f030410e88 on Aug 1, 2018
  68. in test/functional/rpc_scantxoutset.py:82 in f6b7fc349c
    83+        assert_equal(self.nodes[0].scantxoutset("start", [ "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/1)"])['total_amount'], Decimal("8.192"))
    84+        assert_equal(self.nodes[0].scantxoutset("start", [ "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/1500)"])['total_amount'], Decimal("16.384"))
    85+        assert_equal(self.nodes[0].scantxoutset("start", [ "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/0)"])['total_amount'], Decimal("4.096"))
    86+        assert_equal(self.nodes[0].scantxoutset("start", [ "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/1)"])['total_amount'], Decimal("8.192"))
    87+        assert_equal(self.nodes[0].scantxoutset("start", [ "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/1500)"])['total_amount'], Decimal("16.384"))
    88+        assert_equal(self.nodes[0].scantxoutset("start", [ {"desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*')", "range": 1499}])['total_amount'], Decimal("1.536"))
    


    Sjors commented at 11:52 am on August 2, 2018:
    Maybe use range: 1 and range: 2 everywhere to speed up these tests?
  69. sipa added the label Needs release notes on Aug 14, 2018
  70. MarcoFalke referenced this in commit 0fa3703c17 on Nov 27, 2018
  71. fanquake removed the label Needs release note on Mar 20, 2019
  72. kittywhiskers referenced this in commit a1e043a62d on Mar 10, 2021
  73. kittywhiskers referenced this in commit 494d99c29e on Mar 23, 2021
  74. kittywhiskers referenced this in commit 10f923b82c on Apr 18, 2021
  75. UdjinM6 referenced this in commit bf01ed7881 on Apr 27, 2021
  76. kittywhiskers referenced this in commit 24fb4555e8 on Jun 4, 2021
  77. kittywhiskers referenced this in commit cbe1968254 on Jun 9, 2021
  78. kittywhiskers referenced this in commit 3a39207e5b on Jul 9, 2021
  79. kittywhiskers referenced this in commit 572170dc85 on Jul 13, 2021
  80. kittywhiskers referenced this in commit 30a49a9470 on Jul 13, 2021
  81. kittywhiskers referenced this in commit 5024e2bd97 on Jul 14, 2021
  82. kittywhiskers referenced this in commit 463dca7e24 on Jul 15, 2021
  83. kittywhiskers referenced this in commit 262a770c72 on Jul 15, 2021
  84. kittywhiskers referenced this in commit 6113107854 on Jul 20, 2021
  85. kittywhiskers referenced this in commit 9edc8caa14 on Jul 20, 2021
  86. kittywhiskers referenced this in commit e3f4888b2b on Jul 20, 2021
  87. kittywhiskers referenced this in commit 2bdb8540b6 on Jul 21, 2021
  88. random-zebra referenced this in commit 56f10aaa51 on Jul 21, 2021
  89. PastaPastaPasta referenced this in commit edf0552c0c on Jul 26, 2021
  90. linuxsh2 referenced this in commit d32d0e78fe on Jul 29, 2021
  91. linuxsh2 referenced this in commit 670d090a52 on Jul 30, 2021
  92. linuxsh2 referenced this in commit 7603e1ce8c on Aug 3, 2021
  93. random-zebra referenced this in commit b4751e10ce on Aug 11, 2021
  94. DrahtBot locked this on Dec 16, 2021

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-12-18 21:12 UTC

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