-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Add rpc wallet protection endpoints #4214
Conversation
Implemented lockwallet, unlockwallet, removewalletpassword, and setwalletpassword methods with basic error handling. Also added basic error handling to existing getbalance method, and removed unused BalancePresentation from CoreAPI. TODO: update help text
There is a bug needing a fix and test case for |
This @nullable class level variable's name needs to specifically describe the use case.
Note that I changed the description from "To close task list in #4198" to "Resolves #4198" so that this PR shows up as "linked" in #4198 and so that it will in fact close #4198 when merged. See https://help.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword. |
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.
NACK. @ghubstan, I have not comprehensively reviewed this yet, but it's clear so far that error handling will need some rework. You've built in success
and error_message
fields into the new *Reply
definitions, but this is not idiomatic gRPC usage. gRPC provides a first-class Status
object, which should be accessed via StatusRuntimeExceptions
thrown from the server side and caught on the client. We're doing this already with invalid passwords, but we now need to do it more consistently from our services.
See https://grpc.io/docs/tutorials/basic/java/#calling-service-methods and https://github.com/grpc/grpc/blob/master/doc/statuscodes.md for details.
Also, and it's my bad for not specifying this, we probably should have cut this up into multiple PRs implementing one endpoint at a time, or at least separate commits for each. I'm not suggesting that you re-do everything in separate PRs/commits here, but it does mean more re-work when we find something like the |
This change removes non-idiomatic gRPC *Reply proto message fields. The client should not receive success/fail values from server methods with a void return type, nor an optional error_message from any server method. This change improves error handling by wrapping an appropriate gRPC Status with a meaningful error description in a StatusRuntimeException, and placing it in the server's response StreamObserver. User error messages are mapped to general purpose gRPC Status codes in a new ApiStatus enum class. (Maybe ApiStatus should be renamed to CoreApiStatus.)
Notes about commit 2a9d1f6 (WIP) I know you will look at all of the changes, but please review my use of the Tuple2 return type used in CoreApi? Wrapping the return value and an ApiStatus code in a single return value -- for void return types too, unfortunately -- seemed like a better idea than throwing Exceptions to be caught by GrpcServer, to then be transformed into gRPC StatusRuntimeExceptions for the client. I don't know what your preference is. I prefer to make the changes after you ask for them. And there is a lot of redundant error handling code in GrpcServer:
There are only service classes in GrpcServer, no private methods yet. I don't know if you want these duplicate code blocks factored out. |
@ghubstan, in preparation to more fully review the use of |
I doubt, by the way, @ghubstan, that the way we want to implement |
@ghubstan, regarding error handling, I've landed on an exception-based solution I'm happy with so far, but not quite ready to commit. Just FYI. |
Previously, each wallet-related method was implemented with its own grpc service. There is no need to do this, as a grpc service may declare multiple rpc methods. This commit refactors everything wallet-related into a single GrpcWalletService and also extracts a CoreWalletService from CoreApi in order to avoid the latter becoming overly large. Ideally, there would be no need for an abstraction in bisq.grpc called CoreWalletService; we would ideally use such a service implemented in bisq.core. The closest we have is WalletsManager, but it is not designed to be used the way we are using it here in the grpc context. Rather than making changes directly to core (which can be very risky), we will rather make them here in this layer, designing exactly the "core wallet service" we need, and can then later see about folding it into the actual core.
39c868f
to
12d2de5
Compare
And throw an IllegalStateException with an appropriate message if the wallet is not available or still locked. This change also eliminates the NullPointerException that would sometimes be thrown when calling #getAvailableBalance after the wallet has become available but before the balance has become available.
12d2de5
to
163061a
Compare
@ghubstan, have a look at what I've done in 163061a to change the return type of Could you refactor the remaining methods in |
Like the change in commit 163061a, setWalletPassword now throws an IllegalStateException with an appropriate message if the wallet password cannot be set. Also deletes unused StatusApi enums.
And delete unused StatusApi enum.
Also remove unused StatusApi class.
I don't see the value in wrapping and rethrowing there. I'd rather see the exception bubble up as-is to the client and then see specific error handling code put wherever it's most appropriate. Wrapping it in an IllegalStateException may not really reflect the nature of the problem, e.g. just a programmer error.
If you don't explicitly return there, then the next |
This commit factors out a run() method in CliMain that either returns (void) or throws an exception. This eliminates the need to call System.exit in so many places as were previously. Indeed, now there is only one place where System.exit is called. It also removes the duplication of printing "Error: ..." to stderr when something goes wrong by doing this once in the global catch clause in the main method.
This variable no longer needs to be initialized to avoid a compiler error.
Cache the aesKey instead of the password in unlock wallet method, avoiding extra overhead of deriving it from the temp password in the lock method. The KeyCrypterScrypt used in the unlock method must also be cached for use in the manual lock method. Creating a new KeyCrypterScrypt instance in the manual lock method would have a different random salt value, invalidating the password used in the unlock method.
This is consistent with :desktop's PasswordView#onApplyPassword.
@cbeams , I am still figuring out how to ensure the wallet is encrypted in the event of a crash before I think an intermediate step in that direction is to decrypt and re-encrypt the wallet only inside the methods that need to temporarily unlock the wallet, instead of decrypting it in the I don't think Tomorrow, I will run :desktop in regtest mode to see how wallet decryption/encryption works while trading and sending btc/bsq to and from other wallets. I realize maybe all that's needed to unlock the wallet is the aesKey, but so far, bitcoin core src tells me it decrypts the in-memory wallet in its (On a related matter: the UI does have a removepassword function.) EDIT: This comment is obsolete. See commit a79ae57. |
There is no need to decrypt and re-encrypt wallet files in the unlock and lock wallet methods. The temporary aesKey saved in the unlock method will used for operations on an encrypted wallet. There is also no need to cache a tempKeyCrypterScrypt; this class level variable was removed.
The correctness of commit a79ae57 needs to be verified by implementing and testing wallet related methods requiring the aesKey cached in For example, a method analogous to bitcoin-cli's |
These tests are meant to be run against rpc methods in PR bisq-network#4214. This change should not be merged before PR bisq-network#4214.
@ghubstan, I just ran into the following scenario when testing everything locally:
It appears there is no way to recover from this state via the cli, and that the only way out is to spin up a fresh data directory. Stopping and starting the server makes no difference either. |
@cbeams , I haven't been able to reproduce the error yet. I don't suppose you still have the server log, and could check if the I've seen this before and should have mentioned it. Fixing tor binaries is out of this PR's scope, but the daemon needs to return a meaningful msg to the cli when this happens. I've been turning my net connection off & on trying to force the tor errors I think are related to the daemon bug. No success so far, but I'll leave daemon on in hopes tor starts failing. |
@ghubstan, I just tried reproducing this on my side and recorded my efforts in the video at keybase://team/bisq/2020-05-12%20repro%20grpc-api%204214%20bug. In the end, I was unable to reproduce it exactly as I reported it. Please have a look at the video, but I'm not sure if it makes sense to focus on this more if I can't reliably reproduce it myself. |
I will see the keybase video later today. But today I am seeing numerous unrecoverable An interval of tor circuit breakdown during startup prevented the wallet balance from being updated.
the initial null wallet balance cannot be updated. But you saw your balance change from a non-null value to null (then you saw When the circuit if finally reconstructed -- however long it takes -- the wallet balance is updated (confirmed by hitting my breakpoint @ I am discussing with @freimair about how this problem is or might be categorized, and how this state could be communicated to the :cli user. This problem is related and possibly identical to the "3/4" problem described in issue 2547. |
The CoreWalletService should never be running more than one wallet lock TimerTask at any given time, and the wallet should lock at the correct time after a user overrides a prior timeout value. The CoreWalletService now cancels any existing lock TimerTask thread before creating a new one, to prevent the wallet from re-locking at unexpected times. This change prevents error conditions created in scenarios similar to the following: (1) User unlocks wallet for 60s (2) User overrides unlock timeout by calling unlockwallet "pwd" 600s (3) After 60s, the 1st, uncanceled lock timeout expires, and wallet is locked 4 minutes too soon.
This should have been done in commit c7a6c87.
@ghubstan If wallet is encrypted you need the aesKey (or generate it from the password) for certain wallet methods. Bisq does not decrypt/encrypt but requests from the user the password to get the aesKey. Encrypting and decrypting is only done when setting or removing the wallet password. Side note: I am not sure about your use cases for the API but passing the wallet password if required for the wallet operation seems to me the right way to deal with it. |
Implemented
lockwallet
,unlockwallet
,removewalletpassword
, andsetwalletpassword
methods with basic error handling.Also added basic error handling to the existing
getbalance
method, and removed unusedBalancePresentation
fromCoreAPI
.Resolves #4198
Notes
I didn't spend time second-guessing maintainers about the ordering of rpc service defs & methods in grpc.proto and GrpcServer, so changes might need to be made in that regard. It makes sense to group wallet protection related methods together, but that is inconsistent with other alphabetically ordered service and method names.
There is also a
getbalance
error handling bug that needs fixing:Error: null
is printed to the :cli console when the daemon is losing peers (SOCKET_TIMEOUT).Bats test cases for these methods are ready to push in a new PR after (if) 4209 is merged.