wallet: ignore chainStateFlushed notifications while attaching chain #24984

pull mzumsande wants to merge 1 commits into bitcoin:master from mzumsande:202204_wallet_rescan changing 2 files +10 −0
  1. mzumsande commented at 11:57 PM on April 25, 2022: member

    Fixes #24487

    When a rescan is performed during CWallet::AttachChain() (e.g. when loading an old wallet) but this is interrupted by a shutdown signal, the wallet will currently stop the rescan, receive a chainStateFlushed signal, set the saved best block to the tip and shut down. At next startup, the rescan is not continued or repeated because of this. But some blocks have never been scanned by the wallet, which could lead to an incorrect balance.

    Fix this by ignoring chainStateFlushed notifications until the chain is attached. Since CWallet::chainStateFlushed is being manually called by AttachChain() anyway after finishing with the rescan, it is not a problem if intermediate notifications are ignored.

    Manual rescans started / aborted by the rescanblockchain / abortrescan RPCs are not affected by this.

    I didn't choose alternative ways of fixing this issue that would delay the validationinterface registration or change anything else about the handling of blockConnected signals for the reasons mentioned in this existing comment.

  2. DrahtBot added the label Wallet on Apr 26, 2022
  3. mzumsande marked this as a draft on Apr 26, 2022
  4. mzumsande commented at 1:09 AM on April 26, 2022: member

    This breaks feature_pruning.py, putting into draft until I have analyzed it.

    Fixed.

  5. in src/wallet/wallet.cpp:3018 in d533b0429c outdated
    3014 | @@ -3007,6 +3015,7 @@ bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interf
    3015 |                  return false;
    3016 |              }
    3017 |          }
    3018 | +        walletInstance->m_attaching_chain = false;
    


    S3RK commented at 7:36 AM on April 26, 2022:

    If you never started a rescan, than you would leave m_attaching_chain in true state forever


    mzumsande commented at 8:18 AM on April 26, 2022:

    Oh yes, thanks. Moved the spot where we set m_attaching_chain into the conditional, which should also fix the previous issue with feature_pruning.

  6. S3RK commented at 7:37 AM on April 26, 2022: member

    With current implementation we don't save the rescan progress and on the next load we will have to start from scratch. Is it possible to save the last processed blocked at shutdown?

  7. wallet: ignore chainStateFlushed notifications while attaching chain 2052e3aa9a
  8. mzumsande force-pushed on Apr 26, 2022
  9. fanquake requested review from ryanofsky on Apr 26, 2022
  10. mzumsande commented at 10:27 AM on April 26, 2022: member

    With current implementation we don't save the rescan progress and on the next load we will have to start from scratch. Is it possible to save the last processed blocked at shutdown?

    Yes, that's true. I think it should be possible to call chainStateFlushed with a locator to the block that was last processed - I'll look into that. Of course it would save some time only in the rather rare situation of a clean shutdown during rescan (the bug must have existed for years but wasn't reported until very recently), so I'm not sure how important a feature like this would actually be for users.

  11. mzumsande marked this as ready for review on Apr 26, 2022
  12. laanwj commented at 1:37 PM on April 26, 2022: member

    Concept ACK

    so I'm not sure how important a feature like this would actually be for users.

    Right, I'm concerned about unexpected behavior. I don't think optimality is important in this case.

  13. DrahtBot commented at 2:50 PM on April 26, 2022: 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:

    • #23997 (wallet: avoid rescans under assumed-valid blocks by jamesob)

    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.

  14. laanwj added the label Bug on Apr 26, 2022
  15. S3RK commented at 6:50 PM on April 26, 2022: member

    Yes, that's true. I think it should be possible to call chainStateFlushed with a locator to the block that was last processed - I'll look into that. Of course it would save some time only in the rather rare situation of a clean shutdown during rescan (the bug must have existed for years but wasn't reported until very recently), so I'm not sure how important a feature like this would actually be for users.

    The wallet already tracks last processed block, why can't we just write that form chainStateFlushed handler regardless of what locator was passed in. That way we don't need any bool flags I believe.

  16. mzumsande commented at 8:53 PM on April 26, 2022: member

    The wallet already tracks last processed block, why can't we just write that form chainStateFlushed handler regardless of what locator was passed in. That way we don't need any bool flags I believe.

    Not sure I understand this right - do you mean why locators are used in general instead of single block hashes? I think that's because we want to be flexible and not dependent on a specific block: There may be a reorg so a given block is no longer part of the chain when the wallet is loaded, or maybe the the wallet called the chainStateFlushed handler manually without the actual chainstate being flushed, and then an unclean shutdown happens so that the wallet would be ahead of the chain at next startup.

    In any case, any block must be converted to a locator anyway to be stored in BESTBLOCK_NOMERKLE in order not to break the db format.

    Also note that AttachChain() updates m_last_block_processed optimistically to the tip (before actually processing any missing blocks) and not incrementally. So if it gets interrupted early, m_last_block_processed is not correct.

  17. ryanofsky approved
  18. ryanofsky commented at 6:28 PM on April 27, 2022: member

    Code review ACK 2052e3aa9aa666bdc86dac370f1dd8fb978d3497. This is a straightforward fix for the bug described in #24487 where a wallet could skip scanning blocks if is shut down in the middle of a sync and a chainStateFlushed notification was received during the sync. It would be nice to write a test for this but probably would be tricky to write.

    S3RK raises a great point in #24984#pullrequestreview-952900468 that the wallet doesn't save the scan position during syncs, so if it is interrupted, parts of the scan could be repeated unnecessarily next time the wallet is loaded. This issue would be good to fix, and I created #25010 to track it, but I think it's basically a separate issue with a separate fix. This PR makes that issue more likely to happen, but in the cases it does this, current behavior of skipping unscanned blocks is worse than new behavior of rescanning already-scanned blocks, so in every case this PR should be a strict improvement over the status quo.

  19. achow101 commented at 8:27 PM on April 27, 2022: member

    ACK 2052e3aa9aa666bdc86dac370f1dd8fb978d3497

  20. S3RK commented at 7:05 AM on April 28, 2022: member

    Agree that "this PR should be a strict improvement over the status quo".

    The only thing that still slightly worries me is two return false statements between setting and unsetting the m_attaching_chain flag. In both cases rescan fails, so I guess the behaviour is still correct, but it looks fragile.

  21. mzumsande commented at 11:00 AM on April 28, 2022: member

    It would be nice to write a test for this but probably would be tricky to write.

    Yes, I'd like that too. the problem is how to deal with the racing condition, timing the shutdown to be during the sync and not after. I played around a bit with bpftrace / uprobes after an idea by @jnewbery (e.g. hook into a function such as SyncTransaction() and send a SIGINT when it is invoked the 20th time) and that is really cool and seems to work, but obviously still very experimental and not ready for the CI.

    The only thing that still slightly worries me is two return false statements between setting and unsetting the m_attaching_chain flag. In both cases rescan fails, so I guess the behaviour is still correct, but it looks fragile.

    Yes, we currently rely on this to be the case. We aren't only concerned about accidental signals during the sync but also after it, caused by the interrupting signal: In case of a shutdown by SIGINT, Shutdown() in Init will create a chainStateFlushed signal which is processed by the wallet. At this point in time, m_attaching_chain must still be true or the bug is not fixed.

  22. w0xlt approved
  23. w0xlt commented at 3:59 PM on April 28, 2022: contributor

    Code Review ACK https://github.com/bitcoin/bitcoin/pull/24984/commits/2052e3aa9aa666bdc86dac370f1dd8fb978d3497

    If I understand correctly, the problem is that at the end of CWallet::AttachChain(), CWallet::chainStateFlushed(...) is called passing the chain tip as parameter, even if abort or shutdown is requested during the wallet scan (CWallet::ScanForWalletTransactions).

    I think this solution achieves the goal of preventing the wallet from being in an inconsistent state by forcing it to write the tip only after the scan completes.

    Ideally there would be a functional test for this, but I don't see how to simulate this on regtest.

  24. achow101 merged this on Apr 28, 2022
  25. achow101 closed this on Apr 28, 2022

  26. sidhujag referenced this in commit a54a7e137a on Apr 29, 2022
  27. achow101 referenced this in commit 98f4db3305 on May 16, 2022
  28. mzumsande deleted the branch on May 19, 2022
  29. luke-jr referenced this in commit c212cf68fb on May 21, 2022
  30. sidhujag referenced this in commit 889921789f on May 28, 2022
  31. DrahtBot locked this on Jun 9, 2023

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-04-13 21:13 UTC

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