diff --git a/cli/src/main/java/bisq/cli/CliMain.java b/cli/src/main/java/bisq/cli/CliMain.java index 27f40b68b6c..e5463e11cfb 100644 --- a/cli/src/main/java/bisq/cli/CliMain.java +++ b/cli/src/main/java/bisq/cli/CliMain.java @@ -17,15 +17,18 @@ 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.LockWalletRequest; +import bisq.proto.grpc.RemoveWalletPasswordRequest; +import bisq.proto.grpc.SetWalletPasswordRequest; +import bisq.proto.grpc.UnlockWalletRequest; +import bisq.proto.grpc.WalletGrpc; import io.grpc.ManagedChannelBuilder; import io.grpc.StatusRuntimeException; -import joptsimple.OptionException; import joptsimple.OptionParser; import joptsimple.OptionSet; @@ -41,6 +44,7 @@ import lombok.extern.slf4j.Slf4j; +import static java.lang.String.format; import static java.lang.System.err; import static java.lang.System.exit; import static java.lang.System.out; @@ -51,15 +55,25 @@ @Slf4j public class CliMain { - private static final int EXIT_SUCCESS = 0; - private static final int EXIT_FAILURE = 1; - private enum Method { getversion, - getbalance + getbalance, + lockwallet, + unlockwallet, + removewalletpassword, + setwalletpassword } public static void main(String[] args) { + try { + run(args); + } catch (Throwable t) { + err.println("Error: " + t.getMessage()); + exit(1); + } + } + + public static void run(String[] args) { var parser = new OptionParser(); var helpOpt = parser.accepts("help", "Print this help text") @@ -77,43 +91,33 @@ public static void main(String[] args) { var passwordOpt = parser.accepts("password", "rpc server password") .withRequiredArg(); - OptionSet options = null; - try { - options = parser.parse(args); - } catch (OptionException ex) { - err.println("Error: " + ex.getMessage()); - exit(EXIT_FAILURE); - } + OptionSet options = parser.parse(args); if (options.has(helpOpt)) { printHelp(parser, out); - exit(EXIT_SUCCESS); + return; } @SuppressWarnings("unchecked") var nonOptionArgs = (List) options.nonOptionArguments(); if (nonOptionArgs.isEmpty()) { printHelp(parser, err); - err.println("Error: no method specified"); - exit(EXIT_FAILURE); + throw new IllegalArgumentException("no method specified"); } var methodName = nonOptionArgs.get(0); - Method method = null; + final Method method; try { method = Method.valueOf(methodName); } catch (IllegalArgumentException ex) { - err.printf("Error: '%s' is not a supported method\n", methodName); - exit(EXIT_FAILURE); + throw new IllegalArgumentException(format("'%s' is not a supported method", methodName)); } var host = options.valueOf(hostOpt); var port = options.valueOf(portOpt); var password = options.valueOf(passwordOpt); - if (password == null) { - err.println("Error: missing required 'password' option"); - exit(EXIT_FAILURE); - } + if (password == null) + throw new IllegalArgumentException("missing required 'password' option"); var credentials = new PasswordCallCredentials(password); @@ -122,43 +126,87 @@ public static void main(String[] args) { try { channel.shutdown().awaitTermination(1, TimeUnit.SECONDS); } catch (InterruptedException ex) { - ex.printStackTrace(err); - exit(EXIT_FAILURE); + throw new RuntimeException(ex); } })); + 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); + return; } case getbalance: { - var stub = GetBalanceGrpc.newBlockingStub(channel).withCallCredentials(credentials); var request = GetBalanceRequest.newBuilder().build(); - var balance = stub.getBalance(request).getBalance(); - if (balance == -1) { - err.println("Error: server is still initializing"); - exit(EXIT_FAILURE); + var reply = walletService.getBalance(request); + var satoshiBalance = reply.getBalance(); + var satoshiDivisor = new BigDecimal(100000000); + var btcFormat = new DecimalFormat("###,##0.00000000"); + @SuppressWarnings("BigDecimalMethodWithoutRoundingCalled") + var btcBalance = btcFormat.format(BigDecimal.valueOf(satoshiBalance).divide(satoshiDivisor)); + out.println(btcBalance); + return; + } + case lockwallet: { + var request = LockWalletRequest.newBuilder().build(); + walletService.lockWallet(request); + out.println("wallet locked"); + return; + } + case unlockwallet: { + if (nonOptionArgs.size() < 2) + throw new IllegalArgumentException("no password specified"); + + if (nonOptionArgs.size() < 3) + throw new IllegalArgumentException("no unlock timeout specified"); + + long timeout; + try { + timeout = Long.parseLong(nonOptionArgs.get(2)); + } catch (NumberFormatException e) { + throw new IllegalArgumentException(format("'%s' is not a number", nonOptionArgs.get(2))); } - out.println(formatBalance(balance)); - exit(EXIT_SUCCESS); + var request = UnlockWalletRequest.newBuilder() + .setPassword(nonOptionArgs.get(1)) + .setTimeout(timeout).build(); + walletService.unlockWallet(request); + out.println("wallet unlocked"); + return; } - default: { - err.printf("Error: unhandled method '%s'\n", method); - exit(EXIT_FAILURE); + case removewalletpassword: { + if (nonOptionArgs.size() < 2) + throw new IllegalArgumentException("no password specified"); + + var request = RemoveWalletPasswordRequest.newBuilder().setPassword(nonOptionArgs.get(1)).build(); + walletService.removeWalletPassword(request); + out.println("wallet decrypted"); + return; } + case setwalletpassword: { + if (nonOptionArgs.size() < 2) + throw new IllegalArgumentException("no password specified"); + + var requestBuilder = SetWalletPasswordRequest.newBuilder().setPassword(nonOptionArgs.get(1)); + var hasNewPassword = nonOptionArgs.size() == 3; + if (hasNewPassword) + requestBuilder.setNewPassword(nonOptionArgs.get(2)); + walletService.setWalletPassword(requestBuilder.build()); + out.println("wallet encrypted" + (hasNewPassword ? " with new password" : "")); + return; + } + default: { + throw new RuntimeException(format("unhandled method '%s'", method)); + } } } catch (StatusRuntimeException ex) { - // This exception is thrown if the client-provided password credentials do not - // match the value set on the server. The actual error message is in a nested - // exception and we clean it up a bit to make it more presentable. - Throwable t = ex.getCause() == null ? ex : ex.getCause(); - err.println("Error: " + t.getMessage().replace("UNAUTHENTICATED: ", "")); - exit(EXIT_FAILURE); + // Remove the leading gRPC status code (e.g. "UNKNOWN: ") from the message + String message = ex.getMessage().replaceFirst("^[A-Z_]+: ", ""); + throw new RuntimeException(message, ex); } } @@ -166,24 +214,22 @@ private static void printHelp(OptionParser parser, PrintStream stream) { try { stream.println("Bisq RPC Client"); stream.println(); - stream.println("Usage: bisq-cli [options] "); + stream.println("Usage: bisq-cli [options] [params]"); stream.println(); parser.printHelpOn(stream); stream.println(); - stream.println("Method Description"); - stream.println("------ -----------"); - stream.println("getversion Get server version"); - stream.println("getbalance Get server wallet balance"); + stream.format("%-19s%-30s%s%n", "Method", "Params", "Description"); + stream.format("%-19s%-30s%s%n", "------", "------", "------------"); + stream.format("%-19s%-30s%s%n", "getversion", "", "Get server version"); + stream.format("%-19s%-30s%s%n", "getbalance", "", "Get server wallet balance"); + stream.format("%-19s%-30s%s%n", "lockwallet", "", "Remove wallet password from memory, locking the wallet"); + stream.format("%-19s%-30s%s%n", "unlockwallet", "password timeout", + "Store wallet password in memory for timeout seconds"); + stream.format("%-19s%-30s%s%n", "setwalletpassword", "password [newpassword]", + "Encrypt wallet with password, or set new password on encrypted wallet"); stream.println(); } catch (IOException ex) { ex.printStackTrace(stream); } } - - @SuppressWarnings("BigDecimalMethodWithoutRoundingCalled") - private static String formatBalance(long satoshis) { - var btcFormat = new DecimalFormat("###,##0.00000000"); - var satoshiDivisor = new BigDecimal(100000000); - return btcFormat.format(BigDecimal.valueOf(satoshis).divide(satoshiDivisor)); - } } diff --git a/core/src/main/java/bisq/core/grpc/CoreApi.java b/core/src/main/java/bisq/core/grpc/CoreApi.java index 2877849147f..a0671f4d3b0 100644 --- a/core/src/main/java/bisq/core/grpc/CoreApi.java +++ b/core/src/main/java/bisq/core/grpc/CoreApi.java @@ -17,7 +17,6 @@ package bisq.core.grpc; -import bisq.core.btc.Balances; import bisq.core.monetary.Price; import bisq.core.offer.CreateOfferService; import bisq.core.offer.Offer; @@ -25,7 +24,6 @@ import bisq.core.offer.OfferPayload; import bisq.core.offer.OpenOfferManager; import bisq.core.payment.PaymentAccount; -import bisq.core.presentation.BalancePresentation; import bisq.core.trade.handlers.TransactionResultHandler; import bisq.core.trade.statistics.TradeStatistics2; import bisq.core.trade.statistics.TradeStatisticsManager; @@ -49,8 +47,6 @@ */ @Slf4j public class CoreApi { - private final Balances balances; - private final BalancePresentation balancePresentation; private final OfferBookService offerBookService; private final TradeStatisticsManager tradeStatisticsManager; private final CreateOfferService createOfferService; @@ -58,15 +54,11 @@ public class CoreApi { private final User user; @Inject - public CoreApi(Balances balances, - BalancePresentation balancePresentation, - OfferBookService offerBookService, + public CoreApi(OfferBookService offerBookService, TradeStatisticsManager tradeStatisticsManager, CreateOfferService createOfferService, OpenOfferManager openOfferManager, User user) { - this.balances = balances; - this.balancePresentation = balancePresentation; this.offerBookService = offerBookService; this.tradeStatisticsManager = tradeStatisticsManager; this.createOfferService = createOfferService; @@ -78,14 +70,6 @@ public String getVersion() { return Version.VERSION; } - public long getAvailableBalance() { - return balances.getAvailableBalance().get().getValue(); - } - - public String getAvailableBalanceAsString() { - return balancePresentation.getAvailableBalance().get(); - } - public List getTradeStatistics() { return new ArrayList<>(tradeStatisticsManager.getObservableTradeStatisticsSet()); } @@ -160,4 +144,5 @@ public void placeOffer(String offerId, resultHandler, log::error); } + } 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..ff9383c55d4 --- /dev/null +++ b/core/src/main/java/bisq/core/grpc/CoreWalletService.java @@ -0,0 +1,159 @@ +package bisq.core.grpc; + +import bisq.core.btc.Balances; +import bisq.core.btc.wallet.WalletsManager; + +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 java.util.concurrent.TimeUnit.SECONDS; + +@Slf4j +class CoreWalletService { + + private final Balances balances; + private final WalletsManager walletsManager; + + @Nullable + private TimerTask lockTask; + + @Nullable + private KeyParameter tempAesKey; + + @Inject + public CoreWalletService(Balances balances, WalletsManager walletsManager) { + this.balances = balances; + this.walletsManager = walletsManager; + } + + public long getAvailableBalance() { + if (!walletsManager.areWalletsAvailable()) + throw new IllegalStateException("wallet is not yet available"); + + if (walletsManager.areWalletsEncrypted() && tempAesKey == null) + throw new IllegalStateException("wallet is locked"); + + var balance = balances.getAvailableBalance().get(); + if (balance == null) + throw new IllegalStateException("balance is not yet available"); + + return balance.getValue(); + } + + public void setWalletPassword(String password, String newPassword) { + if (!walletsManager.areWalletsAvailable()) + throw new IllegalStateException("wallet is not yet available"); + + KeyCrypterScrypt keyCrypterScrypt = getKeyCrypterScrypt(); + + if (newPassword != null && !newPassword.isEmpty()) { + // TODO Validate new password before replacing old password. + if (!walletsManager.areWalletsEncrypted()) + throw new IllegalStateException("wallet is not encrypted with a password"); + + KeyParameter aesKey = keyCrypterScrypt.deriveKey(password); + if (!walletsManager.checkAESKey(aesKey)) + throw new IllegalStateException("incorrect old password"); + + walletsManager.decryptWallets(aesKey); + aesKey = keyCrypterScrypt.deriveKey(newPassword); + walletsManager.encryptWallets(keyCrypterScrypt, aesKey); + walletsManager.backupWallets(); + return; + } + + if (walletsManager.areWalletsEncrypted()) + throw new IllegalStateException("wallet is encrypted with a password"); + + // TODO Validate new password. + KeyParameter aesKey = keyCrypterScrypt.deriveKey(password); + walletsManager.encryptWallets(keyCrypterScrypt, aesKey); + walletsManager.backupWallets(); + } + + public void lockWallet() { + if (!walletsManager.areWalletsEncrypted()) + throw new IllegalStateException("wallet is not encrypted with a password"); + + if (tempAesKey == null) + throw new IllegalStateException("wallet is already locked"); + + tempAesKey = null; + } + + public void unlockWallet(String password, long timeout) { + verifyWalletIsAvailableAndEncrypted(); + + KeyCrypterScrypt keyCrypterScrypt = getKeyCrypterScrypt(); + // The aesKey is also cached for timeout (secs) after being used to decrypt the + // wallet, in case the user wants to manually lock the wallet before the timeout. + tempAesKey = keyCrypterScrypt.deriveKey(password); + + if (!walletsManager.checkAESKey(tempAesKey)) + throw new IllegalStateException("incorrect password"); + + if (lockTask != null) { + // The user is overriding a prior unlock timeout. Cancel the existing + // lock TimerTask to prevent it from calling lockWallet() before or after the + // new timer task does. + lockTask.cancel(); + // Avoid the synchronized(lock) overhead of an unnecessary lockTask.cancel() + // call the next time 'unlockwallet' is called. + lockTask = null; + } + + lockTask = new TimerTask() { + @Override + public void run() { + if (tempAesKey != null) { + // Do not try to lock wallet after timeout if the user has already + // done so via 'lockwallet' + log.info("Locking wallet after {} second timeout expired.", timeout); + tempAesKey = null; + } + } + }; + Timer timer = new Timer("Lock Wallet Timer"); + timer.schedule(lockTask, SECONDS.toMillis(timeout)); + } + + // Provided for automated wallet protection method testing, despite the + // security risks exposed by providing users the ability to decrypt their wallets. + public void removeWalletPassword(String password) { + verifyWalletIsAvailableAndEncrypted(); + KeyCrypterScrypt keyCrypterScrypt = getKeyCrypterScrypt(); + + KeyParameter aesKey = keyCrypterScrypt.deriveKey(password); + if (!walletsManager.checkAESKey(aesKey)) + throw new IllegalStateException("incorrect password"); + + walletsManager.decryptWallets(aesKey); + walletsManager.backupWallets(); + } + + // Throws a RuntimeException if wallets are not available or not encrypted. + private void verifyWalletIsAvailableAndEncrypted() { + if (!walletsManager.areWalletsAvailable()) + throw new IllegalStateException("wallet is not yet available"); + + if (!walletsManager.areWalletsEncrypted()) + throw new IllegalStateException("wallet is not encrypted with a password"); + } + + private KeyCrypterScrypt getKeyCrypterScrypt() { + KeyCrypterScrypt keyCrypterScrypt = walletsManager.getKeyCrypterScrypt(); + if (keyCrypterScrypt == null) + throw new IllegalStateException("wallet encrypter is not available"); + return keyCrypterScrypt; + } +} diff --git a/core/src/main/java/bisq/core/grpc/GrpcServer.java b/core/src/main/java/bisq/core/grpc/GrpcServer.java index 0ae8ae9ac85..2b3543572b1 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; @@ -43,10 +40,14 @@ import bisq.proto.grpc.PlaceOfferReply; import bisq.proto.grpc.PlaceOfferRequest; +import io.grpc.Server; import io.grpc.ServerBuilder; import io.grpc.stub.StreamObserver; +import javax.inject.Inject; + import java.io.IOException; +import java.io.UncheckedIOException; import java.util.stream.Collectors; @@ -56,32 +57,32 @@ 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 PlaceOfferService()) - .intercept(new PasswordAuthInterceptor(config.apiPassword)) - .build() - .start(); - - log.info("listening on port {}", port); + server.start(); + 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); } } @@ -94,14 +95,6 @@ public void getVersion(GetVersionRequest req, StreamObserver re } } - class GetBalanceService extends GetBalanceGrpc.GetBalanceImplBase { - @Override - public void getBalance(GetBalanceRequest req, StreamObserver responseObserver) { - var reply = GetBalanceReply.newBuilder().setBalance(coreApi.getAvailableBalance()).build(); - responseObserver.onNext(reply); - responseObserver.onCompleted(); - } - } class GetTradeStatisticsService extends GetTradeStatisticsGrpc.GetTradeStatisticsImplBase { @Override 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..92d4cc8b81f --- /dev/null +++ b/core/src/main/java/bisq/core/grpc/GrpcWalletService.java @@ -0,0 +1,103 @@ +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.Status; +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) { + try { + long result = walletService.getAvailableBalance(); + var reply = GetBalanceReply.newBuilder().setBalance(result).build(); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } catch (IllegalStateException cause) { + var ex = new StatusRuntimeException(Status.UNKNOWN.withDescription(cause.getMessage())); + responseObserver.onError(ex); + throw ex; + } + } + + @Override + public void setWalletPassword(SetWalletPasswordRequest req, + StreamObserver responseObserver) { + try { + walletService.setWalletPassword(req.getPassword(), req.getNewPassword()); + var reply = SetWalletPasswordReply.newBuilder().build(); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } catch (IllegalStateException cause) { + var ex = new StatusRuntimeException(Status.UNKNOWN.withDescription(cause.getMessage())); + responseObserver.onError(ex); + throw ex; + } + } + + @Override + public void removeWalletPassword(RemoveWalletPasswordRequest req, + StreamObserver responseObserver) { + try { + walletService.removeWalletPassword(req.getPassword()); + var reply = RemoveWalletPasswordReply.newBuilder().build(); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } catch (IllegalStateException cause) { + var ex = new StatusRuntimeException(Status.UNKNOWN.withDescription(cause.getMessage())); + responseObserver.onError(ex); + throw ex; + } + } + + @Override + public void lockWallet(LockWalletRequest req, + StreamObserver responseObserver) { + try { + walletService.lockWallet(); + var reply = LockWalletReply.newBuilder().build(); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } catch (IllegalStateException cause) { + var ex = new StatusRuntimeException(Status.UNKNOWN.withDescription(cause.getMessage())); + responseObserver.onError(ex); + throw ex; + } + } + + @Override + public void unlockWallet(UnlockWalletRequest req, + StreamObserver responseObserver) { + try { + walletService.unlockWallet(req.getPassword(), req.getTimeout()); + var reply = UnlockWalletReply.newBuilder().build(); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } catch (IllegalStateException cause) { + var ex = new StatusRuntimeException(Status.UNKNOWN.withDescription(cause.getMessage())); + responseObserver.onError(ex); + throw ex; + } + } +} diff --git a/daemon/src/main/java/bisq/daemon/app/BisqDaemonMain.java b/daemon/src/main/java/bisq/daemon/app/BisqDaemonMain.java index 698f8ebe92d..64b5bcfc331 100644 --- a/daemon/src/main/java/bisq/daemon/app/BisqDaemonMain.java +++ b/daemon/src/main/java/bisq/daemon/app/BisqDaemonMain.java @@ -21,7 +21,6 @@ import bisq.core.app.BisqSetup; import bisq.core.app.CoreModule; import bisq.core.grpc.GrpcServer; -import bisq.core.grpc.CoreApi; import bisq.common.UserThread; import bisq.common.app.AppModule; @@ -98,7 +97,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 b1e7d084a65..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 /////////////////////////////////////////////////////////////////////////////////////////// @@ -127,3 +111,56 @@ message PlaceOfferRequest { message PlaceOfferReply { bool result = 1; } + +/////////////////////////////////////////////////////////////////////////////////////////// +// Wallet +/////////////////////////////////////////////////////////////////////////////////////////// + +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 GetBalanceRequest { +} + +message GetBalanceReply { + uint64 balance = 1; +} + +message SetWalletPasswordRequest { + string password = 1; + string newPassword = 2; +} + +message SetWalletPasswordReply { +} + +message RemoveWalletPasswordRequest { + string password = 1; +} + +message RemoveWalletPasswordReply { +} + +message LockWalletRequest { +} + +message LockWalletReply { +} + +message UnlockWalletRequest { + string password = 1; + uint64 timeout = 2; +} + +message UnlockWalletReply { +}