wallet, rpc: add listdescriptors command #20226

pull S3RK wants to merge 1 commits into bitcoin:master from S3RK:listdescriptors changing 4 files +139 −0
  1. S3RK commented at 4:31 AM on October 23, 2020: member

    Looking for concept ACKs

    Rationale: allow users to inspect the contents of their newly created descriptor wallets.

    Currently the command only returns xpubs which is not very useful in itself, but there are multiples ways to extend it:

    • add an option to export xprv
    • with #19136 it'll be possible to return normalised descriptors suitable for a watch-only purposes

    The output is compatible with importdescriptors command so it could be easily used for backup/recover purposes.

    Output example:

    [
      {
        "desc": "wpkh(tpubD6NzVbkrYhZ4WW6E2ZETFyNfq2hfF23SKxqSGFvUpPAY58jmmuBybwqwFihAyQPk9KnwTt5516NDZRJ7k5QPeKjy7wuVd5WvXNxwwAs5tUD/*)#nhavpr5h",
        "timestamp": 1296688602,
        "active": false,
        "range": [
          0,
          999
        ],
        "next": 0
      }
    ]
    
  2. fanquake added the label Wallet on Oct 23, 2020
  3. in src/wallet/rpcdump.cpp:1775 in ac17caba76 outdated
    1767 | +{
    1768 | +    std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
    1769 | +    if (!wallet) return NullUniValue;
    1770 | +
    1771 | +    if (!wallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
    1772 | +        throw JSONRPCError(RPC_WALLET_ERROR, "listdescriptors is not available for non-descriptor wallets");
    


    luke-jr commented at 3:55 PM on November 19, 2020:

    Should we consider returning inferred descriptors for older wallets?


    S3RK commented at 10:12 AM on December 3, 2020:

    I think it's better UX if we keep descriptors RPC separate and don't do any magic under the hood which could confuse user. From what I see the current approach is to allow users manually and explicitly convert their wallet to descriptors with separate command/rpc call.

    Edit: I looked at migratewallet rpc and it's complicated to infer a descriptor for legacy wallets. Another reason to not do it here.


    achow101 commented at 6:46 PM on December 24, 2020:

    Agree that this should only be available for descriptor wallets.

  4. in src/wallet/rpcdump.cpp:1779 in ac17caba76 outdated
    1774 | +
    1775 | +    UniValue response(UniValue::VARR);
    1776 | +    auto active_spk_mans = wallet->GetActiveScriptPubKeyMans();
    1777 | +    for (const auto &spk_man : wallet->GetAllScriptPubKeyMans()) {
    1778 | +        auto desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man);
    1779 | +        if (desc_spk_man) {
    


    luke-jr commented at 3:55 PM on November 19, 2020:

    Probably should check the opposite and throw, rather than ignore?

    (Brings the indentation level down in the normal scenario too)


    S3RK commented at 10:06 AM on December 3, 2020:

    Thanks for suggestion. Updated

  5. in src/wallet/rpcdump.cpp:1745 in ac17caba76 outdated
    1736 | @@ -1737,3 +1737,69 @@ RPCHelpMan importdescriptors()
    1737 |  },
    1738 |      };
    1739 |  }
    1740 | +
    1741 | +RPCHelpMan listdescriptors()
    1742 | +{
    1743 | +    return RPCHelpMan{
    1744 | +        "listdescriptors",
    1745 | +        "\nList descriptors imported into a wallet.",
    


    luke-jr commented at 3:56 PM on November 19, 2020:

    Should document whatever behaviour we end up with for non-descriptor wallets.


    S3RK commented at 10:13 AM on December 3, 2020:

    For me it's kind of obvious and the error is clear. What do others think?


    promag commented at 4:22 PM on December 5, 2020:

    I also think it's fine as it is although it doesn't hurt to add it. Or you could phrase like List descriptors imported into a descriptor enabled wallet..


    S3RK commented at 1:03 PM on December 10, 2020:

    Thanks. I've taken your suggestion and updated the description.

  6. DrahtBot commented at 6:31 PM on November 19, 2020: member

    <!--e57a25ab6845829454e8d69fc972939a-->

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

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #20892 (tests: Run both descriptor and legacy tests within a single test invocation by achow101)
    • #20012 (rpc: Remove duplicate name and argNames from CRPCCommand by MarcoFalke)

    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.

  7. S3RK force-pushed on Dec 3, 2020
  8. promag commented at 12:57 PM on December 6, 2020: member

    Before going further with this we need to ensure access to ScriptPubKeyMan instances are thread safe and decide their life cycle.

    At the moment CWallet::AddWalletDescriptor mutates m_spk_managers, m_internal_spk_managers and m_external_spk_managers while also destroying a DescriptorScriptPubKeyMan.

    A simple solution to fix this in this new RPC is to lock cs_wallet but I'm not sure if this is the way to go. cc @achow101

  9. S3RK force-pushed on Dec 10, 2020
  10. S3RK commented at 1:09 PM on December 10, 2020: member

    @promag For now I updated the code to acquire cs_wallet which seems reasonable and I don't see better alternatives anyway.

  11. achow101 commented at 6:37 PM on December 24, 2020: member

    Locking cs_wallet should be sufficient.

    Concept ACK.

    Can you add some tests for this?

  12. Sjors commented at 3:37 PM on January 1, 2021: member

    Concept ACK

  13. jonatack commented at 7:08 PM on January 15, 2021: member

    Concept ACK

  14. S3RK force-pushed on Jan 21, 2021
  15. S3RK commented at 9:06 AM on January 21, 2021: member

    Added functional test

  16. in test/functional/wallet_listdescriptors.py:47 in 425403c430 outdated
      42 | +        result = node.get_wallet_rpc('w3').listdescriptors()
      43 | +        assert_equal(6, len(result))
      44 | +        assert_equal(6, len([d for d in result if d['active']]))
      45 | +        assert_equal(3, len([d for d in result if d['internal']]))
      46 | +        for item in result:
      47 | +            assert item['desc'] is not ''
    


    adamjonas commented at 9:27 PM on January 21, 2021:

    linter isn't liking this: test/functional/wallet_listdescriptors.py:47:20: F632 use ==/!= to compare constant literals (str, bytes, int, float, tuple)


    S3RK commented at 8:51 AM on January 25, 2021:

    fixed

  17. meshcollider commented at 12:01 AM on January 23, 2021: contributor

    utACK 425403c430618efdd57019396ef0a05ab0e09387 modulo linter issue

  18. S3RK force-pushed on Jan 25, 2021
  19. meshcollider commented at 11:50 PM on January 25, 2021: contributor

    re-utACK 0e31abf1d3e2caf9875790551569ea65cec34d2e

  20. fanquake requested review from achow101 on Jan 26, 2021
  21. achow101 approved
  22. achow101 commented at 1:35 AM on January 26, 2021: member

    ACK 0e31abf1d3e2caf9875790551569ea65cec34d2e

  23. luke-jr changes_requested
  24. luke-jr commented at 6:25 PM on January 26, 2021: member

    Needs:

    diff --git a/test/functional/wallet_listdescriptors.py b/test/functional/wallet_listdescriptors.py
    index c825c8680e8..d0604d199e5 100755
    --- a/test/functional/wallet_listdescriptors.py
    +++ b/test/functional/wallet_listdescriptors.py
    @@ -7,7 +7,10 @@
     from test_framework.descriptors import (
         descsum_create
     )
    -from test_framework.test_framework import BitcoinTestFramework
    +from test_framework.test_framework import (
    +    BitcoinTestFramework,
    +    SkipTest,
    +)
     from test_framework.util import (
         assert_equal,
         assert_raises_rpc_error,
    @@ -19,6 +22,8 @@ class ReceivedByTest(BitcoinTestFramework):
             self.num_nodes = 1
     
         def skip_test_if_missing_module(self):
    +        if not self.options.descriptors:
    +            raise SkipTest("This test only supports descriptor wallets.")
             self.skip_if_no_wallet()
     
         # do not create any wallet by default
    
    
  25. S3RK commented at 8:51 AM on January 27, 2021: member

    @luke-jr interesting point, thanks. I took a different approach which is more consistent with other descriptor specific tests. I made the test work regardless of whether --descriptors is set. @achow101 There are two more tests so far that run only with --descriptors flag:

    wallet_descriptor
    wallet_importdescriptors
    

    I tested and both of them would work without the flag set. I suggest that we remove that flag from test_runner.py for those tests since it doesn't affect the behaviour. What do you think?

  26. S3RK force-pushed on Jan 27, 2021
  27. luke-jr commented at 4:08 PM on January 27, 2021: member

    @luke-jr interesting point, thanks. I took a different approach which is more consistent with other descriptor specific tests. I made the test work regardless of whether --descriptors is set.

    But as-is, the test FAILS without --descriptors set...?

  28. achow101 commented at 5:34 PM on January 27, 2021: member

    I tested and both of them would work without the flag set. I suggest that we remove that flag from test_runner.py for those tests since it doesn't affect the behaviour. What do you think?

    I have planned improvements for that, namely to not make tests dependent on --descriptors.

    But as-is, the test FAILS without --descriptors set...?

    It does not fail without --descriptors. It basically just ignores --descriptors and --legacy-wallet as a couple of tests already do.

    In the interest of getting this merged, I think it is fine to leave the test as is and resolve the --descriptors problem with the similar tests together.

  29. achow101 commented at 5:34 PM on January 27, 2021: member

    re-ACK bbb34e91ffcaebd8c657ee961279c568eb6dd6d5

  30. in src/wallet/rpcdump.cpp:1748 in bbb34e91ff outdated
    1739 | @@ -1740,3 +1740,72 @@ RPCHelpMan importdescriptors()
    1740 |  },
    1741 |      };
    1742 |  }
    1743 | +
    1744 | +RPCHelpMan listdescriptors()
    1745 | +{
    1746 | +    return RPCHelpMan{
    1747 | +        "listdescriptors",
    1748 | +        "\nList descriptors imported into a descriptor enabled wallet.",
    


    jonatack commented at 6:27 PM on January 27, 2021:
            "\nList descriptors imported into a descriptor-enabled wallet.",
    

    S3RK commented at 8:23 PM on January 27, 2021:

    done

  31. in test/functional/wallet_listdescriptors.py:17 in bbb34e91ff outdated
      12 | +    assert_equal,
      13 | +    assert_raises_rpc_error,
      14 | +)
      15 | +
      16 | +
      17 | +class ReceivedByTest(BitcoinTestFramework):
    


    jonatack commented at 6:33 PM on January 27, 2021:

    nit, could name this class ListDescriptorsTest


    S3RK commented at 8:23 PM on January 27, 2021:

    nice catch, done

  32. in src/wallet/rpcdump.cpp:1780 in bbb34e91ff outdated
    1775 | +        throw JSONRPCError(RPC_WALLET_ERROR, "listdescriptors is not available for non-descriptor wallets");
    1776 | +    }
    1777 | +
    1778 | +    LOCK(wallet->cs_wallet);
    1779 | +
    1780 | +    UniValue response(UniValue::VARR);
    


    jonatack commented at 6:41 PM on January 27, 2021:

    IIRC it's preferred for RPCs to return a JSON object rather than array nowadays.

    This following can be done in a follow-up (if desired), can maybe return something like:

    {
      "wallet_name": "str",
      "descriptors": [
        {descriptor JSON objects}
      ]
      "error": (str, optional) error message, if any
    }
    

    See RPC upgradewallet help for an example.


    S3RK commented at 8:23 PM on January 27, 2021:

    I'll leave it for a follow-up


    S3RK commented at 8:37 AM on February 2, 2021:
  33. in src/wallet/rpcdump.cpp:1794 in bbb34e91ff outdated
    1789 | +        const auto &wallet_descriptor = desc_spk_man->GetWalletDescriptor();
    1790 | +        spk.pushKV("desc", wallet_descriptor.descriptor->ToString());
    1791 | +        spk.pushKV("timestamp", wallet_descriptor.creation_time);
    1792 | +        bool active = active_spk_mans.count(desc_spk_man) != 0;
    1793 | +        spk.pushKV("active", active);
    1794 | +        const auto &type = wallet_descriptor.descriptor->GetOutputType();
    


    jonatack commented at 6:51 PM on January 27, 2021:

    A few const and clang format suggestions

    -    auto active_spk_mans = wallet->GetActiveScriptPubKeyMans();
    -    for (const auto &spk_man : wallet->GetAllScriptPubKeyMans()) {
    -        auto desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man);
    +    const auto active_spk_mans = wallet->GetActiveScriptPubKeyMans();
    +    for (const auto& spk_man : wallet->GetAllScriptPubKeyMans()) {
    +        const auto desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man);
             if (!desc_spk_man) {
                 throw JSONRPCError(RPC_WALLET_ERROR, "Unexpected ScriptPubKey manager type.");
             }
             UniValue spk(UniValue::VOBJ);
             LOCK(desc_spk_man->cs_desc_man);
    -        const auto &wallet_descriptor = desc_spk_man->GetWalletDescriptor();
    +        const auto& wallet_descriptor = desc_spk_man->GetWalletDescriptor();
             spk.pushKV("desc", wallet_descriptor.descriptor->ToString());
             spk.pushKV("timestamp", wallet_descriptor.creation_time);
    -        bool active = active_spk_mans.count(desc_spk_man) != 0;
    +        const bool active = (active_spk_mans.count(desc_spk_man) != 0);
             spk.pushKV("active", active);
    -        const auto &type = wallet_descriptor.descriptor->GetOutputType();
    +        const auto& type = wallet_descriptor.descriptor->GetOutputType();
    

    S3RK commented at 8:23 PM on January 27, 2021:

    done

  34. jonatack commented at 7:03 PM on January 27, 2021: member

    Tested ACK bbb34e91ffcaebd8c657ee961279c568eb6dd6d5 rebased to master

    A few comments below. Happy to re-ACK quickly if you update.

  35. wallet, rpc: add listdescriptors command 647b81b709
  36. S3RK force-pushed on Jan 27, 2021
  37. jonatack commented at 8:55 PM on January 27, 2021: member

    re-ACK 647b81b70938dc4dbcf32399c56f78be395c721a rebased to master, debug builds cleanly, reviewed diff since last review, tested with a descriptor wallet (and with a legacy wallet)

    help output

    listdescriptors
    
    List descriptors imported into a descriptor-enabled wallet.
    Result:
    [                               (json array) Response is an array of descriptor objects
      {                             (json object)
        "desc" : "str",             (string) Descriptor string representation
        "timestamp" : n,            (numeric) The creation time of the descriptor
        "active" : true|false,      (boolean) Activeness flag
        "internal" : true|false,    (boolean, optional) Whether this is internal or external descriptor; defined only for active descriptors
        "range" : [                 (json array, optional) Defined only for ranged descriptors
          n,                        (numeric) Range start inclusive
          n                         (numeric) Range end inclusive
        ],
        "next" : n                  (numeric, optional) The next index to generate addresses from; defined only for ranged descriptors
      },
      ...
    ]
    
    Examples:
    > bitcoin-cli listdescriptors 
    > curl --user myusername --data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "listdescriptors", "params": []}' -H 'content-type: text/plain;' http://127.0.0.1:8332/
    
  38. achow101 commented at 10:32 PM on January 27, 2021: member

    re-ACK 647b81b

    Changes since last just address the review comments.

  39. jonatack commented at 11:29 PM on January 27, 2021: member

    Needs a release note (can be done in a follow-up.)

  40. meshcollider commented at 12:40 AM on January 28, 2021: contributor

    re-utACK

  41. meshcollider merged this on Jan 28, 2021
  42. meshcollider closed this on Jan 28, 2021

  43. sidhujag referenced this in commit a57031f639 on Jan 28, 2021
  44. luke-jr referenced this in commit afb26b244a on Jan 28, 2021
  45. S3RK referenced this in commit a935d2b3be on Feb 1, 2021
  46. S3RK referenced this in commit 1ac5bc03c6 on Feb 1, 2021
  47. S3RK referenced this in commit 51f3752fbe on Feb 2, 2021
  48. S3RK deleted the branch on Feb 2, 2021
  49. MarcoFalke referenced this in commit 5a429d3d0f on Feb 4, 2021
  50. sidhujag referenced this in commit 0abaa33f63 on Feb 4, 2021
  51. fanquake referenced this in commit 7aa0d8adf8 on Apr 2, 2021
  52. sidhujag referenced this in commit 993616e6e9 on Apr 2, 2021
  53. 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: 2026-05-11 18:14 UTC

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