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 +73 −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
    Concept ACK Bicaru20

    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. 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
    ed2d1ef1b5
  15. test: test the result order of a multiple import request is correct 84bc7db250
  16. polespinasa force-pushed on Jul 2, 2026
  17. 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
     
    
    
  18. nebula-21 commented at 4:42 PM on July 10, 2026: none

    ACK 84bc7db250efb0fc253b2359a8ac879c6e3dd724

  19. 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)
    
  20. Bicaru20 commented at 10:48 AM on July 15, 2026: none

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


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-07-22 05:50 UTC

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