-
Notifications
You must be signed in to change notification settings - Fork 38.7k
refactor: Return util::Result from WalletLoader methods
#25616
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
Conversation
furszy
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
left a quick review
src/qt/walletcontroller.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| auto wallet{node().walletLoader().loadWallet(path, m_warning_message)}; | |
| if (wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(wallet)); | |
| m_error_message = wallet ? bilingual_str{} : wallet.GetError(); | |
| if (wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(wallet.ReleaseObj()); | |
| auto wallet{node().walletLoader().loadWallet(path, m_warning_message)}; | |
| if (wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(wallet.ReleaseObj()); | |
| else m_error_message = wallet.GetError(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 948651d.
src/qt/walletcontroller.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| auto wallet{node().walletLoader().createWallet(name, m_passphrase, flags, m_warning_message)}; | |
| if (wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(wallet)); | |
| m_error_message = wallet ? bilingual_str{} : wallet.GetError(); | |
| if (wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(wallet.ReleaseObj()); | |
| auto res_wallet{node().walletLoader().createWallet(name, m_passphrase, flags, m_warning_message)}; | |
| if (res_wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(res_wallet.ReleaseObj()); | |
| else m_error_message = res_wallet.GetError(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 3c96b25.
5eb9f50 to
324d3bb
Compare
src/qt/walletcontroller.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Below I used m_error_message = wallet ? bilingual_str{} : wallet.GetError();
It would be good to use the same code consistently everywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 7cbfa13. Thanks for the review.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the code change was not correctly pushed.
The commit 7cbfa1335bd89d5831e916672e57aa8601b12bb4 still displays the line:
else m_error_message = wallet.GetError();
and not:
m_error_message = wallet ? bilingual_str{} : wallet.GetError();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@shaavan I said the suggestion was addressed because I understand it's about keeping all code consistent across all commits.
I had originally defined all m_error_message same way as done in #25594: m_error_message = wallet ? bilingual_str{} : wallet.GetError();.
So #25616 (comment) and #25616 (comment) suggested changing them to else m_error_message = res_wallet.GetError(); but I had forgotten to change it to restoreWallet(). 7cbfa13 fixed this.
If I understand the BResult interface correctly, it seems to be the clearest option, as BResult<T>::GetError() will return the default bilingual_str{} : value if T is not set.
However, both approaches work. If the reviewers have a strong opinion about one of them, I can change it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BResult is essentially an std::variant wrapper. It stores one object OR the other (succeed or failure). Not both.
So, it is redundant to ask m_error_message = wallet ? bilingual_str{} : wallet.GetError(); first and then ask if succeeded in another if block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
m_error_message = wallet ? bilingual_str{} : wallet.GetError(); was intentional to reset the member that stores the error, instead of leaving the previous value, if no error occurred. However, I am fine changing it to something else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I double checked it before comment. The error message is only set here after calling the backend method. So, the string will always be empty if no error occurs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to load a wallet with an error and then load a wallet without error on the same walletcontroller?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I missed the comment, sorry Marko.
Is it possible to load a wallet with an error and then load a wallet without error on the same walletcontroller?
You mean, restoring or opening a wallet?
Because if that is the case, there shouldn't be any problem. Every time that we restore or open a wallet we create a new WalletControllerActivity subclass which encapsulates the error and warning messages (it's a single shot class. It gets deleted as soon as it finishes processing the action).
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsReviewers, this pull request conflicts with the following ones:
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. |
324d3bb to
7cbfa13
Compare
|
#25616 (comment) addressed in 7cbfa13. |
aureleoules
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK 7cbfa1335bd89d5831e916672e57aa8601b12bb4.
I think this is a good use-case of BResult.
nit: Shouldn't the commits be merged into one as the changes are small and related?
src/wallet/interfaces.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can makeWallet be renamed to makeWalletResult or something else? Having both createWallet and makeWallet is a bit confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 087959d. Thanks.
shaavan
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Concept ACK
- I agree with the idea of using BResult as the return type for walletloader method, as it ensures that the return value has either the result value (if succeed) or the error value (if failure) and never both.
- The code changes look clean and concise. I want to look further into this comment before ACKing the correctness of code change.
Is it possible to load a wallet with an error and then load a wallet without an error on the same walletcontroller?
- In the meantime, I would suggest squashing the commits together.
7cbfa13 to
087959d
Compare
|
#25616 (review) and #25616 (review) were addressed in 087959d. |
shaavan
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK 087959d6e92ed37b72b86dbbc9b0ff052a6c5a75
Changes since my last review:
- Renamed
makeWallet->makeWalletResult. - Squashed commits.
As per @furszy explanation in this comment, since the WalletControllerActivity class gets deleted after the loading of a wallet finishes, it is not possible to have an error message stored from a previous erroneous loading of a wallet.
Hence it is not necessary to manually reset bilingual_str{}.
Thanks, @furszy, for the explanation.
jonatack
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approach ACK
src/qt/walletcontroller.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- if (res_wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(res_wallet.ReleaseObj());
- else m_error_message = res_wallet.GetError();
+ if (res_wallet) {
+ m_wallet_model = m_wallet_controller->getOrCreateWallet(res_wallet.ReleaseObj());
+ } else {
+ m_error_message = res_wallet.GetError();
+ }Any reason why you've changed the variable name from wallet to res_wallet here? It's wallet in the identical code sections below.
src/qt/walletcontroller.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- if (wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(wallet.ReleaseObj());
- else m_error_message = wallet.GetError();
+ if (wallet) {
+ m_wallet_model = m_wallet_controller->getOrCreateWallet(wallet.ReleaseObj());
+ } else {
+ m_error_message = wallet.GetError();
+ }
src/qt/walletcontroller.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- if (wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(wallet.ReleaseObj());
- else m_error_message = wallet.GetError();
+ if (wallet) {
+ m_wallet_model = m_wallet_controller->getOrCreateWallet(wallet.ReleaseObj());
+ } else {
+ m_error_message = wallet.GetError();
+ }017b473 to
f179998
Compare
jonatack
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review/debug build/unit tests ACK f179998f8072ef5f754d42b5d3d7c14e2fdfe7a3
src/interfaces/wallet.h
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for picking up #25721 (comment) here and fixing up indentation in this file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The wallet method still creates a copy?
$ git grep GetNewDestination -- '*wallet.h'
src/wallet/wallet.h: util::Result<CTxDestination> GetNewDestination(const OutputType type, const std::string label);
src/wallet/interfaces.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit if you repush, here and in createWallet() above, while touching this line can use Clang-tidy named arg format (/*load_on_start=*/true) like in restoreWallet() below.
src/wallet/interfaces.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit, could save 9 lines by replacing the above 4 lines in each of the 3 methods with identical logic in one line: return wallet ? wallet : util::Error{error}; (feel free to ignore)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would be good, but applying this change will result in the error below.
I'm not sure about the reason for the error.
wallet/interfaces.cpp:577:25: error: call to implicitly-deleted copy constructor of 'util::Result<std::unique_ptr<Wallet>>'
return wallet ? wallet : util::Error{error};
^~~~~~
./util/result.h:38:36: note: copy constructor of 'Result<std::unique_ptr<interfaces::Wallet>>' is implicitly deleted because field 'm_variant' has a deleted copy constructor
std::variant<bilingual_str, T> m_variant;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, std::unique_ptr is move constructible and move assignable but not copy constructible or copy assignable. So this would work: return wallet ? std::move(wallet) : util::Error{error};
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. Done in 466374f.
So return wallet moves the object and return wallet ? wallet : util::Error{error}; copies the object. Interesting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be that return with a ternary doesn't benefit from RVO (return value optimization) by the compiler in the same way as with an if statement.
Edit: benefitting from RVO could (possibly, unsure) be an argument for using the more verbose if statement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting discussion about this:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, not a blocker but this is a topic I plan to go deeper into in order to improve my understanding.
theStack
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Concept ACK
nit: Same as done in the commit message, the PR title and description should also be adapted to the latest rebase (s/BResult/util::Result/).
BResult from WalletLoader methodsutil::Result from WalletLoader methods
f179998 to
466374f
Compare
|
CI error seems unrelated. |
.vscode/settings.json
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this file invited itself into the last push, ACK 466374f6c1a032d7540b1c9307631f56fe555fcf otherwise :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed. Thanks.
466374f to
be13477
Compare
jonatack
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK be13477a82e4dc33a88d49dcf3f7a296bcb6c05c
src/wallet/test/wallet_tests.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for picking up #25721 (comment) here.
src/wallet/scriptpubkeyman.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for picking up the suggestion #25721 (comment) here.
furszy
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK be13477a, just a small comment.
src/qt/walletcontroller.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why the revert here? (and in the others as well).
the result could be an object or an error, not both.
if (wallet) m_wallet_model = something;
else m_error_message = util::ErrorString(wallet);Thinking that might be good to add an assertion inside util::ErrorString, callers should always check that the result is an error prior to convert it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe assert(std::holds_alternative<bilingual_str>(result.m_variant)); inside util::ErrorString ?
However this assertion cause a compile-time error error: static_assert failed due to requirement '__detail::__variant::__exactly_once<bilingual_str, bilingual_str, bilingual_str>' "T must occur exactly once in alternatives"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep, that was where I was pointing to.
assert(!result.has_value()); there should work fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I applied the suggestion above (if (wallet) m_wallet_model = something;) in ac74799.
But regarding the assertion, I think a follow-up PR is better because it also requires changing the src/test/result_tests.cpp which expects the result of util::ErrorString be {} if the object has value This would increase the scope of this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing brackets in the conditionals again (e.g. #25616 (comment))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"If an if only has a single-statement then-clause, it can appear on the same line as the if, without braces. In every other case, braces are required, and the then and else clauses must appear correctly indented on a new line."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep, aren't we having a single line statement here?
if (wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(*wallet));
else m_error_message = util::ErrorString(wallet);be13477 to
ac74799
Compare
furszy
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK ac74799
ac74799 to
84b08f3
Compare
84b08f3 to
07df6cd
Compare
|
Rebased. |
|
Review ACK 07df6cd |
theStack
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code-review ACK 07df6cd
| public: | ||
| //! Create new wallet. | ||
| virtual std::unique_ptr<Wallet> createWallet(const std::string& name, const SecureString& passphrase, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings) = 0; | ||
| virtual util::Result<std::unique_ptr<Wallet>> createWallet(const std::string& name, const SecureString& passphrase, uint64_t wallet_creation_flags, std::vector<bilingual_str>& warnings) = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would probably be best to also wrap the warnings in the result? Are you working on this @ryanofsky ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re: #25616 (comment)
Would probably be best to also wrap the warnings in the result? Are you working on this @ryanofsky ?
It needs rebase, but yes I did this in #25722
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I meant a minimal extract without the other C++ bloat (for example Result<void>) that isn't needed for simply passing warnings
| auto wallet{node().walletLoader().restoreWallet(backup_file, wallet_name, m_warning_message)}; | ||
|
|
||
| m_error_message = util::ErrorString(wallet); | ||
| if (wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(*wallet)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I preferred the previous code, as it is shorter and less ambiguous
…r methods 07df6cd wallet: Return `util::Result` from WalletLoader methods (w0xlt) Pull request description: This PR adds a method that implement common logic to WalletLoader methods and change them to return `BResult<std::unique_ptr<Wallet>>`. Motivation: bitcoin#25594 changed `restoreWallet` to return `BResult` but this method shares a common pattern with `createWallet` and `loadWallet`. This PR keeps the same pattern to all WalletLoader methods. ACKs for top commit: jonatack: Review ACK 07df6cd theStack: Code-review ACK 07df6cd Tree-SHA512: 2fe321134883f7cce60206888113800afef0fa168dab184e1a8692cd21a231970eb9c25c7220ea39e5d457134002d47f0974463925db76abbf8dfcd421629c63
This PR adds a method that implement common logic to WalletLoader methods and change them to return
BResult<std::unique_ptr<Wallet>>.Motivation: #25594 changed
restoreWalletto returnBResultbut this method shares a common pattern withcreateWalletandloadWallet. This PR keeps the same pattern to all WalletLoader methods.