test: Add importdescriptors rpc error test coverage #35630

pull polespinasa wants to merge 3 commits into bitcoin:master from polespinasa:2026-07-01-addimportdescriptorstestcoverage changing 1 files +77 −1
  1. polespinasa commented at 12:06 PM on July 1, 2026: member

    In addition to #35179 (already merged) this adds more missing test coverage that was detected while rebasing #34861.

    The three tests added checks:

    • Locked wallet throws because of being locked if giving an empty importdescriptors request.
    • Invalid or missing timestamp throws as a top level RPC error and not a per-item error.
    • The order of the requests and the response is the same, even if failing or succeeding.
  2. DrahtBot added the label Tests on Jul 1, 2026
  3. DrahtBot commented at 12:06 PM on July 1, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

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

    <!--006a51241073e994b41acfe9ec718e94-->

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/35630.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline and AI policy for information on the review process.

    Type Reviewers
    ACK nebula-21, Bicaru20, brunoerg
    Stale ACK davidgumberg

    If your review is incorrectly listed, please copy-paste <code>&lt;!--meta-tag:bot-skip--&gt;</code> into the comment that the bot should ignore.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #33392 (wallet, rpc: add UTXO set check and incremental rescan to importdescriptors by musaHaruna)

    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.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

  4. polespinasa force-pushed on Jul 1, 2026
  5. DrahtBot added the label CI failed on Jul 1, 2026
  6. polespinasa force-pushed on Jul 1, 2026
  7. DrahtBot removed the label CI failed on Jul 1, 2026
  8. polespinasa renamed this:
    test: Add importdescriptors rpc error coverage
    test: Add importdescriptors rpc error test coverage
    on Jul 1, 2026
  9. davidgumberg commented at 6:13 PM on July 1, 2026: contributor

    Why is it desirable to enforce the order in which these errors are enforced?

  10. brunoerg commented at 6:39 PM on July 1, 2026: contributor

    Why is it desirable to enforce the order in which these errors are enforced?

    +1

  11. polespinasa commented at 8:23 PM on July 1, 2026: member

    Why is it desirable to enforce the order in which these errors are enforced?

    Because the importdescriptors RPC response is an array. Not a json with key-value elements that can be used to match requests and responses. If the order is not kept if an import fail you cannot know which descriptor is the one that failed.

  12. davidgumberg commented at 9:49 PM on July 1, 2026: contributor

    @polespinasa That makes sense. What I mean is e.g. in https://github.com/bitcoin/bitcoin/pull/35630/changes/2d536455829b904138c8853d9aed636916373e94 it seems that the test is checking that even when the importdescriptors call is erroneous because it's passed an empty list of descriptors, that the error is for being locked and not for being an empty list of descriptors. I guess what I'm asking is why does it matter to enforce which error gets checked for first, that seems like an implementation detail, but maybe I have misunderstood what is being tested for there.

  13. test: Test a locked wallet rejects an empty importdescriptors request
    Co-authored-by: w0xlt <woltx@protonmail.com>
    07fb58b9ef
  14. polespinasa force-pushed on Jul 2, 2026
  15. polespinasa commented at 5:38 AM on July 2, 2026: member

    Oh sorry, I completely misunderstood your question.

    it seems that the test is checking that even when the importdescriptors call is erroneous because it's passed an empty list of descriptors, that the error is for being locked and not for being an empty list of descriptors.

    An empty list of descriptors is not an error. We do accept that in the current implementation. The following diff allows to trigger it:

    $ git diff
    diff --git a/src/wallet/rpc/backup.cpp b/src/wallet/rpc/backup.cpp
    index 396be62825..3d2d7f89c8 100644
    --- a/src/wallet/rpc/backup.cpp
    +++ b/src/wallet/rpc/backup.cpp
    @@ -399,7 +399,7 @@ RPCMethod importdescriptors()
         int64_t lowest_timestamp = 0;
         bool rescan = false;
         UniValue response(UniValue::VARR);
    -    {
    +    if (!requests.empty()) {
             LOCK(pwallet->cs_wallet);
             EnsureWalletIsUnlocked(*pwallet);
     
    

    This test is ensuring that even if we don't provide a list of descriptors to import, the wallet-state related errors are still enforced. We could argue if this behavior is the correct one or not, we could just throw at the really beginning of the RPC call because it does not make sense, but the current state is the one defined in the test. And that is not covered by the tests (before this PR) as I almost silently break it in #34861 as can be seen in: #34861 (review)

    In the last force-push I have added the other case so this expected behavior is clear in the test itself.

    $ git diff 6ca82938bbbe43deeabb65fdbf649687620e3b5e..84bc7db250efb0fc253b2359a8ac879c6e3dd724
    diff --git a/test/functional/wallet_importdescriptors.py b/test/functional/wallet_importdescriptors.py
    index b48db8dbfd..f92b34c7dd 100755
    --- a/test/functional/wallet_importdescriptors.py
    +++ b/test/functional/wallet_importdescriptors.py
    @@ -909,6 +909,11 @@ class ImportDescriptorsTest(BitcoinTestFramework):
             assert_raises_rpc_error(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.",
                 encrypted_wallet.importdescriptors, [])
     
    +        self.log.info("An unlocked wallet accepts an empty importdescriptors request")
    +        self.nodes[0].createwallet("unencrypted_wallet", blank=True)
    +        unencrypted_wallet = self.nodes[0].get_wallet_rpc("unencrypted_wallet")
    +        assert_equal(unencrypted_wallet.importdescriptors([]), [])
    +
             descriptor["timestamp"] = 0
             descriptor["next_index"] = 0
     
    
    
  16. nebula-21 commented at 4:42 PM on July 10, 2026: contributor

    ACK 84bc7db250efb0fc253b2359a8ac879c6e3dd724

  17. in test/functional/wallet_importdescriptors.py:162 in 84bc7db250
     158 | +            }, False),
     159 | +        ]
     160 | +
     161 | +        descriptors, expected = map(list, zip(*cases))
     162 | +        result = wallet.importdescriptors(descriptors)
     163 | +        assert_equal([r["success"] for r in result], expected)
    


    Bicaru20 commented at 10:08 AM on July 15, 2026:

    Since the main goal here is to check that the results are returned in the same order as passed in the request, I think we should check the error messages rather than wheter if it was successfull or not. The way we are currently doing it, if the descriptors that must fail get swapped with each other, the test will still pass. Checking the returned error messages would also catch that case.

    The same applies to the descriptors that are expected to succeed. If those get swapped, we won't be able to detect it either. However, in that case, I don't think there is a way to solve this..

            succes_cases = [
                        ({
                    "desc": descsum_create(f"pkh({get_generate_key().privkey})"),
                    "timestamp": 1,
                    "label": "Valid descriptor1",
                }, True),
                ({
                    "desc": descsum_create(f"pkh({get_generate_key().privkey})"),
                    "timestamp": "now",
                    "internal": True,
                }, True),
            ]
    
            descriptors, expected = map(list, zip(*succes_cases))
            result = wallet.importdescriptors(descriptors)
            assert_equal([r["success"] for r in result], expected)
    
            whitespace_pubkey = f" {get_generate_key().pubkey}"
            failed_cases = [
                ({
                    "timestamp": "now"
                }, 'Descriptor not found.'),
                ({
                    "desc": descsum_create(f"pkh({get_generate_key().pubkey})"),
                    "timestamp": "now",
                    "label": "Invalid descriptor 2",
                    "internal": True,
                }, 'Internal addresses should not have a label'),
                ({
                    "desc": descsum_create(f"pkh({whitespace_pubkey})"),
                    "timestamp": "now",
                    "internal": True,
                }, f"pkh(): Key '{whitespace_pubkey}' is invalid due to whitespace"),
            ]
    
            descriptors, errors = map(list, zip(*failed_cases))
            result = wallet.importdescriptors(descriptors)
            for r,err in zip(result, errors):
                assert_equal(r["success"], False)
                assert_equal(r["error"]["message"], err)
    

    polespinasa commented at 9:49 AM on July 27, 2026:

    taken with a few tweeks

  18. Bicaru20 commented at 10:48 AM on July 15, 2026: contributor

    Concept ACK. Left a suggestion that I think can make the test in 84bc7db250efb0fc253b2359a8ac879c6e3dd724 more robust.

  19. polespinasa force-pushed on Jul 27, 2026
  20. polespinasa force-pushed on Jul 27, 2026
  21. DrahtBot added the label CI failed on Jul 27, 2026
  22. DrahtBot commented at 9:51 AM on July 27, 2026: contributor

    <!--85328a0da195eb286784d51f73fa0af9-->

    🚧 At least one of the CI tasks failed. <sub>Task lint: https://github.com/bitcoin/bitcoin/actions/runs/30255489290/job/89942991968</sub> <sub>LLM reason (✨ experimental): CI failed because the Python lint step (ruff) reported an error (E712) in test/functional/wallet_importdescriptors.py.</sub>

    <details><summary>Hints</summary>

    Try to run the tests locally, according to the documentation. However, a CI failure may still happen due to a number of reasons, for example:

    • Possibly due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    • A sanitizer issue, which can only be found by compiling with the sanitizer and running the affected test.

    • An intermittent issue.

    Leave a comment here, if you need help tracking down a confusing failure.

    </details>

  23. DrahtBot removed the label CI failed on Jul 27, 2026
  24. in test/functional/wallet_importdescriptors.py:130 in e9570adcf8
     125 | @@ -118,6 +126,45 @@ def test_import_unused_noprivs(self):
     126 |                               wallet=wallet)
     127 |          wallet.unloadwallet()
     128 |  
     129 | +    def test_per_item_errors_are_reported_in_order(self):
     130 | +        self.log.info("Test that imports results are in the same order as the original request")
    


    nebula-21 commented at 12:59 PM on July 27, 2026:
            self.log.info("Test that import results are in the same order as the original request")
    
  25. in test/functional/wallet_importdescriptors.py:141 in e9570adcf8
     136 | +                "timestamp": "now"
     137 | +            }, [False, "Descriptor not found."]),
     138 | +            ({
     139 | +                "desc": descsum_create(f"pkh({get_generate_key().privkey})"),
     140 | +                "timestamp": 1,
     141 | +                "label": "Valid descriptor1",
    


    nebula-21 commented at 1:01 PM on July 27, 2026:
                    "label": "Valid descriptor 1",
    
  26. nebula-21 commented at 1:23 PM on July 27, 2026: contributor

    ACK e9570adcf8efd087e1ea4fe48fb9765056e9cf3e

    left two nits

  27. DrahtBot requested review from Bicaru20 on Jul 27, 2026
  28. Bicaru20 commented at 10:23 AM on July 28, 2026: contributor

    ACK e9570adcf8.

  29. sedited removed review request from Bicaru20 on Aug 4, 2026
  30. sedited requested review from brunoerg on Aug 4, 2026
  31. brunoerg commented at 3:01 PM on August 4, 2026: contributor

    Commit e9570adcf8efd087e1ea4fe48fb9765056e9cf3e can be tested with the following mutant:

    diff --git a/src/wallet/rpc/backup.cpp b/src/wallet/rpc/backup.cpp
    index 396be62825..d38ab9f76e 100644
    --- a/src/wallet/rpc/backup.cpp
    +++ b/src/wallet/rpc/backup.cpp
    @@ -477,6 +477,13 @@ RPCMethod importdescriptors()
             }
         }
     
    +    std::vector<UniValue> reordered = response.getValues();
    +    response.clear();
    +    response.setArray();
    +    for (auto it = reordered.rbegin(); it != reordered.rend(); ++it) {
    +        response.push_back(*it);
    +    }
    +
         return response;
     },
         };
    
    

    Just ran all the tests and the added one in e9570adcf8efd087e1ea4fe48fb9765056e9cf3e is the only one that detects it.

  32. in test/functional/wallet_importdescriptors.py:231 in ed2d1ef1b5
     227 | @@ -220,6 +228,25 @@ def run_test(self):
     228 |                               error_code=-8,
     229 |                               error_message='Descriptor not found.')
     230 |  
     231 | +        # # Test import fails if one timestamp is invalid or missing
    


    davidgumberg commented at 5:25 PM on August 4, 2026:

    nit:

    -# #
    +#
    
  33. brunoerg approved
  34. brunoerg commented at 5:44 PM on August 4, 2026: contributor

    ACK e9570adcf8efd087e1ea4fe48fb9765056e9cf3e

    happy to re-ack if nits are addressed.

  35. sedited commented at 8:22 AM on August 5, 2026: contributor

    @polespinasa do you wanna fix the typos here?

  36. test: test invalid or missing timestamp throws importdescriptors
    Also adds global_error to test_importdesc to make it able to test per-item errors or global RPC errors
    e4732bf018
  37. test: test the result order of a multiple import request is correct
    Co-Authored-By: Bicaru20 <bicaru2@gmail.com>
    3ac8b806a6
  38. polespinasa force-pushed on Aug 5, 2026
  39. polespinasa commented at 8:26 AM on August 5, 2026: member

    Force pushed to just fix the typos

  40. nebula-21 commented at 9:44 AM on August 5, 2026: contributor

    ACK 3ac8b806a699a1d518ce225b39aa504f84b4cc06

  41. DrahtBot requested review from Bicaru20 on Aug 5, 2026
  42. DrahtBot requested review from davidgumberg on Aug 5, 2026
  43. DrahtBot requested review from brunoerg on Aug 5, 2026
  44. Bicaru20 commented at 9:54 AM on August 5, 2026: contributor

    re-ACK 3ac8b806a699a1d518ce225b39aa504f84b4cc06

  45. brunoerg approved
  46. brunoerg commented at 11:18 AM on August 5, 2026: contributor

    reACK 3ac8b806a699a1d518ce225b39aa504f84b4cc06

  47. sedited merged this on Aug 5, 2026
  48. sedited closed this on Aug 5, 2026


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-08-11 10:51 UTC

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