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.
This commit is contained in:
Chris Beams 2020-05-02 11:21:18 +02:00
parent 2a9d1f6d34
commit c7a6c87bd1
No known key found for this signature in database
GPG Key ID: 3D214F8F5BC5ED73
7 changed files with 306 additions and 321 deletions

View File

@ -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 class CliMain {
}
}));
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 class CliMain {
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 class CliMain {
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 class CliMain {
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);
}

View File

@ -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.trade.statistics.TradeStatisticsManager;
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 class CoreApi {
return Version.VERSION;
}
public Tuple2<Long, ApiStatus> 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<TradeStatistics2> getTradeStatistics() {
return new ArrayList<>(tradeStatisticsManager.getObservableTradeStatisticsSet());
}
@ -185,96 +145,4 @@ public class CoreApi {
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<Boolean, ApiStatus> 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<Boolean, ApiStatus> 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<Boolean, ApiStatus> lockWallet() {
if (tempLockWalletPassword != null) {
Tuple2<Boolean, ApiStatus> 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<Boolean, ApiStatus> unlockWallet(String password, long timeout) {
Tuple2<Boolean, ApiStatus> 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);
}
}

View File

@ -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<Long, ApiStatus> 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<Boolean, ApiStatus> 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<Boolean, ApiStatus> lockWallet() {
if (tempLockWalletPassword != null) {
Tuple2<Boolean, ApiStatus> 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<Boolean, ApiStatus> unlockWallet(String password, long timeout) {
Tuple2<Boolean, ApiStatus> 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<Boolean, ApiStatus> 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);
}
}

View File

@ -24,9 +24,6 @@ import bisq.core.trade.statistics.TradeStatistics2;
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.GetTradeStatisticsRequest;
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,32 @@ import lombok.extern.slf4j.Slf4j;
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();
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);
}
}
@ -111,21 +95,6 @@ public class GrpcServer {
}
}
class GetBalanceService extends GetBalanceGrpc.GetBalanceImplBase {
@Override
public void getBalance(GetBalanceRequest req, StreamObserver<GetBalanceReply> 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
@ -192,72 +161,4 @@ public class GrpcServer {
resultHandler);
}
}
class RemoveWalletPasswordService extends RemoveWalletPasswordGrpc.RemoveWalletPasswordImplBase {
@Override
public void removeWalletPassword(RemoveWalletPasswordRequest req,
StreamObserver<RemoveWalletPasswordReply> 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<SetWalletPasswordReply> 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<LockWalletReply> 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<UnlockWalletReply> 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();
}
}
}

View File

@ -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<GetBalanceReply> 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<SetWalletPasswordReply> 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<RemoveWalletPasswordReply> 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<LockWalletReply> 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<UnlockWalletReply> 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();
}
}

View File

@ -98,7 +98,7 @@ public class BisqDaemonMain extends BisqHeadlessAppMain implements BisqSetup.Bis
protected void onApplicationStarted() {
super.onApplicationStarted();
CoreApi coreApi = injector.getInstance(CoreApi.class);
new GrpcServer(config, coreApi);
GrpcServer grpcServer = injector.getInstance(GrpcServer.class);
grpcServer.start();
}
}

View File

@ -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 {
rpc RemoveWalletPassword (RemoveWalletPasswordRequest) returns (RemoveWalletPasswordReply) {
service Wallet {
rpc GetBalance (GetBalanceRequest) returns (GetBalanceReply) {
}
}
message RemoveWalletPasswordRequest {
string password = 1;
}
message RemoveWalletPasswordReply {
}
///////////////////////////////////////////////////////////////////////////////////////////
// SetWalletPassword
///////////////////////////////////////////////////////////////////////////////////////////
service SetWalletPassword {
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 {
@ -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;