wallet: Only fail rescan when blocks have actually been pruned #15870

pull MarcoFalke wants to merge 3 commits into bitcoin:master from MarcoFalke:1904-walletRescanPruned changing 32 files +80 −62
  1. MarcoFalke commented at 5:55 pm on April 22, 2019: member

    This brings the behaviour of the import* calls closer to importmulti. After this change, the difference between importmulti and the other import* calls is

    • that in importmulti you can “opt-out” of scanning early blocks by setting a later timestamp.
    • that in importmulti the wallet will successfully import the data, but fail to rescan. Whereas in the other calls, the wallet will abort before importing the data.
  2. MarcoFalke force-pushed on Apr 22, 2019
  3. gmaxwell commented at 6:44 pm on April 22, 2019: contributor

    Concept ACK.

    There is an argument against doing things like this: that it’s better to fail deterministically rather than fail randomly at some point in the future and have created incorrect expectations of functionality. But setting prune to some large “future” amount is a perfectly reasonable configuration. And this doesn’t cause it to fail randomly, but rather work until it doesn’t. So I don’t believe the general argument against this sort of thing offsets the benefit.

  4. ryanofsky commented at 7:16 pm on April 22, 2019: member
    This seems to affect importaddress, importpubkey, importprivkey, and importwallet but not importmulti. I wonder how the new behavior compares to importmulti behavior, and if there’s a difference, whether that’s justified.
  5. DrahtBot commented at 7:30 pm on April 22, 2019: member

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

    Conflicts

    No conflicts as of last run.

  6. MarcoFalke commented at 7:34 pm on April 22, 2019: member

    @ryanofsky The only difference between importmulti and the other calls is that in importmulti you can “opt-out” of scanning early blocks by setting a later timestamp. Otherwise, my change makes all calls behave in the same way.

    Edit: I believe the only difference is that importmulti will import the keys and then fail the rescan, whereas the other calls will exit early and not attempt to import anything.

  7. DrahtBot added the label RPC/REST/ZMQ on Apr 22, 2019
  8. DrahtBot added the label Tests on Apr 22, 2019
  9. DrahtBot added the label Wallet on Apr 22, 2019
  10. MarcoFalke force-pushed on Apr 23, 2019
  11. MarcoFalke force-pushed on Apr 23, 2019
  12. promag commented at 8:12 pm on April 24, 2019: member

    Edit: I believe the only difference is that importmulti will import the keys and then fail the rescan, whereas the other calls will exit early and not attempt to import anything.

    Why a different behavior? It’s already possible to call importmulti ... '{"rescan": false}' and then rescanblockchain ...? I think RPCs should (try to) be atomic so that the client doesn’t have to figure out what succeeded or not.

  13. MarcoFalke commented at 8:23 pm on April 24, 2019: member

    Why a different behavior?

    I don’t know. I didn’t write the importmulti RPC and I don’t plan to change it in this pull request.

  14. in src/interfaces/chain.cpp:328 in fad8451939 outdated
    325@@ -326,7 +326,11 @@ class ChainImpl : public Chain
    326     CFeeRate relayIncrementalFee() override { return ::incrementalRelayFee; }
    327     CFeeRate relayDustFee() override { return ::dustRelayFee; }
    328     CAmount maxTxFee() override { return ::maxTxFee; }
    329-    bool getPruneMode() override { return ::fPruneMode; }
    330+    bool havePruned() override
    


    promag commented at 8:33 pm on April 24, 2019:
    Move to the chain lock?

    MarcoFalke commented at 8:58 pm on April 24, 2019:
    Why?

    MarcoFalke commented at 9:00 pm on April 24, 2019:

    Sorry for the brief reply, but see here:

    https://github.com/bitcoin/bitcoin/blob/fad84519391a05f2ad42381cca9135323b709657/src/interfaces/chain.h#L69-L73

    The chain lock is deprecated and will be removed in the future.

  15. in src/wallet/rpcdump.cpp:160 in fad8451939 outdated
    156@@ -157,8 +157,8 @@ UniValue importprivkey(const JSONRPCRequest& request)
    157         if (!request.params[2].isNull())
    158             fRescan = request.params[2].get_bool();
    159 
    160-        if (fRescan && pwallet->chain().getPruneMode()) {
    161-            throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode");
    162+        if (fRescan && pwallet->chain().havePruned()) {
    


    promag commented at 8:38 pm on April 24, 2019:
    A block can be pruned before RescanWallet (L202). What would be the error then?

    MarcoFalke commented at 8:57 pm on April 24, 2019:
    It will throw an error "Rescan was unable to fully rescan the blockchain. Some transactions may be missing."
  16. promag commented at 8:41 pm on April 24, 2019: member

    @MarcoFalke I wasn’t suggesting changing that behavior.

    Concept ACK. Correct me if I’m wrong, but it doesn’t have to fail if all required blocks are available right?

  17. MarcoFalke commented at 9:03 pm on April 24, 2019: member

    Concept ACK. Correct me if I’m wrong, but it doesn’t have to fail if all required blocks are available right?

    importmulti does not fail the rescan when all blocks after the timestamp are available, but it fails after the import if a block is missing. the other import* (after my change) also do not fail the rescan when all blocks after time=0 are available. However, they fail before the import if a block is missing.

  18. MarcoFalke force-pushed on Apr 27, 2019
  19. DrahtBot added the label Needs rebase on Apr 27, 2019
  20. DrahtBot removed the label Needs rebase on Apr 27, 2019
  21. MarcoFalke force-pushed on Apr 29, 2019
  22. luke-jr commented at 6:02 am on May 1, 2019: member
    Let’s use prune height instead, so rescanblockchain can be picky about it?
  23. MarcoFalke commented at 2:02 pm on May 1, 2019: member
    I am not touching rescanblockchain at all. Could you explain what you mean?
  24. luke-jr commented at 11:26 pm on May 1, 2019: member
    I mean it would make sense to use a common interface with rescanblockchain, which would need prune-height rather than have-pruned.
  25. MarcoFalke commented at 1:10 pm on May 2, 2019: member
    Oh, the RPCs I touch are already deprecated in favor of importmulti, so I’d rather not add features to them.
  26. luke-jr commented at 6:17 pm on May 2, 2019: member
    I’m commenting on the src/interfaces changes specifically.
  27. MarcoFalke commented at 6:21 pm on May 2, 2019: member
    Ah. Yeah, since we don’t store the prune height, this would complicate my changes slightly. I think the additional code can be added later (when needed in rescanblockchain).
  28. jnewbery added this to the "Blockers" column in a project

  29. jnewbery commented at 5:25 pm on May 3, 2019: member

    I’m a little concerned about this change. Anywhere that havePruned() is called without cs_main is racy, since a block may be pruned after havePruned() is called. We therefore need to make sure that the code after those havePruned() calls is robust against being run when blocks have been pruned. If that’s the case, why not just remove the chain().getPruneMode() check entirely and let the error-handling in the later code handle when a block is missing?

    The four call sites in rpcdump do not hold cs_main. The call site in CreateWalletFromFile() does hold cs_main indirectly through locked_chain, but a goal is to remove that lock.

  30. MarcoFalke commented at 5:49 pm on May 3, 2019: member

    @jnewbery The check is only there to provide a nice error message. As you correctly observe it is not needed for correctness. Should I clarify that with a comment?

    The error message otherwise would be #15870 (review)

  31. jnewbery commented at 5:57 pm on May 3, 2019: member

    The check is only there to provide a nice error message. As you correctly observe it is not needed for correctness. Should I clarify that with a comment?

    Yes, that sounds good. Thanks!

  32. in test/functional/wallet_import_rescan.py:42 in fa9fa5d647 outdated
    38@@ -40,9 +39,6 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")):
    39     """Helper for importing one key and verifying scanned transactions."""
    40 
    41     def try_rpc(self, func, *args, **kwargs):
    42-        if self.expect_disabled:
    43-            assert_raises_rpc_error(-4, "Rescan is disabled in pruned mode", func, *args, **kwargs)
    44-        else:
    45             return func(*args, **kwargs)
    


    jnewbery commented at 6:59 pm on May 3, 2019:

    nit: indentation

    EDIT: just remove this function entirely


    MarcoFalke commented at 7:33 pm on May 3, 2019:
    Done
  33. jnewbery commented at 7:00 pm on May 3, 2019: member

    utACK fa9fa5d647092710aa03dadf92433584e9fe26a3

    Will happily reACK a squashed branch.

  34. MarcoFalke force-pushed on May 3, 2019
  35. jnewbery commented at 7:46 pm on May 3, 2019: member
    utACK fa9f3cc89442e9b332a27b81b04201bb4a0caf28
  36. in src/wallet/rpcdump.cpp:157 in fa9f3cc894 outdated
    156@@ -157,8 +157,11 @@ UniValue importprivkey(const JSONRPCRequest& request)
    157         if (!request.params[2].isNull())
    


    jonatack commented at 4:11 pm on May 6, 2019:
    Nit: Update year in line 1? // Copyright (c) 2009-2018 The Bitcoin Core developers

    MarcoFalke commented at 6:07 pm on May 6, 2019:
    Done with a scripted diff
  37. in src/wallet/wallet.cpp:4223 in fa9f3cc894 outdated
    4220@@ -4221,8 +4221,11 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
    4221     {
    4222         //We can't rescan beyond non-pruned blocks, stop and throw an error
    4223         //this might happen if a user uses an old wallet within a pruned node
    


    jonatack commented at 4:13 pm on May 6, 2019:
    Nit: space after // in lines 4222 and 4223

    jonatack commented at 4:16 pm on May 6, 2019:
    Perhaps out of PR scope but the docs in lines 4222 and 4223 could be clearer, e.g. is “error” the end of a sentence and “this” the start of a sentence, or…

    MarcoFalke commented at 5:03 pm on May 6, 2019:
    This is only a dev comment, so no need to over-optimize. Will take specific suggestions if I need to touch this pull for other reasons, though.

    jnewbery commented at 5:20 pm on May 6, 2019:
    I don’t think “no need to over-optimize” is a helpful response. The grammar is wrong. Why not correct it?

    MarcoFalke commented at 6:07 pm on May 6, 2019:
    Added a dot and space if that is what you meant
  38. in src/wallet/rpcdump.cpp:589 in fa9f3cc894 outdated
    581@@ -573,7 +582,10 @@ UniValue importwallet(const JSONRPCRequest& request)
    582                 },
    583             }.ToString());
    584 
    585-    if (pwallet->chain().getPruneMode()) {
    586+    if (pwallet->chain().havePruned()) {
    587+        // Exit early and print an error.
    588+        // If a block is pruned after this check, we will import the key(s),
    589+        // but fail the rescan with a generic error.
    590         throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled in pruned mode");
    


    jonatack commented at 4:36 pm on May 6, 2019:
    Does the code doc in 588 correspond correctly with the error message in 589?

    MarcoFalke commented at 5:01 pm on May 6, 2019:
    Yes, “importing wallet” means “importing key(s)”, I believe.

    ryanofsky commented at 4:05 pm on May 15, 2019:
    Maybe s/in pruned mode/when blocks are pruned/ for consistency with the other error messages.

    MarcoFalke commented at 6:11 pm on May 15, 2019:
    Done (and added a release note)
  39. in src/wallet/rpcdump.cpp:324 in fa9f3cc894 outdated
    321-        throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode");
    322+    if (fRescan && pwallet->chain().havePruned()) {
    323+        // Exit early and print an error.
    324+        // If a block is pruned after this check, we will import the key(s),
    325+        // but fail the rescan with a generic error.
    326+        throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled when blocks are pruned");
    


    jonatack commented at 4:45 pm on May 6, 2019:

    Would it be worth extracting this code block repeated in importprivkey, importaddress, importpubkey, and importwallet (the latter with a different error message).

    0    if (fRescan && pwallet->chain().havePruned()) {
    1        // Exit early and print an error.
    2        // If a block is pruned after this check, we will import the key(s),
    3        // but fail the rescan with a generic error.
    4        throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled when blocks are pruned");
    5    }
    

    Edit: I understand it may be a premature or unnecessary optimisation. Just an observation.


    MarcoFalke commented at 5:02 pm on May 6, 2019:
    Yeah, could make sense. Will do if I have to touch for other reasons.

    promag commented at 1:44 pm on May 16, 2019:

    faf3729242c5e9486e137e549f05bf20bd3908a0

    Yeah, in a follow up is fine.

  40. jonatack commented at 4:56 pm on May 6, 2019: member
    A few code comments. More documentation and tests might make this change and the expected behavior before/after clearer, at least to me.
  41. wallet: Only fail rescan when blocks have actually been pruned faf3729242
  42. scripted-diff: Bump copyright headers in wallet
    -BEGIN VERIFY SCRIPT-
    ./contrib/devtools/copyright_header.py update ./src/wallet/
    -END VERIFY SCRIPT-
    aaaa57c2aa
  43. MarcoFalke force-pushed on May 6, 2019
  44. sipa commented at 10:59 pm on May 9, 2019: member
    Concept ACK. My first read of this was that it’d permit rescanning pruned chains, as long as the to-be-rescanned blocks are not pruned, but it seems to just be about whether or not any blocks are actually pruned (independent of the ones to be rescanned), right?
  45. MarcoFalke commented at 12:22 pm on May 10, 2019: member
    The legacy calls would always rescan the whole chain. As soon as one block is pruned, it fails.
  46. wpaulino commented at 4:40 am on May 15, 2019: none
    Out of scope for this PR but somewhat related – would there be any reason to not attempt to fetch the pruned blocks from the P2P network to allow the rescan to happen?
  47. MarcoFalke commented at 12:00 pm on May 15, 2019: member
    Jup, but that might be something to do only after #15946
  48. in test/functional/wallet_import_rescan.py:44 in faf3729242 outdated
    37@@ -39,23 +38,17 @@
    38 class Variant(collections.namedtuple("Variant", "call data rescan prune")):
    39     """Helper for importing one key and verifying scanned transactions."""
    40 
    41-    def try_rpc(self, func, *args, **kwargs):
    42-        if self.expect_disabled:
    43-            assert_raises_rpc_error(-4, "Rescan is disabled in pruned mode", func, *args, **kwargs)
    


    ryanofsky commented at 5:07 pm on May 15, 2019:
    Now there is no longer any test coverage for the “rescan is disabled” cases, but I’m not sure if there’s an easy way to add it back. Maybe a pruneblockchain RPC call could be added.

    MarcoFalke commented at 5:50 pm on May 15, 2019:
    If a test was added, it should also (or more importantly) test importmulti, so I am not actually decreasing test coverage here.

    MarcoFalke commented at 5:51 pm on May 15, 2019:
    I agree that a test would be nice for that case, but can be done in a follow up pull request.

    ryanofsky commented at 6:01 pm on May 15, 2019:

    it should also (or more importantly) test importmulti

    In case it helps, there is currently a unit test for importmulti behavior with pruning: https://github.com/bitcoin/bitcoin/blob/2d16fb7a2b6a9e5a2535295d2de03e27c2438d1f/src/wallet/test/wallet_tests.cpp#L158

  49. ryanofsky approved
  50. ryanofsky commented at 5:17 pm on May 15, 2019: member
    utACK aaaa57c2aad3a6278559ae3db5d953c847c6ffcb. The change seems fine, though it’s not really clear what the motivation for it is. It might be good to add a release note describing the change and what benefits it has from a user perspective.
  51. jamesob commented at 5:23 pm on May 15, 2019: member

    utACK https://github.com/bitcoin/bitcoin/pull/15870/commits/aaaa57c2aad3a6278559ae3db5d953c847c6ffcb

    Makes a lot of sense to allow rescanning for as long as we can as @gmaxwell notes above - and indeed it might even be prudent for most users to set a future-proofing prune value that reflects their disk limitations at startup time. Better that your node start pruning vs. run out of disk space and exit.

    Maybe in the future we could even default to such a thing based on the datadir device if pruning-enabled-but-not-done-yet behavior is equivalent to a non-pruning node. Or introduce an “adaptive” pruning mode, which periodically polls for available disk space and adjusts the prune target accordingly. But of course this is all out of scope for this change.

  52. promag commented at 5:29 pm on May 15, 2019: member
    @jamesob you mean an option to ensure a minimum free space?
  53. [doc] rpcwallet: Only fail rescan when blocks have been pruned fa7e311e16
  54. in src/wallet/rpcdump.cpp:589 in fa7e311e16
    586-        throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled in pruned mode");
    587+    if (pwallet->chain().havePruned()) {
    588+        // Exit early and print an error.
    589+        // If a block is pruned after this check, we will import the key(s),
    590+        // but fail the rescan with a generic error.
    591+        throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled when blocks are pruned");
    


    promag commented at 1:53 pm on May 16, 2019:

    fa7e311e169349bfcf1dab8b980724e8ddf4e749

    This should be in 1s commit.

  55. in src/wallet/rpcdump.cpp:585 in faf3729242 outdated
    581@@ -573,7 +582,10 @@ UniValue importwallet(const JSONRPCRequest& request)
    582                 },
    583             }.ToString());
    584 
    585-    if (pwallet->chain().getPruneMode()) {
    586+    if (pwallet->chain().havePruned()) {
    


    promag commented at 1:55 pm on May 16, 2019:

    faf3729242c5e9486e137e549f05bf20bd3908a0

    The dump file contains timestamps so, correct me if I’m wrong, it could only throw this error if earliest is pruned - the same for importmulti if timestamps are given.

    Edit: especially when manual pruning.


    MarcoFalke commented at 3:20 pm on May 16, 2019:
    Sounds like a nice follow up pull request
  56. promag commented at 1:58 pm on May 16, 2019: member

    This PR would be just fine without aaaa57c2aad3a6278559ae3db5d953c847c6ffcb IMO.

    utACK fa7e311e169349bfcf1dab8b980724e8ddf4e749.

  57. jamesob commented at 2:13 pm on May 16, 2019: member

    @jamesob you mean an option to ensure a minimum free space?

    If you mean “prune as little as possible and only until necessitated by lack of space,” yes. Some kind of adaptive prune would just be a measure to avoid hitting an out-of-disk error for as long as possible. But this is out of scope in terms of this PR so maybe I’ll file an issue later or something.

  58. in src/interfaces/chain.cpp:330 in fa7e311e16
    324@@ -325,7 +325,11 @@ class ChainImpl : public Chain
    325     CFeeRate relayMinFee() override { return ::minRelayTxFee; }
    326     CFeeRate relayIncrementalFee() override { return ::incrementalRelayFee; }
    327     CFeeRate relayDustFee() override { return ::dustRelayFee; }
    328-    bool getPruneMode() override { return ::fPruneMode; }
    329+    bool havePruned() override
    330+    {
    331+        LOCK(cs_main);
    


    jnewbery commented at 2:50 pm on May 16, 2019:
    I’m not suggesting you change this PR, but for future reference we now have a handy WITH_LOCK macro: https://github.com/bitcoin/bitcoin/blob/fd61b9fc22faaa946ca43211c6c43b40ca4a860c/src/sync.h#L209

    MarcoFalke commented at 3:20 pm on May 16, 2019:
    I fail to see how this makes the code any easier to read
  59. jnewbery commented at 2:50 pm on May 16, 2019: member
    utACK fa7e311e169349bfcf1dab8b980724e8ddf4e749
  60. MarcoFalke referenced this in commit d5931f3676 on May 16, 2019
  61. MarcoFalke merged this on May 16, 2019
  62. MarcoFalke closed this on May 16, 2019

  63. MarcoFalke deleted the branch on May 16, 2019
  64. Sjors commented at 3:54 pm on May 16, 2019: member
    post-merge utACK
  65. laanwj removed this from the "Blockers" column in a project

  66. sidhujag referenced this in commit 3fd8014a38 on May 18, 2019
  67. kittywhiskers referenced this in commit 889bf5e84a on Dec 4, 2021
  68. kittywhiskers referenced this in commit 7d38e0d29f on Dec 8, 2021
  69. kittywhiskers referenced this in commit bdc75ccd74 on Dec 8, 2021
  70. kittywhiskers referenced this in commit cfaeeedfca on Dec 12, 2021
  71. kittywhiskers referenced this in commit 5970c36f91 on Dec 13, 2021
  72. kittywhiskers referenced this in commit 9d77298cbe on Dec 13, 2021
  73. kittywhiskers referenced this in commit 73085c6526 on Dec 13, 2021
  74. 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-06-02 01:13 UTC

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