Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Add util::Result failure values, multiple error and warning messages #25665

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

ryanofsky
Copy link
Contributor

@ryanofsky ryanofsky commented Jul 21, 2022

Add util::Result support for returning more error information and make use of it in LoadChainstate method as an initial application. Followup PRs #25722 and #29700 use it more broadly to return errors and warnings from wallet and kernel functions as well.

This change adds two major features to the result class:

  • For better error handling, adds the ability to return a value on failure, not just a value on success. This is a key missing feature that makes the result class not useful for functions like LoadChainstate() which produce different errors that need to be handled differently 1.
  • For better error reporting, adds the ability to return warning messages and multiple errors, not just a single error string. This provides a way for functions to report errors and warnings in a standard way, and simplifies interfaces:

    -virtual std::unique_ptr<Wallet> loadWallet(const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings) = 0;
    +virtual util::Result<std::unique_ptr<Wallet>> loadWallet(const std::string& name) = 0;
    -std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
    +util::Result<std::unique_ptr<WalletDatabase>, DatabaseError> MakeDatabase(const fs::path& path, const DatabaseOptions& options);

This change also makes some more minor improvements:

  • Smaller type size. util::Result<int> is 16 bytes, and util::Result<void> is 8 bytes. Previously both were 72 bytes.

  • Broader type compatibility. Supports noncopyable and nonmovable success and error types.

Alternatives & design notes

The main goal for the util::Result class is to provide a standard way for functions to report error and warnings strings. The interface tries to make it easy to provide detailed error feedback to end users, without cluttering code or making it harder to return other result information. There have been multiple iterations of the design using different internal representations and providing different capabilities:

Representation (approximate) Notes
1 tuple<T,optional<bilingual_str>> Original #25218 implementation in da98fd2. Good capabilities, but larger type size. Supports error standardized error reporting and customized error handling with failure values.
2 variant<T,bilingual_str> Final #25218 implementation in 7a45c33. No support for returning failure values so not useful in many cases.
3 variant<monostate,T,F> Pending #25601 implementation that allows choosing whether to use standardized error reporting or return custom failure values, but doesn't support having both at the same time, like approaches (1), (4), (5), (6) do.
4 tuple<variant<T,F>,bilingual_str> Original #25608 implementation in c29d300. Basically the same as (1) except it uses separate success and failure types instead of the same type. Using separate success and failure types makes the result class a little less flexible but can help avoid some ambiguity and inconsistent result states.
5 tuple<T,optional<bilingual_str>> Final #25608 implementation in dd91f29. Essentially the same as original implementation (1) except has some usability improvements.
6 tuple<T,unique_ptr<tuple<F,bilingual_str>> Pending #25665 implementation (this PR). Supports returning failure values, uses separate success and failure types to avoid ambiguity, uses unique_ptr to reduce result type size, and avoids heap allocation in the happy path.

Prior discussions & history

  • furszy introduced a BResult class providing a standard error reporting mechanism in #25218. It was renamed to util::Result in #25721 but kept the same representation and capabilities.

  • MarcoFalke suggested using BResult for the LoadChainstate function in #25308 (comment). Inability to use BResult there due to lack of support for failure values led to initial work on #25608.

  • w0xlt wrote a StructuredResult class in #25308 adding the ability to return failure values but sacrificing standard error reporting, which led to more work on #25608.

  • martinus suggested a space optimization in #25608 (comment) that also made it easier to support distinct failure & success types, leading to #25665 as a replacement for #25608.

Footnotes

  1. Ability to return error values was actually present in the original implementation of #25218, but unfortunately removed in later iterations.

@ryanofsky
Copy link
Contributor Author

Draft PR since I want to add a commit taking advantages of ability to return chain results and return warnings.

@DrahtBot
Copy link
Contributor

DrahtBot commented Jul 21, 2022

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

Code Coverage & Benchmarks

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

Reviews

See the guideline for information on the review process.

Type Reviewers
Approach ACK hebasto
Stale ACK w0xlt, stickies-v, hernanmarino, jonatack, achow101, maflcko, TheCharlatan

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #31616 (init,log: Unify block index log line by l0rinc)
  • #31483 (kernel: Move kernel-related cache constants to kernel cache by TheCharlatan)
  • #31061 (refactor: Check translatable format strings at compile-time by maflcko)
  • #30965 (kernel: Move block tree db open to block manager by TheCharlatan)
  • #29641 (scripted-diff: Use LogInfo over LogPrintf [WIP, NOMERGE, DRAFT] by maflcko)
  • #19461 (multiprocess: Add bitcoin-gui -ipcconnect option by ryanofsky)
  • #19460 (multiprocess: Add bitcoin-wallet -ipcconnect option by ryanofsky)
  • #10102 (Multiprocess bitcoin by ryanofsky)

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.

@DrahtBot DrahtBot removed the CI failed label Nov 4, 2024
ryanofsky added a commit to ryanofsky/bitcoin that referenced this pull request Nov 4, 2024
Use result.Update in CompleteChainstateInit() and
ChainstateManager::ActivateSnapshot() so it is possible for them to return
warning messages (about flush failures) in upcoming commits.

CompleteChainstateInit() was previously changed to use util::Result in bitcoin#25665
and ChainstateManager::ActivateSnapshot() was changed to use it in bitcoin#30267, but
previously these functions only returned Result values themselves, and did not
call other functions that return Result values. Now, some functions they are
calling will also return Result values, so refactor these functions to use
result.Update so they can merge results and return complete error and warning
messages.
@stickies-v stickies-v mentioned this pull request Nov 8, 2024
@ryanofsky
Copy link
Contributor Author

ryanofsky commented Dec 6, 2024

Rebased 7e2b357 -> 58c33f0 (pr/bresult2.61 -> pr/bresult2.62, compare) due to conflict with #30377
Rebased 58c33f0 -> 5e14665 (pr/bresult2.62 -> pr/bresult2.63, compare) due to conflict with #31072

ryanofsky and others added 6 commits December 6, 2024 07:38
Add util::Result support for returning more error information. For better error
handling, adds the ability to return a value on failure, not just a value on
success. This is a key missing feature that made the result class not useful
for functions like LoadChainstate() which produce different errors that need to
be handled differently.

This change also makes some more minor improvements:

- Smaller type size. util::Result<int> is 16 bytes, and util::Result<void> is 8
  bytes. Previously util::Result<int> and util::Result<void> were 72 bytes.

- More documentation and tests.
Previous commit causes the warning to be triggered, but the suboptimal return
from const statement was added earlier in 517e204.

Warning was causing CI failure https://cirrus-ci.com/task/6159914970644480
Add util::Result Update method to make it possible to change the value of an
existing Result object. This will be useful for functions that can return
multiple error and warning messages, and want to be able to change the result
value while keeping existing messages.
Add util::Result support for returning warning messages and multiple errors,
not just a single error string. This provides a way for functions to report
errors and warnings in a standard way, and simplifies interfaces.

The functionality is unit tested here, and put to use in followup PR
bitcoin#25722
Suggested by Martin Leitner-Ankerl <martin.ankerl@gmail.com>
bitcoin#25722 (comment)

Co-authored-by: Martin Leitner-Ankerl <martin.ankerl@gmail.com>
ryanofsky added a commit to ryanofsky/bitcoin that referenced this pull request Dec 6, 2024
Use result.Update in CompleteChainstateInit() and
ChainstateManager::ActivateSnapshot() so it is possible for them to return
warning messages (about flush failures) in upcoming commits.

CompleteChainstateInit() was previously changed to use util::Result in bitcoin#25665
and ChainstateManager::ActivateSnapshot() was changed to use it in bitcoin#30267, but
previously these functions only returned Result values themselves, and did not
call other functions that return Result values. Now, some functions they are
calling will also return Result values, so refactor these functions to use
result.Update so they can merge results and return complete error and warning
messages.
ryanofsky added a commit to ryanofsky/bitcoin that referenced this pull request Dec 6, 2024
Use result.Update in CompleteChainstateInit() and
ChainstateManager::ActivateSnapshot() so it is possible for them to return
warning messages (about flush failures) in upcoming commits.

CompleteChainstateInit() was previously changed to use util::Result in bitcoin#25665
and ChainstateManager::ActivateSnapshot() was changed to use it in bitcoin#30267, but
previously these functions only returned Result values themselves, and did not
call other functions that return Result values. Now, some functions they are
calling will also return Result values, so refactor these functions to use
result.Update so they can merge results and return complete error and warning
messages.
ryanofsky added a commit to ryanofsky/bitcoin that referenced this pull request Dec 6, 2024
Use result.Update in CompleteChainstateInit() and
ChainstateManager::ActivateSnapshot() so it is possible for them to return
warning messages (about flush failures) in upcoming commits.

CompleteChainstateInit() was previously changed to use util::Result in bitcoin#25665
and ChainstateManager::ActivateSnapshot() was changed to use it in bitcoin#30267, but
previously these functions only returned Result values themselves, and did not
call other functions that return Result values. Now, some functions they are
calling will also return Result values, so refactor these functions to use
result.Update so they can merge results and return complete error and warning
messages.
ryanofsky added a commit to ryanofsky/bitcoin that referenced this pull request Dec 6, 2024
Use result.Update in CompleteChainstateInit() and
ChainstateManager::ActivateSnapshot() so it is possible for them to return
warning messages (about flush failures) in upcoming commits.

CompleteChainstateInit() was previously changed to use util::Result in bitcoin#25665
and ChainstateManager::ActivateSnapshot() was changed to use it in bitcoin#30267, but
previously these functions only returned Result values themselves, and did not
call other functions that return Result values. Now, some functions they are
calling will also return Result values, so refactor these functions to use
result.Update so they can merge results and return complete error and warning
messages.
@DrahtBot
Copy link
Contributor

🐙 This pull request conflicts with the target branch and needs rebase.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.