From 39c868f5959679993b5c12691b34a308c94068c5 Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Sat, 2 May 2020 11:21:18 +0200 Subject: [PATCH] Refactor grpc wallet service 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. --- cli/src/main/java/bisq/cli/CliMain.java | 27 ++-- .../src/main/java/bisq/core/grpc/CoreApi.java | 134 +--------------- .../bisq/core/grpc/CoreWalletService.java | 149 ++++++++++++++++++ .../main/java/bisq/core/grpc/GrpcServer.java | 140 +++------------- .../bisq/core/grpc/GrpcWalletService.java | 102 ++++++++++++ .../java/bisq/daemon/app/BisqDaemonMain.java | 4 +- proto/src/main/proto/grpc.proto | 62 ++------ 7 files changed, 303 insertions(+), 315 deletions(-) create mode 100644 core/src/main/java/bisq/core/grpc/CoreWalletService.java create mode 100644 core/src/main/java/bisq/core/grpc/GrpcWalletService.java diff --git a/cli/src/main/java/bisq/cli/CliMain.java b/cli/src/main/java/bisq/cli/CliMain.java index e3a340c8dca..e16c6289d7c 100644 --- a/cli/src/main/java/bisq/cli/CliMain.java +++ b/cli/src/main/java/bisq/cli/CliMain.java @@ -17,18 +17,14 @@ package bisq.cli; -import bisq.proto.grpc.GetBalanceGrpc; import bisq.proto.grpc.GetBalanceRequest; import bisq.proto.grpc.GetVersionGrpc; import bisq.proto.grpc.GetVersionRequest; -import bisq.proto.grpc.LockWalletGrpc; import bisq.proto.grpc.LockWalletRequest; -import bisq.proto.grpc.RemoveWalletPasswordGrpc; import bisq.proto.grpc.RemoveWalletPasswordRequest; -import bisq.proto.grpc.SetWalletPasswordGrpc; import bisq.proto.grpc.SetWalletPasswordRequest; -import bisq.proto.grpc.UnlockWalletGrpc; import bisq.proto.grpc.UnlockWalletRequest; +import bisq.proto.grpc.WalletGrpc; import io.grpc.ManagedChannelBuilder; import io.grpc.StatusRuntimeException; @@ -139,26 +135,26 @@ public static void main(String[] args) { } })); + var versionService = GetVersionGrpc.newBlockingStub(channel).withCallCredentials(credentials); + var walletService = WalletGrpc.newBlockingStub(channel).withCallCredentials(credentials); + try { switch (method) { case getversion: { - var stub = GetVersionGrpc.newBlockingStub(channel).withCallCredentials(credentials); var request = GetVersionRequest.newBuilder().build(); - var version = stub.getVersion(request).getVersion(); + var version = versionService.getVersion(request).getVersion(); out.println(version); exit(EXIT_SUCCESS); } case getbalance: { - var stub = GetBalanceGrpc.newBlockingStub(channel).withCallCredentials(credentials); var request = GetBalanceRequest.newBuilder().build(); - var reply = stub.getBalance(request); + var reply = walletService.getBalance(request); out.println(formatBalance(reply.getBalance())); exit(EXIT_SUCCESS); } case lockwallet: { - var stub = LockWalletGrpc.newBlockingStub(channel).withCallCredentials(credentials); var request = LockWalletRequest.newBuilder().build(); - stub.lockWallet(request); + walletService.lockWallet(request); out.println("wallet locked"); exit(EXIT_SUCCESS); } @@ -178,11 +174,10 @@ public static void main(String[] args) { err.println(nonOptionArgs.get(2) + " is not a number"); exit(EXIT_FAILURE); } - var stub = UnlockWalletGrpc.newBlockingStub(channel).withCallCredentials(credentials); var request = UnlockWalletRequest.newBuilder() .setPassword(nonOptionArgs.get(1)) .setTimeout(timeout).build(); - stub.unlockWallet(request); + walletService.unlockWallet(request); out.println("wallet unlocked"); exit(EXIT_SUCCESS); } @@ -191,9 +186,8 @@ public static void main(String[] args) { err.println("Error: no \"password\" specified"); exit(EXIT_FAILURE); } - var stub = RemoveWalletPasswordGrpc.newBlockingStub(channel).withCallCredentials(credentials); var request = RemoveWalletPasswordRequest.newBuilder().setPassword(nonOptionArgs.get(1)).build(); - stub.removeWalletPassword(request); + walletService.removeWalletPassword(request); out.println("wallet decrypted"); exit(EXIT_SUCCESS); } @@ -202,11 +196,10 @@ public static void main(String[] args) { err.println("Error: no \"password\" specified"); exit(EXIT_FAILURE); } - var stub = SetWalletPasswordGrpc.newBlockingStub(channel).withCallCredentials(credentials); var request = (nonOptionArgs.size() == 3) ? SetWalletPasswordRequest.newBuilder().setPassword(nonOptionArgs.get(1)).setNewPassword(nonOptionArgs.get(2)).build() : SetWalletPasswordRequest.newBuilder().setPassword(nonOptionArgs.get(1)).build(); - stub.setWalletPassword(request); + walletService.setWalletPassword(request); out.println("wallet encrypted" + (nonOptionArgs.size() == 2 ? "" : " with new password")); exit(EXIT_SUCCESS); } diff --git a/core/src/main/java/bisq/core/grpc/CoreApi.java b/core/src/main/java/bisq/core/grpc/CoreApi.java index ddb165b5788..a0671f4d3b0 100644 --- a/core/src/main/java/bisq/core/grpc/CoreApi.java +++ b/core/src/main/java/bisq/core/grpc/CoreApi.java @@ -17,8 +17,6 @@ package bisq.core.grpc; -import bisq.core.btc.Balances; -import bisq.core.btc.wallet.WalletsManager; import bisq.core.monetary.Price; import bisq.core.offer.CreateOfferService; import bisq.core.offer.Offer; @@ -32,59 +30,39 @@ import bisq.core.user.User; import bisq.common.app.Version; -import bisq.common.util.Tuple2; import org.bitcoinj.core.Coin; -import org.bitcoinj.crypto.KeyCrypterScrypt; import javax.inject.Inject; -import org.spongycastle.crypto.params.KeyParameter; - import java.util.ArrayList; import java.util.List; import java.util.Set; -import java.util.Timer; -import java.util.TimerTask; import lombok.extern.slf4j.Slf4j; -import javax.annotation.Nullable; - -import static bisq.core.grpc.ApiStatus.*; -import static java.util.concurrent.TimeUnit.SECONDS; - /** * Provides high level interface to functionality of core Bisq features. * E.g. useful for different APIs to access data of different domains of Bisq. */ @Slf4j public class CoreApi { - private final Balances balances; private final OfferBookService offerBookService; private final TradeStatisticsManager tradeStatisticsManager; private final CreateOfferService createOfferService; private final OpenOfferManager openOfferManager; - private final WalletsManager walletsManager; private final User user; - @Nullable - private String tempLockWalletPassword; - @Inject - public CoreApi(Balances balances, - OfferBookService offerBookService, + public CoreApi(OfferBookService offerBookService, TradeStatisticsManager tradeStatisticsManager, CreateOfferService createOfferService, OpenOfferManager openOfferManager, - WalletsManager walletsManager, User user) { - this.balances = balances; this.offerBookService = offerBookService; this.tradeStatisticsManager = tradeStatisticsManager; this.createOfferService = createOfferService; this.openOfferManager = openOfferManager; - this.walletsManager = walletsManager; this.user = user; } @@ -92,24 +70,6 @@ public String getVersion() { return Version.VERSION; } - public Tuple2 getAvailableBalance() { - if (!walletsManager.areWalletsAvailable()) - return new Tuple2<>(-1L, WALLET_NOT_AVAILABLE); - - if (walletsManager.areWalletsEncrypted()) - return new Tuple2<>(-1L, WALLET_IS_ENCRYPTED_WITH_UNLOCK_INSTRUCTION); - - try { - long balance = balances.getAvailableBalance().get().getValue(); - return new Tuple2<>(balance, OK); - } catch (Throwable t) { - // TODO Derive new ApiStatus codes from server stack traces. - t.printStackTrace(); - // TODO Fix bug causing NPE thrown by getAvailableBalance(). - return new Tuple2<>(-1L, INTERNAL); - } - } - public List getTradeStatistics() { return new ArrayList<>(tradeStatisticsManager.getObservableTradeStatisticsSet()); } @@ -185,96 +145,4 @@ public void placeOffer(String offerId, log::error); } - // Provided for automated wallet protection method testing, despite the - // security risks exposed by providing users the ability to decrypt their wallets. - public Tuple2 removeWalletPassword(String password) { - if (!walletsManager.areWalletsAvailable()) - return new Tuple2<>(false, WALLET_NOT_AVAILABLE); - - if (!walletsManager.areWalletsEncrypted()) - return new Tuple2<>(false, WALLET_NOT_ENCRYPTED); - - KeyCrypterScrypt keyCrypterScrypt = walletsManager.getKeyCrypterScrypt(); - if (keyCrypterScrypt == null) - return new Tuple2<>(false, WALLET_ENCRYPTER_NOT_AVAILABLE); - - KeyParameter aesKey = keyCrypterScrypt.deriveKey(password); - if (!walletsManager.checkAESKey(aesKey)) - return new Tuple2<>(false, INCORRECT_WALLET_PASSWORD); - - walletsManager.decryptWallets(aesKey); - return new Tuple2<>(true, OK); - } - - public Tuple2 setWalletPassword(String password, String newPassword) { - try { - if (!walletsManager.areWalletsAvailable()) - return new Tuple2<>(false, WALLET_NOT_AVAILABLE); - - KeyCrypterScrypt keyCrypterScrypt = walletsManager.getKeyCrypterScrypt(); - if (keyCrypterScrypt == null) - return new Tuple2<>(false, WALLET_ENCRYPTER_NOT_AVAILABLE); - - if (newPassword != null && !newPassword.isEmpty()) { - // TODO Validate new password before replacing old password. - if (!walletsManager.areWalletsEncrypted()) - return new Tuple2<>(false, WALLET_NOT_ENCRYPTED); - - KeyParameter aesKey = keyCrypterScrypt.deriveKey(password); - if (!walletsManager.checkAESKey(aesKey)) - return new Tuple2<>(false, INCORRECT_OLD_WALLET_PASSWORD); - - walletsManager.decryptWallets(aesKey); - aesKey = keyCrypterScrypt.deriveKey(newPassword); - walletsManager.encryptWallets(keyCrypterScrypt, aesKey); - return new Tuple2<>(true, OK); - } - - if (walletsManager.areWalletsEncrypted()) - return new Tuple2<>(false, WALLET_IS_ENCRYPTED); - - // TODO Validate new password. - KeyParameter aesKey = keyCrypterScrypt.deriveKey(password); - walletsManager.encryptWallets(keyCrypterScrypt, aesKey); - return new Tuple2<>(true, OK); - } catch (Throwable t) { - // TODO Derive new ApiStatus codes from server stack traces. - t.printStackTrace(); - return new Tuple2<>(false, INTERNAL); - } - } - - public Tuple2 lockWallet() { - if (tempLockWalletPassword != null) { - Tuple2 encrypted = setWalletPassword(tempLockWalletPassword, null); - tempLockWalletPassword = null; - if (!encrypted.second.equals(OK)) - return encrypted; - - return new Tuple2<>(true, OK); - } - return new Tuple2<>(false, WALLET_ALREADY_LOCKED); - } - - public Tuple2 unlockWallet(String password, long timeout) { - Tuple2 decrypted = removeWalletPassword(password); - if (!decrypted.second.equals(OK)) - return decrypted; - - TimerTask timerTask = new TimerTask() { - @Override - public void run() { - log.info("Locking wallet"); - setWalletPassword(password, null); - tempLockWalletPassword = null; - } - }; - Timer timer = new Timer("Lock Wallet Timer"); - timer.schedule(timerTask, SECONDS.toMillis(timeout)); - - // Cache wallet password for timeout (secs), in case - // user wants to lock the wallet for timeout expires. - tempLockWalletPassword = password; - return new Tuple2<>(true, OK); - } } diff --git a/core/src/main/java/bisq/core/grpc/CoreWalletService.java b/core/src/main/java/bisq/core/grpc/CoreWalletService.java new file mode 100644 index 00000000000..ec8c5b709a6 --- /dev/null +++ b/core/src/main/java/bisq/core/grpc/CoreWalletService.java @@ -0,0 +1,149 @@ +package bisq.core.grpc; + +import bisq.core.btc.Balances; +import bisq.core.btc.wallet.WalletsManager; + +import bisq.common.util.Tuple2; + +import org.bitcoinj.crypto.KeyCrypterScrypt; + +import javax.inject.Inject; + +import org.spongycastle.crypto.params.KeyParameter; + +import java.util.Timer; +import java.util.TimerTask; + +import lombok.extern.slf4j.Slf4j; + +import javax.annotation.Nullable; + +import static bisq.core.grpc.ApiStatus.*; +import static java.util.concurrent.TimeUnit.SECONDS; + +@Slf4j +class CoreWalletService { + + private final Balances balances; + private final WalletsManager walletsManager; + + @Nullable + private String tempLockWalletPassword; + + @Inject + public CoreWalletService(Balances balances, WalletsManager walletsManager) { + this.balances = balances; + this.walletsManager = walletsManager; + } + + public Tuple2 getAvailableBalance() { + if (!walletsManager.areWalletsAvailable()) + return new Tuple2<>(-1L, WALLET_NOT_AVAILABLE); + + if (walletsManager.areWalletsEncrypted()) + return new Tuple2<>(-1L, WALLET_IS_ENCRYPTED_WITH_UNLOCK_INSTRUCTION); + + try { + long balance = balances.getAvailableBalance().get().getValue(); + return new Tuple2<>(balance, OK); + } catch (Throwable t) { + // TODO Derive new ApiStatus codes from server stack traces. + t.printStackTrace(); + // TODO Fix bug causing NPE thrown by getAvailableBalance(). + return new Tuple2<>(-1L, INTERNAL); + } + } + + public Tuple2 setWalletPassword(String password, String newPassword) { + try { + if (!walletsManager.areWalletsAvailable()) + return new Tuple2<>(false, WALLET_NOT_AVAILABLE); + + KeyCrypterScrypt keyCrypterScrypt = walletsManager.getKeyCrypterScrypt(); + if (keyCrypterScrypt == null) + return new Tuple2<>(false, WALLET_ENCRYPTER_NOT_AVAILABLE); + + if (newPassword != null && !newPassword.isEmpty()) { + // TODO Validate new password before replacing old password. + if (!walletsManager.areWalletsEncrypted()) + return new Tuple2<>(false, WALLET_NOT_ENCRYPTED); + + KeyParameter aesKey = keyCrypterScrypt.deriveKey(password); + if (!walletsManager.checkAESKey(aesKey)) + return new Tuple2<>(false, INCORRECT_OLD_WALLET_PASSWORD); + + walletsManager.decryptWallets(aesKey); + aesKey = keyCrypterScrypt.deriveKey(newPassword); + walletsManager.encryptWallets(keyCrypterScrypt, aesKey); + return new Tuple2<>(true, OK); + } + + if (walletsManager.areWalletsEncrypted()) + return new Tuple2<>(false, WALLET_IS_ENCRYPTED); + + // TODO Validate new password. + KeyParameter aesKey = keyCrypterScrypt.deriveKey(password); + walletsManager.encryptWallets(keyCrypterScrypt, aesKey); + return new Tuple2<>(true, OK); + } catch (Throwable t) { + // TODO Derive new ApiStatus codes from server stack traces. + t.printStackTrace(); + return new Tuple2<>(false, INTERNAL); + } + } + + public Tuple2 lockWallet() { + if (tempLockWalletPassword != null) { + Tuple2 encrypted = setWalletPassword(tempLockWalletPassword, null); + tempLockWalletPassword = null; + if (!encrypted.second.equals(OK)) + return encrypted; + + return new Tuple2<>(true, OK); + } + return new Tuple2<>(false, WALLET_ALREADY_LOCKED); + } + + public Tuple2 unlockWallet(String password, long timeout) { + Tuple2 decrypted = removeWalletPassword(password); + if (!decrypted.second.equals(OK)) + return decrypted; + + TimerTask timerTask = new TimerTask() { + @Override + public void run() { + log.info("Locking wallet"); + setWalletPassword(password, null); + tempLockWalletPassword = null; + } + }; + Timer timer = new Timer("Lock Wallet Timer"); + timer.schedule(timerTask, SECONDS.toMillis(timeout)); + + // Cache wallet password for timeout (secs), in case + // user wants to lock the wallet for timeout expires. + tempLockWalletPassword = password; + return new Tuple2<>(true, OK); + } + + // Provided for automated wallet protection method testing, despite the + // security risks exposed by providing users the ability to decrypt their wallets. + public Tuple2 removeWalletPassword(String password) { + if (!walletsManager.areWalletsAvailable()) + return new Tuple2<>(false, WALLET_NOT_AVAILABLE); + + if (!walletsManager.areWalletsEncrypted()) + return new Tuple2<>(false, WALLET_NOT_ENCRYPTED); + + KeyCrypterScrypt keyCrypterScrypt = walletsManager.getKeyCrypterScrypt(); + if (keyCrypterScrypt == null) + return new Tuple2<>(false, WALLET_ENCRYPTER_NOT_AVAILABLE); + + KeyParameter aesKey = keyCrypterScrypt.deriveKey(password); + if (!walletsManager.checkAESKey(aesKey)) + return new Tuple2<>(false, INCORRECT_WALLET_PASSWORD); + + walletsManager.decryptWallets(aesKey); + return new Tuple2<>(true, OK); + } +} diff --git a/core/src/main/java/bisq/core/grpc/GrpcServer.java b/core/src/main/java/bisq/core/grpc/GrpcServer.java index 0a379e0b3aa..ad5ed0e8659 100644 --- a/core/src/main/java/bisq/core/grpc/GrpcServer.java +++ b/core/src/main/java/bisq/core/grpc/GrpcServer.java @@ -24,9 +24,6 @@ import bisq.common.config.Config; -import bisq.proto.grpc.GetBalanceGrpc; -import bisq.proto.grpc.GetBalanceReply; -import bisq.proto.grpc.GetBalanceRequest; import bisq.proto.grpc.GetOffersGrpc; import bisq.proto.grpc.GetOffersReply; import bisq.proto.grpc.GetOffersRequest; @@ -39,27 +36,18 @@ import bisq.proto.grpc.GetVersionGrpc; import bisq.proto.grpc.GetVersionReply; import bisq.proto.grpc.GetVersionRequest; -import bisq.proto.grpc.LockWalletGrpc; -import bisq.proto.grpc.LockWalletReply; -import bisq.proto.grpc.LockWalletRequest; import bisq.proto.grpc.PlaceOfferGrpc; import bisq.proto.grpc.PlaceOfferReply; import bisq.proto.grpc.PlaceOfferRequest; -import bisq.proto.grpc.RemoveWalletPasswordGrpc; -import bisq.proto.grpc.RemoveWalletPasswordReply; -import bisq.proto.grpc.RemoveWalletPasswordRequest; -import bisq.proto.grpc.SetWalletPasswordGrpc; -import bisq.proto.grpc.SetWalletPasswordReply; -import bisq.proto.grpc.SetWalletPasswordRequest; -import bisq.proto.grpc.UnlockWalletGrpc; -import bisq.proto.grpc.UnlockWalletReply; -import bisq.proto.grpc.UnlockWalletRequest; +import io.grpc.Server; import io.grpc.ServerBuilder; -import io.grpc.StatusRuntimeException; import io.grpc.stub.StreamObserver; +import javax.inject.Inject; + import java.io.IOException; +import java.io.UncheckedIOException; import java.util.stream.Collectors; @@ -69,36 +57,34 @@ public class GrpcServer { private final CoreApi coreApi; - private final int port; + private final Server server; - public GrpcServer(Config config, CoreApi coreApi) { + @Inject + public GrpcServer(Config config, CoreApi coreApi, GrpcWalletService walletService) { this.coreApi = coreApi; - this.port = config.apiPort; + this.server = ServerBuilder.forPort(config.apiPort) + .addService(new GetVersionService()) + .addService(new GetTradeStatisticsService()) + .addService(new GetOffersService()) + .addService(new GetPaymentAccountsService()) + .addService(new PlaceOfferService()) + .addService(walletService) + .intercept(new PasswordAuthInterceptor(config.apiPassword)) + .build(); + } + public void start() { try { - var server = ServerBuilder.forPort(port) - .addService(new GetVersionService()) - .addService(new GetBalanceService()) - .addService(new GetTradeStatisticsService()) - .addService(new GetOffersService()) - .addService(new GetPaymentAccountsService()) - .addService(new LockWalletService()) - .addService(new PlaceOfferService()) - .addService(new RemoveWalletPasswordService()) - .addService(new SetWalletPasswordService()) - .addService(new UnlockWalletService()) - .intercept(new PasswordAuthInterceptor(config.apiPassword)) - .build() - .start(); + server.start(); - log.info("listening on port {}", port); + log.info("listening on port {}", server.getPort()); Runtime.getRuntime().addShutdownHook(new Thread(() -> { server.shutdown(); log.info("shutdown complete"); })); - } catch (IOException e) { - log.error(e.toString(), e); + } catch (IOException ex) { + throw new UncheckedIOException(ex); } } @@ -111,21 +97,6 @@ public void getVersion(GetVersionRequest req, StreamObserver re } } - class GetBalanceService extends GetBalanceGrpc.GetBalanceImplBase { - @Override - public void getBalance(GetBalanceRequest req, StreamObserver responseObserver) { - var result = coreApi.getAvailableBalance(); - if (!result.second.equals(ApiStatus.OK)) { - StatusRuntimeException ex = new StatusRuntimeException(result.second.getGrpcStatus() - .withDescription(result.second.getDescription())); - responseObserver.onError(ex); - throw ex; - } - var reply = GetBalanceReply.newBuilder().setBalance(result.first).build(); - responseObserver.onNext(reply); - responseObserver.onCompleted(); - } - } class GetTradeStatisticsService extends GetTradeStatisticsGrpc.GetTradeStatisticsImplBase { @Override @@ -193,71 +164,4 @@ public void placeOffer(PlaceOfferRequest req, StreamObserver re } } - class RemoveWalletPasswordService extends RemoveWalletPasswordGrpc.RemoveWalletPasswordImplBase { - @Override - public void removeWalletPassword(RemoveWalletPasswordRequest req, - StreamObserver responseObserver) { - var result = coreApi.removeWalletPassword(req.getPassword()); - if (!result.second.equals(ApiStatus.OK)) { - StatusRuntimeException ex = new StatusRuntimeException(result.second.getGrpcStatus() - .withDescription(result.second.getDescription())); - responseObserver.onError(ex); - throw ex; - } - var reply = RemoveWalletPasswordReply.newBuilder().build(); - responseObserver.onNext(reply); - responseObserver.onCompleted(); - } - } - - class SetWalletPasswordService extends SetWalletPasswordGrpc.SetWalletPasswordImplBase { - @Override - public void setWalletPassword(SetWalletPasswordRequest req, - StreamObserver responseObserver) { - var result = coreApi.setWalletPassword(req.getPassword(), req.getNewPassword()); - if (!result.second.equals(ApiStatus.OK)) { - StatusRuntimeException ex = new StatusRuntimeException(result.second.getGrpcStatus() - .withDescription(result.second.getDescription())); - responseObserver.onError(ex); - throw ex; - } - var reply = SetWalletPasswordReply.newBuilder().build(); - responseObserver.onNext(reply); - responseObserver.onCompleted(); - } - } - - class LockWalletService extends LockWalletGrpc.LockWalletImplBase { - @Override - public void lockWallet(LockWalletRequest req, - StreamObserver responseObserver) { - var result = coreApi.lockWallet(); - if (!result.second.equals(ApiStatus.OK)) { - StatusRuntimeException ex = new StatusRuntimeException(result.second.getGrpcStatus() - .withDescription(result.second.getDescription())); - responseObserver.onError(ex); - throw ex; - } - var reply = LockWalletReply.newBuilder().build(); - responseObserver.onNext(reply); - responseObserver.onCompleted(); - } - } - - class UnlockWalletService extends UnlockWalletGrpc.UnlockWalletImplBase { - @Override - public void unlockWallet(UnlockWalletRequest req, - StreamObserver responseObserver) { - var result = coreApi.unlockWallet(req.getPassword(), req.getTimeout()); - if (!result.second.equals(ApiStatus.OK)) { - StatusRuntimeException ex = new StatusRuntimeException(result.second.getGrpcStatus() - .withDescription(result.second.getDescription())); - responseObserver.onError(ex); - throw ex; - } - var reply = UnlockWalletReply.newBuilder().build(); - responseObserver.onNext(reply); - responseObserver.onCompleted(); - } - } } diff --git a/core/src/main/java/bisq/core/grpc/GrpcWalletService.java b/core/src/main/java/bisq/core/grpc/GrpcWalletService.java new file mode 100644 index 00000000000..0d5ea8fd79a --- /dev/null +++ b/core/src/main/java/bisq/core/grpc/GrpcWalletService.java @@ -0,0 +1,102 @@ +package bisq.core.grpc; + +import bisq.proto.grpc.GetBalanceReply; +import bisq.proto.grpc.GetBalanceRequest; +import bisq.proto.grpc.LockWalletReply; +import bisq.proto.grpc.LockWalletRequest; +import bisq.proto.grpc.RemoveWalletPasswordReply; +import bisq.proto.grpc.RemoveWalletPasswordRequest; +import bisq.proto.grpc.SetWalletPasswordReply; +import bisq.proto.grpc.SetWalletPasswordRequest; +import bisq.proto.grpc.UnlockWalletReply; +import bisq.proto.grpc.UnlockWalletRequest; +import bisq.proto.grpc.WalletGrpc; + +import io.grpc.StatusRuntimeException; +import io.grpc.stub.StreamObserver; + +import javax.inject.Inject; + +class GrpcWalletService extends WalletGrpc.WalletImplBase { + + private final CoreWalletService walletService; + + @Inject + public GrpcWalletService(CoreWalletService walletService) { + this.walletService = walletService; + } + + @Override + public void getBalance(GetBalanceRequest req, StreamObserver responseObserver) { + var result = walletService.getAvailableBalance(); + if (!result.second.equals(ApiStatus.OK)) { + StatusRuntimeException ex = new StatusRuntimeException(result.second.getGrpcStatus() + .withDescription(result.second.getDescription())); + responseObserver.onError(ex); + throw ex; + } + var reply = GetBalanceReply.newBuilder().setBalance(result.first).build(); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } + + @Override + public void setWalletPassword(SetWalletPasswordRequest req, + StreamObserver responseObserver) { + var result = walletService.setWalletPassword(req.getPassword(), req.getNewPassword()); + if (!result.second.equals(ApiStatus.OK)) { + StatusRuntimeException ex = new StatusRuntimeException(result.second.getGrpcStatus() + .withDescription(result.second.getDescription())); + responseObserver.onError(ex); + throw ex; + } + var reply = SetWalletPasswordReply.newBuilder().build(); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } + + @Override + public void removeWalletPassword(RemoveWalletPasswordRequest req, + StreamObserver responseObserver) { + var result = walletService.removeWalletPassword(req.getPassword()); + if (!result.second.equals(ApiStatus.OK)) { + StatusRuntimeException ex = new StatusRuntimeException(result.second.getGrpcStatus() + .withDescription(result.second.getDescription())); + responseObserver.onError(ex); + throw ex; + } + var reply = RemoveWalletPasswordReply.newBuilder().build(); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } + + @Override + public void lockWallet(LockWalletRequest req, + StreamObserver responseObserver) { + var result = walletService.lockWallet(); + if (!result.second.equals(ApiStatus.OK)) { + StatusRuntimeException ex = new StatusRuntimeException(result.second.getGrpcStatus() + .withDescription(result.second.getDescription())); + responseObserver.onError(ex); + throw ex; + } + var reply = LockWalletReply.newBuilder().build(); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } + + @Override + public void unlockWallet(UnlockWalletRequest req, + StreamObserver responseObserver) { + var result = walletService.unlockWallet(req.getPassword(), req.getTimeout()); + if (!result.second.equals(ApiStatus.OK)) { + StatusRuntimeException ex = new StatusRuntimeException(result.second.getGrpcStatus() + .withDescription(result.second.getDescription())); + responseObserver.onError(ex); + throw ex; + } + var reply = UnlockWalletReply.newBuilder().build(); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } +} diff --git a/daemon/src/main/java/bisq/daemon/app/BisqDaemonMain.java b/daemon/src/main/java/bisq/daemon/app/BisqDaemonMain.java index 698f8ebe92d..36dbd4a351b 100644 --- a/daemon/src/main/java/bisq/daemon/app/BisqDaemonMain.java +++ b/daemon/src/main/java/bisq/daemon/app/BisqDaemonMain.java @@ -98,7 +98,7 @@ protected void startApplication() { protected void onApplicationStarted() { super.onApplicationStarted(); - CoreApi coreApi = injector.getInstance(CoreApi.class); - new GrpcServer(config, coreApi); + GrpcServer grpcServer = injector.getInstance(GrpcServer.class); + grpcServer.start(); } } diff --git a/proto/src/main/proto/grpc.proto b/proto/src/main/proto/grpc.proto index d7dbf1df327..b8db4c6d24b 100644 --- a/proto/src/main/proto/grpc.proto +++ b/proto/src/main/proto/grpc.proto @@ -39,22 +39,6 @@ message GetVersionReply { string version = 1; } -/////////////////////////////////////////////////////////////////////////////////////////// -// Balance -/////////////////////////////////////////////////////////////////////////////////////////// - -service GetBalance { - rpc GetBalance (GetBalanceRequest) returns (GetBalanceReply) { - } -} - -message GetBalanceRequest { -} - -message GetBalanceReply { - uint64 balance = 1; -} - /////////////////////////////////////////////////////////////////////////////////////////// // TradeStatistics /////////////////////////////////////////////////////////////////////////////////////////// @@ -129,28 +113,27 @@ message PlaceOfferReply { } /////////////////////////////////////////////////////////////////////////////////////////// -// RemoveWalletPassword +// Wallet /////////////////////////////////////////////////////////////////////////////////////////// -service RemoveWalletPassword { +service Wallet { + rpc GetBalance (GetBalanceRequest) returns (GetBalanceReply) { + } + rpc SetWalletPassword (SetWalletPasswordRequest) returns (SetWalletPasswordReply) { + } rpc RemoveWalletPassword (RemoveWalletPasswordRequest) returns (RemoveWalletPasswordReply) { } + rpc LockWallet (LockWalletRequest) returns (LockWalletReply) { + } + rpc UnlockWallet (UnlockWalletRequest) returns (UnlockWalletReply) { + } } -message RemoveWalletPasswordRequest { - string password = 1; -} - -message RemoveWalletPasswordReply { +message GetBalanceRequest { } -/////////////////////////////////////////////////////////////////////////////////////////// -// SetWalletPassword -/////////////////////////////////////////////////////////////////////////////////////////// - -service SetWalletPassword { - rpc SetWalletPassword (SetWalletPasswordRequest) returns (SetWalletPasswordReply) { - } +message GetBalanceReply { + uint64 balance = 1; } message SetWalletPasswordRequest { @@ -161,13 +144,11 @@ message SetWalletPasswordRequest { message SetWalletPasswordReply { } -/////////////////////////////////////////////////////////////////////////////////////////// -// LockWallet -/////////////////////////////////////////////////////////////////////////////////////////// +message RemoveWalletPasswordRequest { + string password = 1; +} -service LockWallet { - rpc LockWallet (LockWalletRequest) returns (LockWalletReply) { - } +message RemoveWalletPasswordReply { } message LockWalletRequest { @@ -176,15 +157,6 @@ message LockWalletRequest { message LockWalletReply { } -/////////////////////////////////////////////////////////////////////////////////////////// -// UnlockWallet -/////////////////////////////////////////////////////////////////////////////////////////// - -service UnlockWallet { - rpc UnlockWallet (UnlockWalletRequest) returns (UnlockWalletReply) { - } -} - message UnlockWalletRequest { string password = 1; uint64 timeout = 2;