mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 01:41:11 +01:00
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:
parent
2a9d1f6d34
commit
c7a6c87bd1
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
149
core/src/main/java/bisq/core/grpc/CoreWalletService.java
Normal file
149
core/src/main/java/bisq/core/grpc/CoreWalletService.java
Normal 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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
102
core/src/main/java/bisq/core/grpc/GrpcWalletService.java
Normal file
102
core/src/main/java/bisq/core/grpc/GrpcWalletService.java
Normal 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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user