diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index d52f38b3e8f..65ad21a8f9d 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -33,7 +33,7 @@
-
+
diff --git a/build.gradle b/build.gradle
index 2e2341b2cee..d361cd0da22 100644
--- a/build.gradle
+++ b/build.gradle
@@ -38,6 +38,7 @@ configure(subprojects) {
fontawesomefxVersion = '8.0.0'
fontawesomefxCommonsVersion = '9.1.2'
fontawesomefxMaterialdesignfontVersion = '2.0.26-9.1.2'
+ grpcVersion = '1.25.0'
guavaVersion = '20.0'
guiceVersion = '4.2.2'
hamcrestVersion = '1.3'
@@ -59,7 +60,8 @@ configure(subprojects) {
lombokVersion = '1.18.2'
mockitoVersion = '3.0.0'
netlayerVersion = '0.6.5.2'
- protobufVersion = '3.9.1'
+ protobufVersion = '3.10.0'
+ protocVersion = protobufVersion
pushyVersion = '0.13.2'
qrgenVersion = '1.3'
sarxosVersion = '0.3.12'
@@ -86,7 +88,9 @@ configure(subprojects) {
}
-configure([project(':desktop'),
+configure([project(':cli'),
+ project(':daemon'),
+ project(':desktop'),
project(':monitor'),
project(':relay'),
project(':seednode'),
@@ -166,7 +170,7 @@ configure(project(':common')) {
protobuf {
protoc {
- artifact = "com.google.protobuf:protoc:$protobufVersion"
+ artifact = "com.google.protobuf:protoc:$protocVersion"
}
}
@@ -175,7 +179,7 @@ configure(project(':common')) {
compile "org.openjfx:javafx-base:$javafxVersion:$os"
compile "org.openjfx:javafx-graphics:$javafxVersion:$os"
compile "com.google.protobuf:protobuf-java:$protobufVersion"
- compile 'com.google.code.gson:gson:2.7'
+ compile 'com.google.code.gson:gson:2.8.5'
compile "org.springframework:spring-core:$springVersion"
compile "org.slf4j:slf4j-api:$slf4jVersion"
compile "ch.qos.logback:logback-core:$logbackVersion"
@@ -225,6 +229,8 @@ configure(project(':p2p')) {
configure(project(':core')) {
+ apply plugin: 'com.google.protobuf'
+
dependencies {
compile project(':assets')
compile project(':p2p')
@@ -250,7 +256,20 @@ configure(project(':core')) {
compile("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion") {
exclude(module: 'jackson-annotations')
}
-
+ implementation "com.google.protobuf:protobuf-java:$protobufVersion"
+ implementation("io.grpc:grpc-protobuf:$grpcVersion") {
+ exclude(module: 'guava')
+ exclude(module: 'animal-sniffer-annotations')
+ }
+ implementation("io.grpc:grpc-stub:$grpcVersion") {
+ exclude(module: 'guava')
+ exclude(module: 'animal-sniffer-annotations')
+ }
+ compileOnly "javax.annotation:javax.annotation-api:1.2"
+ runtimeOnly ("io.grpc:grpc-netty-shaded:$grpcVersion") {
+ exclude(module: 'guava')
+ exclude(module: 'animal-sniffer-annotations')
+ }
compileOnly "org.projectlombok:lombok:$lombokVersion"
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
@@ -262,11 +281,47 @@ configure(project(':core')) {
testAnnotationProcessor "org.projectlombok:lombok:$lombokVersion"
}
+ protobuf {
+ protoc {
+ artifact = "com.google.protobuf:protoc:${protocVersion}"
+ }
+ plugins {
+ grpc {
+ artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}"
+ }
+ }
+ generateProtoTasks {
+ all()*.plugins { grpc {} }
+ }
+ }
+
+ sourceSets.main.java.srcDirs += [
+ 'build/generated/source/proto/main/grpc',
+ 'build/generated/source/proto/main/java'
+ ]
+
test {
systemProperty 'jdk.attach.allowAttachSelf', true
}
}
+configure(project(':cli')) {
+ mainClassName = 'bisq.cli.app.BisqCliMain'
+
+ dependencies {
+ compile project(':core')
+ implementation("io.grpc:grpc-core:$grpcVersion") {
+ exclude(module: 'guava')
+ exclude(module: 'animal-sniffer-annotations')
+ }
+ implementation("io.grpc:grpc-stub:$grpcVersion") {
+ exclude(module: 'guava')
+ exclude(module: 'animal-sniffer-annotations')
+ }
+ compileOnly "org.projectlombok:lombok:$lombokVersion"
+ annotationProcessor "org.projectlombok:lombok:$lombokVersion"
+ }
+}
configure(project(':desktop')) {
apply plugin: 'com.github.johnrengelman.shadow'
@@ -402,3 +457,14 @@ configure(project(':statsnode')) {
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
}
}
+
+configure(project(':daemon')) {
+ mainClassName = 'bisq.daemon.app.BisqDaemonMain'
+
+ dependencies {
+ compile project(':core')
+ compileOnly "org.projectlombok:lombok:$lombokVersion"
+ compileOnly "javax.annotation:javax.annotation-api:1.2"
+ annotationProcessor "org.projectlombok:lombok:$lombokVersion"
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/app/BisqCliMain.java b/cli/src/main/java/bisq/cli/app/BisqCliMain.java
new file mode 100644
index 00000000000..754a47fac43
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/app/BisqCliMain.java
@@ -0,0 +1,296 @@
+/*
+ * This file is part of Bisq.
+ *
+ * Bisq is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Bisq is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Bisq. If not, see .
+ */
+
+package bisq.cli.app;
+
+import bisq.core.grpc.GetBalanceGrpc;
+import bisq.core.grpc.GetBalanceRequest;
+import bisq.core.grpc.GetOffersGrpc;
+import bisq.core.grpc.GetOffersRequest;
+import bisq.core.grpc.GetPaymentAccountsGrpc;
+import bisq.core.grpc.GetPaymentAccountsRequest;
+import bisq.core.grpc.GetTradeStatisticsGrpc;
+import bisq.core.grpc.GetTradeStatisticsRequest;
+import bisq.core.grpc.GetVersionGrpc;
+import bisq.core.grpc.GetVersionRequest;
+import bisq.core.grpc.PlaceOfferGrpc;
+import bisq.core.grpc.PlaceOfferRequest;
+import bisq.core.grpc.StopServerGrpc;
+import bisq.core.grpc.StopServerRequest;
+import bisq.core.payment.PaymentAccount;
+import bisq.core.proto.network.CoreNetworkProtoResolver;
+import bisq.core.proto.persistable.CorePersistenceProtoResolver;
+
+import io.grpc.ManagedChannel;
+import io.grpc.ManagedChannelBuilder;
+import io.grpc.StatusRuntimeException;
+
+import org.bitcoinj.core.Coin;
+
+import java.time.Clock;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Scanner;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import lombok.extern.slf4j.Slf4j;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.lang.String.format;
+
+/**
+ * gRPC client.
+ *
+ * FIXME We get warning 'DEBUG io.grpc.netty.shaded.io.netty.util.internal.PlatformDependent0 - direct buffer constructor: unavailable
+ * java.lang.UnsupportedOperationException: Reflective setAccessible(true) disabled' which is
+ * related to Java 10 changes. Requests are working but we should find out why we get that warning
+ */
+@Slf4j
+public class BisqCliMain {
+
+ private final ManagedChannel channel;
+ private final GetVersionGrpc.GetVersionBlockingStub getVersionStub;
+ private final GetBalanceGrpc.GetBalanceBlockingStub getBalanceStub;
+ private final StopServerGrpc.StopServerBlockingStub stopServerStub;
+ private final GetTradeStatisticsGrpc.GetTradeStatisticsBlockingStub getTradeStatisticsStub;
+ private final GetOffersGrpc.GetOffersBlockingStub getOffersStub;
+ private final GetPaymentAccountsGrpc.GetPaymentAccountsBlockingStub getPaymentAccountsStub;
+ private final PlaceOfferGrpc.PlaceOfferBlockingStub placeOfferBlockingStub;
+ private final CorePersistenceProtoResolver corePersistenceProtoResolver;
+
+ public static void main(String[] args) throws Exception {
+ new BisqCliMain("localhost", 8888);
+ }
+
+ private BisqCliMain(String host, int port) {
+ this(ManagedChannelBuilder.forAddress(host, port)
+ // Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
+ // needing certificates.
+ .usePlaintext(true).build());
+
+ // Simple input scanner
+ // TODO use some more sophisticated input processing with validation....
+ try (Scanner scanner = new Scanner(System.in);) {
+ while (true) {
+ long startTs = System.currentTimeMillis();
+
+ String[] tokens = scanner.nextLine().split(" ");
+ if (tokens.length == 0) {
+ return;
+ }
+ String command = tokens[0];
+ List params = new ArrayList<>();
+ if (tokens.length > 1) {
+ params.addAll(Arrays.asList(tokens));
+ params.remove(0);
+ }
+ String result = "";
+
+ switch (command) {
+ case "getVersion":
+ result = getVersion();
+ break;
+ case "getBalance":
+ result = Coin.valueOf(getBalance()).toFriendlyString();
+ break;
+ case "getTradeStatistics":
+ List tradeStatistics = getTradeStatistics().stream()
+ .map(bisq.core.trade.statistics.TradeStatistics2::fromProto)
+ .collect(Collectors.toList());
+
+ result = tradeStatistics.toString();
+ break;
+ case "getOffers":
+ List offers = getOffers().stream()
+ .map(bisq.core.offer.Offer::fromProto)
+ .collect(Collectors.toList());
+ result = offers.toString();
+ break;
+ case "getPaymentAccounts":
+ List paymentAccounts = getPaymentAccounts().stream()
+ .map(proto -> PaymentAccount.fromProto(proto, corePersistenceProtoResolver))
+ .collect(Collectors.toList());
+ result = paymentAccounts.toString();
+ break;
+ case "placeOffer":
+ // test input: placeOffer CNY BUY 750000000 true -0.2251 1000000 500000 0.15 5a972121-c30a-4b0e-b519-b17b63795d16
+ // payment accountId and currency need to be adopted
+
+ // We expect 9 params
+ // TODO add basic input validation
+ try {
+ checkArgument(params.size() == 9);
+ String currencyCode = params.get(0);
+ String directionAsString = params.get(1);
+ long priceAsLong = Long.parseLong(params.get(2));
+ boolean useMarketBasedPrice = Boolean.parseBoolean(params.get(3));
+ double marketPriceMargin = Double.parseDouble(params.get(4));
+ long amountAsLong = Long.parseLong(params.get(5));
+ long minAmountAsLong = Long.parseLong(params.get(6));
+ double buyerSecurityDeposit = Double.parseDouble(params.get(7));
+ String paymentAccountId = params.get(8);
+ boolean success = placeOffer(currencyCode,
+ directionAsString,
+ priceAsLong,
+ useMarketBasedPrice,
+ marketPriceMargin,
+ amountAsLong,
+ minAmountAsLong,
+ buyerSecurityDeposit,
+ paymentAccountId);
+ result = String.valueOf(success);
+ break;
+ } catch (Throwable t) {
+ log.error(t.toString(), t);
+ break;
+ }
+ case "stop":
+ result = "Shut down client";
+ try {
+ shutdown();
+ } catch (InterruptedException e) {
+ log.error(e.toString(), e);
+ }
+ break;
+ case "stopServer":
+ stopServer();
+ result = "Server stopped";
+ break;
+ default:
+ result = format("Unknown command '%s'", command);
+ }
+
+ // First response is rather slow (300 ms) but following responses are fast (3-5 ms).
+ log.info("Request took: {} ms", System.currentTimeMillis() - startTs);
+ log.info(result);
+ }
+ }
+ }
+
+ /**
+ * Construct client for accessing server using the existing channel.
+ */
+ private BisqCliMain(ManagedChannel channel) {
+ this.channel = channel;
+
+ getVersionStub = GetVersionGrpc.newBlockingStub(channel);
+ getBalanceStub = GetBalanceGrpc.newBlockingStub(channel);
+ getTradeStatisticsStub = GetTradeStatisticsGrpc.newBlockingStub(channel);
+ getOffersStub = GetOffersGrpc.newBlockingStub(channel);
+ getPaymentAccountsStub = GetPaymentAccountsGrpc.newBlockingStub(channel);
+ placeOfferBlockingStub = PlaceOfferGrpc.newBlockingStub(channel);
+ stopServerStub = StopServerGrpc.newBlockingStub(channel);
+
+ CoreNetworkProtoResolver coreNetworkProtoResolver = new CoreNetworkProtoResolver(Clock.systemDefaultZone());
+ //TODO
+ corePersistenceProtoResolver = new CorePersistenceProtoResolver(null, coreNetworkProtoResolver, null, null);
+ }
+
+ private String getVersion() {
+ GetVersionRequest request = GetVersionRequest.newBuilder().build();
+ try {
+ return getVersionStub.getVersion(request).getVersion();
+ } catch (StatusRuntimeException e) {
+ return "RPC failed: " + e.getStatus();
+ }
+ }
+
+ private long getBalance() {
+ GetBalanceRequest request = GetBalanceRequest.newBuilder().build();
+ try {
+ return getBalanceStub.getBalance(request).getBalance();
+ } catch (StatusRuntimeException e) {
+ log.warn("RPC failed: {}", e.getStatus());
+ return -1;
+ }
+ }
+
+ private List getTradeStatistics() {
+ GetTradeStatisticsRequest request = GetTradeStatisticsRequest.newBuilder().build();
+ try {
+ return getTradeStatisticsStub.getTradeStatistics(request).getTradeStatisticsList();
+ } catch (StatusRuntimeException e) {
+ log.warn("RPC failed: {}", e.getStatus());
+ return null;
+ }
+ }
+
+ private List getOffers() {
+ GetOffersRequest request = GetOffersRequest.newBuilder().build();
+ try {
+ return getOffersStub.getOffers(request).getOffersList();
+ } catch (StatusRuntimeException e) {
+ log.warn("RPC failed: {}", e.getStatus());
+ return null;
+ }
+ }
+
+ private List getPaymentAccounts() {
+ GetPaymentAccountsRequest request = GetPaymentAccountsRequest.newBuilder().build();
+ try {
+ return getPaymentAccountsStub.getPaymentAccounts(request).getPaymentAccountsList();
+ } catch (StatusRuntimeException e) {
+ log.warn("RPC failed: {}", e.getStatus());
+ return null;
+ }
+ }
+
+ private boolean placeOffer(String currencyCode,
+ String directionAsString,
+ long priceAsLong,
+ boolean useMarketBasedPrice,
+ double marketPriceMargin,
+ long amountAsLong,
+ long minAmountAsLong,
+ double buyerSecurityDeposit,
+ String paymentAccountId) {
+ PlaceOfferRequest request = PlaceOfferRequest.newBuilder()
+ .setCurrencyCode(currencyCode)
+ .setDirection(directionAsString)
+ .setPrice(priceAsLong)
+ .setUseMarketBasedPrice(useMarketBasedPrice)
+ .setMarketPriceMargin(marketPriceMargin)
+ .setAmount(amountAsLong)
+ .setMinAmount(minAmountAsLong)
+ .setBuyerSecurityDeposit(buyerSecurityDeposit)
+ .setPaymentAccountId(paymentAccountId)
+ .build();
+ try {
+ return placeOfferBlockingStub.placeOffer(request).getResult();
+ } catch (StatusRuntimeException e) {
+ log.warn("RPC failed: {}", e.getStatus());
+ return false;
+ }
+ }
+
+ private void stopServer() {
+ StopServerRequest request = StopServerRequest.newBuilder().build();
+ try {
+ stopServerStub.stopServer(request);
+ } catch (StatusRuntimeException e) {
+ log.warn("RPC failed: {}", e.getStatus());
+ }
+ }
+
+ private void shutdown() throws InterruptedException {
+ channel.shutdown().awaitTermination(1, TimeUnit.SECONDS);
+ System.exit(0);
+ }
+}
diff --git a/core/src/main/java/bisq/core/app/AppOptionKeys.java b/core/src/main/java/bisq/core/app/AppOptionKeys.java
index 6c44f7d4f81..b18679cbf0b 100644
--- a/core/src/main/java/bisq/core/app/AppOptionKeys.java
+++ b/core/src/main/java/bisq/core/app/AppOptionKeys.java
@@ -18,8 +18,6 @@
package bisq.core.app;
public class AppOptionKeys {
- public static final String DESKTOP_WITH_HTTP_API = "desktopWithHttpApi";
- public static final String DESKTOP_WITH_GRPC_API = "desktopWithGrpcApi";
public static final String APP_NAME_KEY = "appName";
public static final String USER_DATA_DIR_KEY = "userDataDir";
public static final String APP_DATA_DIR_KEY = "appDataDir";
diff --git a/core/src/main/java/bisq/core/app/BisqEnvironment.java b/core/src/main/java/bisq/core/app/BisqEnvironment.java
index ddebb407ae1..de8decf27dd 100644
--- a/core/src/main/java/bisq/core/app/BisqEnvironment.java
+++ b/core/src/main/java/bisq/core/app/BisqEnvironment.java
@@ -186,8 +186,6 @@ public static boolean isDaoActivated(Environment environment) {
@Setter
protected boolean isBitcoinLocalhostNodeRunning;
@Getter
- protected String desktopWithHttpApi, desktopWithGrpcApi;
- @Getter
protected List bannedSeedNodes, bannedBtcNodes, bannedPriceRelayNodes;
protected final String btcNodes, seedNodes, ignoreDevMsg, useDevPrivilegeKeys, useDevMode, useTorForBtc, rpcUser, rpcPassword,
@@ -219,8 +217,6 @@ public BisqEnvironment(PropertySource commandLineProperties) {
appDataDir = getProperty(commandLineProperties, AppOptionKeys.APP_DATA_DIR_KEY, appDataDir(userDataDir, appName));
staticAppDataDir = appDataDir;
- desktopWithHttpApi = getProperty(commandLineProperties, AppOptionKeys.DESKTOP_WITH_HTTP_API, "false");
- desktopWithGrpcApi = getProperty(commandLineProperties, AppOptionKeys.DESKTOP_WITH_GRPC_API, "false");
ignoreDevMsg = getProperty(commandLineProperties, AppOptionKeys.IGNORE_DEV_MSG_KEY, "");
useDevPrivilegeKeys = getProperty(commandLineProperties, AppOptionKeys.USE_DEV_PRIVILEGE_KEYS, "");
referralId = getProperty(commandLineProperties, AppOptionKeys.REFERRAL_ID, "");
@@ -398,8 +394,6 @@ private PropertySource> defaultProperties() {
setProperty(NetworkOptionKeys.SEND_MSG_THROTTLE_SLEEP, sendMsgThrottleSleep);
setProperty(AppOptionKeys.APP_DATA_DIR_KEY, appDataDir);
- setProperty(AppOptionKeys.DESKTOP_WITH_HTTP_API, desktopWithHttpApi);
- setProperty(AppOptionKeys.DESKTOP_WITH_GRPC_API, desktopWithGrpcApi);
setProperty(AppOptionKeys.IGNORE_DEV_MSG_KEY, ignoreDevMsg);
setProperty(AppOptionKeys.USE_DEV_PRIVILEGE_KEYS, useDevPrivilegeKeys);
setProperty(AppOptionKeys.REFERRAL_ID, referralId);
diff --git a/core/src/main/java/bisq/core/app/BisqExecutable.java b/core/src/main/java/bisq/core/app/BisqExecutable.java
index 6a8be7955e4..8dd905bc06e 100644
--- a/core/src/main/java/bisq/core/app/BisqExecutable.java
+++ b/core/src/main/java/bisq/core/app/BisqExecutable.java
@@ -446,16 +446,6 @@ protected void customizeOptionParsing(OptionParser parser) {
.withRequiredArg()
.ofType(boolean.class);
- parser.accepts(AppOptionKeys.DESKTOP_WITH_HTTP_API,
- format("If set to true Bisq Desktop starts with Http API (default: %s)", "false"))
- .withRequiredArg()
- .ofType(boolean.class);
-
- parser.accepts(AppOptionKeys.DESKTOP_WITH_GRPC_API,
- format("If set to true Bisq Desktop starts with gRPC API (default: %s)", "false"))
- .withRequiredArg()
- .ofType(boolean.class);
-
parser.accepts(AppOptionKeys.USE_DEV_PRIVILEGE_KEYS,
format("If that is true all the privileged features which requires a private key " +
"to enable it are overridden by a dev key pair (This is for developers only!) (default: %s)", "false"))
diff --git a/core/src/main/java/bisq/core/app/BisqHeadlessAppMain.java b/core/src/main/java/bisq/core/app/BisqHeadlessAppMain.java
index 09e9f451088..e3fe211572f 100644
--- a/core/src/main/java/bisq/core/app/BisqHeadlessAppMain.java
+++ b/core/src/main/java/bisq/core/app/BisqHeadlessAppMain.java
@@ -17,8 +17,6 @@
package bisq.core.app;
-import bisq.core.CoreModule;
-
import bisq.common.UserThread;
import bisq.common.app.AppModule;
import bisq.common.app.Version;
diff --git a/core/src/main/java/bisq/core/CoreModule.java b/core/src/main/java/bisq/core/app/CoreModule.java
similarity index 98%
rename from core/src/main/java/bisq/core/CoreModule.java
rename to core/src/main/java/bisq/core/app/CoreModule.java
index 4d921e6501e..431dc1555e3 100644
--- a/core/src/main/java/bisq/core/CoreModule.java
+++ b/core/src/main/java/bisq/core/app/CoreModule.java
@@ -15,11 +15,9 @@
* along with Bisq. If not, see .
*/
-package bisq.core;
+package bisq.core.app;
import bisq.core.alert.AlertModule;
-import bisq.core.app.AppOptionKeys;
-import bisq.core.app.BisqEnvironment;
import bisq.core.btc.BitcoinModule;
import bisq.core.dao.DaoModule;
import bisq.core.filter.FilterModule;
diff --git a/core/src/main/java/bisq/core/grpc/BisqGrpcServer.java b/core/src/main/java/bisq/core/grpc/BisqGrpcServer.java
new file mode 100644
index 00000000000..55db5011175
--- /dev/null
+++ b/core/src/main/java/bisq/core/grpc/BisqGrpcServer.java
@@ -0,0 +1,203 @@
+/*
+ * This file is part of Bisq.
+ *
+ * Bisq is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Bisq is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Bisq. If not, see .
+ */
+
+package bisq.core.grpc;
+
+import bisq.core.offer.Offer;
+import bisq.core.payment.PaymentAccount;
+import bisq.core.trade.handlers.TransactionResultHandler;
+import bisq.core.trade.statistics.TradeStatistics2;
+
+import io.grpc.Server;
+import io.grpc.ServerBuilder;
+import io.grpc.stub.StreamObserver;
+
+import java.io.IOException;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import lombok.extern.slf4j.Slf4j;
+
+
+/**
+ * gRPC server. Gets a instance of BisqFacade passed to access data from the running Bisq instance.
+ */
+@Slf4j
+public class BisqGrpcServer {
+
+ private Server server;
+
+ private static BisqGrpcServer instance;
+ private static CoreApi coreApi;
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Services
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ static class GetVersionImpl extends GetVersionGrpc.GetVersionImplBase {
+ @Override
+ public void getVersion(GetVersionRequest req, StreamObserver responseObserver) {
+ GetVersionReply reply = GetVersionReply.newBuilder().setVersion(coreApi.getVersion()).build();
+ responseObserver.onNext(reply);
+ responseObserver.onCompleted();
+ }
+ }
+
+ static class GetBalanceImpl extends GetBalanceGrpc.GetBalanceImplBase {
+ @Override
+ public void getBalance(GetBalanceRequest req, StreamObserver responseObserver) {
+ GetBalanceReply reply = GetBalanceReply.newBuilder().setBalance(coreApi.getAvailableBalance()).build();
+ responseObserver.onNext(reply);
+ responseObserver.onCompleted();
+ }
+ }
+
+ static class GetTradeStatisticsImpl extends GetTradeStatisticsGrpc.GetTradeStatisticsImplBase {
+ @Override
+ public void getTradeStatistics(GetTradeStatisticsRequest req,
+ StreamObserver responseObserver) {
+ List tradeStatistics = coreApi.getTradeStatistics().stream()
+ .map(TradeStatistics2::toProtoTradeStatistics2)
+ .collect(Collectors.toList());
+ GetTradeStatisticsReply reply = GetTradeStatisticsReply.newBuilder().addAllTradeStatistics(tradeStatistics).build();
+ responseObserver.onNext(reply);
+ responseObserver.onCompleted();
+ }
+ }
+
+ static class GetOffersImpl extends GetOffersGrpc.GetOffersImplBase {
+ @Override
+ public void getOffers(GetOffersRequest req, StreamObserver responseObserver) {
+
+ List tradeStatistics = coreApi.getOffers().stream()
+ .map(Offer::toProtoMessage)
+ .collect(Collectors.toList());
+
+ GetOffersReply reply = GetOffersReply.newBuilder().addAllOffers(tradeStatistics).build();
+ responseObserver.onNext(reply);
+ responseObserver.onCompleted();
+ }
+ }
+
+ static class GetPaymentAccountsImpl extends GetPaymentAccountsGrpc.GetPaymentAccountsImplBase {
+ @Override
+ public void getPaymentAccounts(GetPaymentAccountsRequest req,
+ StreamObserver responseObserver) {
+
+ List tradeStatistics = coreApi.getPaymentAccounts().stream()
+ .map(PaymentAccount::toProtoMessage)
+ .collect(Collectors.toList());
+
+ GetPaymentAccountsReply reply = GetPaymentAccountsReply.newBuilder().addAllPaymentAccounts(tradeStatistics).build();
+ responseObserver.onNext(reply);
+ responseObserver.onCompleted();
+ }
+ }
+
+ static class PlaceOfferImpl extends PlaceOfferGrpc.PlaceOfferImplBase {
+ @Override
+ public void placeOffer(PlaceOfferRequest req, StreamObserver responseObserver) {
+ TransactionResultHandler resultHandler = transaction -> {
+ PlaceOfferReply reply = PlaceOfferReply.newBuilder().setResult(true).build();
+ responseObserver.onNext(reply);
+ responseObserver.onCompleted();
+ };
+ coreApi.placeOffer(
+ req.getCurrencyCode(),
+ req.getDirection(),
+ req.getPrice(),
+ req.getUseMarketBasedPrice(),
+ req.getMarketPriceMargin(),
+ req.getAmount(),
+ req.getMinAmount(),
+ req.getBuyerSecurityDeposit(),
+ req.getPaymentAccountId(),
+ resultHandler);
+ }
+ }
+
+ static class StopServerImpl extends StopServerGrpc.StopServerImplBase {
+ @Override
+ public void stopServer(StopServerRequest req, StreamObserver responseObserver) {
+ StopServerReply reply = StopServerReply.newBuilder().build();
+ responseObserver.onNext(reply);
+ responseObserver.onCompleted();
+
+ instance.stop();
+ }
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Constructor
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ public BisqGrpcServer(CoreApi coreApi) {
+ instance = this;
+
+ BisqGrpcServer.coreApi = coreApi;
+
+ try {
+ start();
+
+ } catch (IOException e) {
+ log.error(e.toString(), e);
+ }
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // API
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ public void stop() {
+ if (server != null) {
+ server.shutdown();
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Private
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ private void start() throws IOException {
+ // TODO add to options
+ int port = 8888;
+
+ // Config services
+ server = ServerBuilder.forPort(port)
+ .addService(new GetVersionImpl())
+ .addService(new GetBalanceImpl())
+ .addService(new GetTradeStatisticsImpl())
+ .addService(new GetOffersImpl())
+ .addService(new GetPaymentAccountsImpl())
+ .addService(new PlaceOfferImpl())
+ .addService(new StopServerImpl())
+ .build()
+ .start();
+
+ log.info("Server started, listening on " + port);
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ // Use stderr here since the logger may have been reset by its JVM shutdown hook.
+ log.error("*** shutting down gRPC server since JVM is shutting down");
+ BisqGrpcServer.this.stop();
+ log.error("*** server shut down");
+ }));
+ }
+}
diff --git a/core/src/main/java/bisq/core/grpc/CoreApi.java b/core/src/main/java/bisq/core/grpc/CoreApi.java
new file mode 100644
index 00000000000..2877849147f
--- /dev/null
+++ b/core/src/main/java/bisq/core/grpc/CoreApi.java
@@ -0,0 +1,163 @@
+/*
+ * This file is part of Bisq.
+ *
+ * Bisq is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Bisq is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Bisq. If not, see .
+ */
+
+package bisq.core.grpc;
+
+import bisq.core.btc.Balances;
+import bisq.core.monetary.Price;
+import bisq.core.offer.CreateOfferService;
+import bisq.core.offer.Offer;
+import bisq.core.offer.OfferBookService;
+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;
+import bisq.core.user.User;
+
+import bisq.common.app.Version;
+
+import org.bitcoinj.core.Coin;
+
+import javax.inject.Inject;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 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 BalancePresentation balancePresentation;
+ private final OfferBookService offerBookService;
+ private final TradeStatisticsManager tradeStatisticsManager;
+ private final CreateOfferService createOfferService;
+ private final OpenOfferManager openOfferManager;
+ private final User user;
+
+ @Inject
+ public CoreApi(Balances balances,
+ BalancePresentation balancePresentation,
+ 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;
+ this.openOfferManager = openOfferManager;
+ this.user = user;
+ }
+
+ 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());
+ }
+
+ public List getOffers() {
+ return offerBookService.getOffers();
+ }
+
+ public Set getPaymentAccounts() {
+ return user.getPaymentAccounts();
+ }
+
+ public void placeOffer(String currencyCode,
+ String directionAsString,
+ long priceAsLong,
+ boolean useMarketBasedPrice,
+ double marketPriceMargin,
+ long amountAsLong,
+ long minAmountAsLong,
+ double buyerSecurityDeposit,
+ String paymentAccountId,
+ TransactionResultHandler resultHandler) {
+ String offerId = createOfferService.getRandomOfferId();
+ OfferPayload.Direction direction = OfferPayload.Direction.valueOf(directionAsString);
+ Price price = Price.valueOf(currencyCode, priceAsLong);
+ Coin amount = Coin.valueOf(amountAsLong);
+ Coin minAmount = Coin.valueOf(minAmountAsLong);
+ PaymentAccount paymentAccount = user.getPaymentAccount(paymentAccountId);
+ // We don't support atm funding from external wallet to keep it simple
+ boolean useSavingsWallet = true;
+
+ placeOffer(offerId,
+ currencyCode,
+ direction,
+ price,
+ useMarketBasedPrice,
+ marketPriceMargin,
+ amount,
+ minAmount,
+ buyerSecurityDeposit,
+ paymentAccount,
+ useSavingsWallet,
+ resultHandler);
+ }
+
+ public void placeOffer(String offerId,
+ String currencyCode,
+ OfferPayload.Direction direction,
+ Price price,
+ boolean useMarketBasedPrice,
+ double marketPriceMargin,
+ Coin amount,
+ Coin minAmount,
+ double buyerSecurityDeposit,
+ PaymentAccount paymentAccount,
+ boolean useSavingsWallet,
+ TransactionResultHandler resultHandler) {
+ Offer offer = createOfferService.createAndGetOffer(offerId,
+ direction,
+ currencyCode,
+ amount,
+ minAmount,
+ price,
+ useMarketBasedPrice,
+ marketPriceMargin,
+ buyerSecurityDeposit,
+ paymentAccount);
+
+ openOfferManager.placeOffer(offer,
+ buyerSecurityDeposit,
+ useSavingsWallet,
+ resultHandler,
+ log::error);
+ }
+}
diff --git a/core/src/main/proto/grpc.proto b/core/src/main/proto/grpc.proto
new file mode 100644
index 00000000000..33957fb4124
--- /dev/null
+++ b/core/src/main/proto/grpc.proto
@@ -0,0 +1,148 @@
+/*
+ * This file is part of Bisq.
+ *
+ * Bisq is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Bisq is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Bisq. If not, see .
+ */
+
+syntax = "proto3";
+package io.bisq.protobuffer;
+
+// FIXME: IntelliJ does not recognize the import but the compiler does
+import "pb.proto";
+
+option java_package = "bisq.core.grpc";
+option java_multiple_files = true;
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// Version
+///////////////////////////////////////////////////////////////////////////////////////////
+
+service GetVersion {
+ rpc GetVersion (GetVersionRequest) returns (GetVersionReply) {
+ }
+}
+
+message GetVersionRequest {
+}
+
+message GetVersionReply {
+ string version = 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// Balance
+///////////////////////////////////////////////////////////////////////////////////////////
+
+service GetBalance {
+ rpc GetBalance (GetBalanceRequest) returns (GetBalanceReply) {
+ }
+}
+
+message GetBalanceRequest {
+}
+
+message GetBalanceReply {
+ uint64 balance = 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// TradeStatistics
+///////////////////////////////////////////////////////////////////////////////////////////
+
+service GetTradeStatistics {
+ rpc GetTradeStatistics (GetTradeStatisticsRequest) returns (GetTradeStatisticsReply) {
+ }
+}
+
+message GetTradeStatisticsRequest {
+}
+
+// FIXME: IntelliJ does not recognize the imported TradeStatistics2 but the compiler does
+message GetTradeStatisticsReply {
+ repeated TradeStatistics2 TradeStatistics = 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// Offer
+///////////////////////////////////////////////////////////////////////////////////////////
+
+service GetOffers {
+ rpc GetOffers (GetOffersRequest) returns (GetOffersReply) {
+ }
+}
+
+message GetOffersRequest {
+}
+
+// FIXME: IntelliJ does not recognize the imported Offer but the compiler does
+message GetOffersReply {
+ repeated Offer offers = 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// PaymentAccount
+///////////////////////////////////////////////////////////////////////////////////////////
+
+service GetPaymentAccounts {
+ rpc GetPaymentAccounts (GetPaymentAccountsRequest) returns (GetPaymentAccountsReply) {
+ }
+}
+
+message GetPaymentAccountsRequest {
+}
+
+// FIXME: IntelliJ does not recognize the imported PaymentAccount but the compiler does
+message GetPaymentAccountsReply {
+ repeated PaymentAccount paymentAccounts = 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// PlaceOffer
+///////////////////////////////////////////////////////////////////////////////////////////
+
+service PlaceOffer {
+ rpc PlaceOffer (PlaceOfferRequest) returns (PlaceOfferReply) {
+ }
+}
+
+message PlaceOfferRequest {
+ string currencyCode = 1;
+ string direction = 2;
+ uint64 price = 3;
+ bool useMarketBasedPrice = 4;
+ double marketPriceMargin = 5;
+ uint64 amount = 6;
+ uint64 minAmount = 7;
+ double buyerSecurityDeposit = 8;
+ string paymentAccountId = 9;
+}
+
+message PlaceOfferReply {
+ bool result = 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// StopServer
+///////////////////////////////////////////////////////////////////////////////////////////
+
+service StopServer {
+ rpc StopServer (StopServerRequest) returns (StopServerReply) {
+ }
+}
+
+message StopServerRequest {
+}
+
+message StopServerReply {
+}
diff --git a/daemon/src/main/java/bisq/daemon/app/BisqDaemon.java b/daemon/src/main/java/bisq/daemon/app/BisqDaemon.java
new file mode 100644
index 00000000000..f813f84d3ed
--- /dev/null
+++ b/daemon/src/main/java/bisq/daemon/app/BisqDaemon.java
@@ -0,0 +1,23 @@
+/*
+ * This file is part of Bisq.
+ *
+ * Bisq is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Bisq is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Bisq. If not, see .
+ */
+
+package bisq.daemon.app;
+
+import bisq.core.app.BisqHeadlessApp;
+
+public class BisqDaemon extends BisqHeadlessApp {
+}
diff --git a/daemon/src/main/java/bisq/daemon/app/BisqDaemonMain.java b/daemon/src/main/java/bisq/daemon/app/BisqDaemonMain.java
new file mode 100644
index 00000000000..f25d1be3b14
--- /dev/null
+++ b/daemon/src/main/java/bisq/daemon/app/BisqDaemonMain.java
@@ -0,0 +1,111 @@
+/*
+ * This file is part of Bisq.
+ *
+ * Bisq is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Bisq is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Bisq. If not, see .
+ */
+
+package bisq.daemon.app;
+
+import bisq.core.app.BisqExecutable;
+import bisq.core.app.BisqHeadlessAppMain;
+import bisq.core.app.BisqSetup;
+import bisq.core.app.CoreModule;
+import bisq.core.grpc.BisqGrpcServer;
+import bisq.core.grpc.CoreApi;
+
+import bisq.common.UserThread;
+import bisq.common.app.AppModule;
+import bisq.common.setup.CommonSetup;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class BisqDaemonMain extends BisqHeadlessAppMain implements BisqSetup.BisqSetupListener {
+
+ public static void main(String[] args) throws Exception {
+ if (BisqExecutable.setupInitialOptionParser(args)) {
+ // For some reason the JavaFX launch process results in us losing the thread context class loader: reset it.
+ // In order to work around a bug in JavaFX 8u25 and below, you must include the following code as the first line of your realMain method:
+ Thread.currentThread().setContextClassLoader(BisqDaemonMain.class.getClassLoader());
+
+ new BisqDaemonMain().execute(args);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // First synchronous execution tasks
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ protected void configUserThread() {
+ final ThreadFactory threadFactory = new ThreadFactoryBuilder()
+ .setNameFormat(this.getClass().getSimpleName())
+ .setDaemon(true)
+ .build();
+ UserThread.setExecutor(Executors.newSingleThreadExecutor(threadFactory));
+ }
+
+ @Override
+ protected void launchApplication() {
+ headlessApp = new BisqDaemon();
+ CommonSetup.setup(BisqDaemonMain.this.headlessApp);
+
+ UserThread.execute(this::onApplicationLaunched);
+ }
+
+ @Override
+ protected void onApplicationLaunched() {
+ super.onApplicationLaunched();
+ headlessApp.setGracefulShutDownHandler(this);
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // We continue with a series of synchronous execution tasks
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ protected AppModule getModule() {
+ return new CoreModule(bisqEnvironment);
+ }
+
+ @Override
+ protected void applyInjector() {
+ super.applyInjector();
+
+ headlessApp.setInjector(injector);
+ }
+
+ @Override
+ protected void startApplication() {
+ // We need to be in user thread! We mapped at launchApplication already...
+ headlessApp.startApplication();
+
+ // In headless mode we don't have an async behaviour so we trigger the setup by calling onApplicationStarted
+ onApplicationStarted();
+ }
+
+ @Override
+ protected void onApplicationStarted() {
+ super.onApplicationStarted();
+
+ CoreApi coreApi = injector.getInstance(CoreApi.class);
+ new BisqGrpcServer(coreApi);
+ }
+}
diff --git a/daemon/src/main/java/resources/logback.xml b/daemon/src/main/java/resources/logback.xml
new file mode 100644
index 00000000000..ac5e6444ea0
--- /dev/null
+++ b/daemon/src/main/java/resources/logback.xml
@@ -0,0 +1,16 @@
+
+
+
+
+ %highlight(%d{MMM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{30}: %msg %xEx%n)
+
+
+
+
+
+
+
+
+
+
+
diff --git a/desktop/src/main/java/bisq/desktop/app/BisqAppMain.java b/desktop/src/main/java/bisq/desktop/app/BisqAppMain.java
index a2494dfbdb1..7f63a3ef696 100644
--- a/desktop/src/main/java/bisq/desktop/app/BisqAppMain.java
+++ b/desktop/src/main/java/bisq/desktop/app/BisqAppMain.java
@@ -44,11 +44,6 @@ public BisqAppMain() {
super("Bisq Desktop", "bisq-desktop", Version.VERSION);
}
- /* @Nullable
- private BisqHttpApiServer bisqHttpApiServer;*/
- /* @Nullable
- private BisqGrpcServer bisqGrpcServer;
- */
public static void main(String[] args) throws Exception {
if (BisqExecutable.setupInitialOptionParser(args)) {
// For some reason the JavaFX launch process results in us losing the thread context class loader: reset it.
@@ -135,20 +130,11 @@ protected void startApplication() {
protected void onApplicationStarted() {
super.onApplicationStarted();
- /* if (runWithHttpApi()) {
- bisqHttpApiServer = new BisqHttpApiServer();
- }*/
/*
if (runWithGrpcApi()) {
- bisqGrpcServer = new BisqGrpcServer();
- }*/
- }
-
- private boolean runWithHttpApi() {
- return bisqEnvironment.getDesktopWithHttpApi().toLowerCase().equals("true");
- }
-
- private boolean runWithGrpcApi() {
- return bisqEnvironment.getDesktopWithGrpcApi().toLowerCase().equals("true");
+ CoreApi coreApi = injector.getInstance(CoreApi.class);
+ bisqGrpcServer = new BisqGrpcServer(coreApi);
+ }
+ */
}
}
diff --git a/desktop/src/main/java/bisq/desktop/app/BisqAppModule.java b/desktop/src/main/java/bisq/desktop/app/BisqAppModule.java
index 75c389288e3..942c49a718b 100644
--- a/desktop/src/main/java/bisq/desktop/app/BisqAppModule.java
+++ b/desktop/src/main/java/bisq/desktop/app/BisqAppModule.java
@@ -19,7 +19,7 @@
import bisq.desktop.DesktopModule;
-import bisq.core.CoreModule;
+import bisq.core.app.CoreModule;
import bisq.common.app.AppModule;
diff --git a/gradle/witness/gradle-witness.gradle b/gradle/witness/gradle-witness.gradle
index 19d1f00cfb4..9be698d7e9e 100644
--- a/gradle/witness/gradle-witness.gradle
+++ b/gradle/witness/gradle-witness.gradle
@@ -11,7 +11,6 @@
//
// See https://github.com/signalapp/gradle-witness#using-witness for further details.
-
dependencyVerification {
verify = [
'aopalliance:aopalliance:0addec670fedcd3f113c5c8091d783280d23f75e3acb841b61a9cdb079376a08',
@@ -32,11 +31,14 @@ dependencyVerification {
'com.github.bisq-network.bitcoinj:bitcoinj-core:f979c2187e61ee3b08dd4cbfc49a149734cff64c045d29ed112f2e12f34068a3',
'com.github.ravn:jsocks:3c71600af027b2b6d4244e4ad14d98ff2352a379410daebefff5d8cd48d742a4',
'com.github.sarxos:webcam-capture:d960b7ea8ec3ddf2df0725ef214c3fccc9699ea7772df37f544e1f8e4fd665f6',
+ 'com.google.android:annotations:ba734e1e84c09d615af6a09d33034b4f0442f8772dec120efb376d86a565ae15',
+ 'com.google.api.grpc:proto-google-common-protos:bd60cd7a423b00fb824c27bdd0293aaf4781be1daba6ed256311103fb4b84108',
'com.google.code.findbugs:jsr305:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
- 'com.google.code.gson:gson:2d43eb5ea9e133d2ee2405cc14f5ee08951b8361302fdd93494a3a997b508d32',
+ 'com.google.code.gson:gson:233a0149fc365c9f6edbd683cfe266b19bdc773be98eabdaf6b3c924b48e7d81',
+ 'com.google.errorprone:error_prone_annotations:ec59f1b702d9afc09e8c3929f5c42777dec623a6ea2731ac694332c7d7680f5a',
'com.google.guava:guava:36a666e3b71ae7f0f0dca23654b67e086e6c93d192f60ba5dfd5519db6c288c8',
'com.google.inject:guice:d258ff1bd9b8b527872f8402648226658ad3149f1f40e74b0566d69e7e042fbc',
- 'com.google.protobuf:protobuf-java:5a1e5c225791eccd3d67a598922e637406190c90155fb97f38e4eab29719324d',
+ 'com.google.protobuf:protobuf-java:161d7d61a8cb3970891c299578702fd079646e032329d6c2cabf998d191437c9',
'com.google.zxing:core:11aae8fd974ab25faa8208be50468eb12349cd239e93e7c797377fa13e381729',
'com.google.zxing:javase:0ec23e2ec12664ddd6347c8920ad647bb3b9da290f897a88516014b56cc77eb9',
'com.googlecode.jcsv:jcsv:73ca7d715e90c8d2c2635cc284543b038245a34f70790660ed590e157b8714a2',
@@ -53,6 +55,16 @@ dependencyVerification {
'de.jensd:fontawesomefx-materialdesignfont:dbad8dfdd1c85e298d5bbae25b2399aec9e85064db57b2427d10f3815aa98752',
'de.jensd:fontawesomefx:73bacc991a0a6f5cf0f911767c8db161e0949dbca61e8371eb4342e3da96887b',
'io.github.microutils:kotlin-logging:4992504fd3c6ecdf9ed10874b9508e758bb908af9e9d7af19a61e9afb6b7e27a',
+ 'io.grpc:grpc-api:a269094009588213ab5386a6fb92426b8056a130b2653d3b4e59e971f2f1ef08',
+ 'io.grpc:grpc-context:f4c8f878c320f6fb56c1c14692618f6df8253314b556176e32727afbc5921a73',
+ 'io.grpc:grpc-core:d67fa113fd9cc45a02710f9c41dda9c15191448c14e9e96fcc21839a41345d4c',
+ 'io.grpc:grpc-netty-shaded:9edfd45da473d2efbb5683fc3eaf1857e82d2148033d82dd558a7ac38731ea33',
+ 'io.grpc:grpc-protobuf-lite:9ba9aaa3e6997a04c707793c25e3ec88c6bad86f8d6f6b8b7a1a0c33ea2429d8',
+ 'io.grpc:grpc-protobuf:454dae7e246dac25526ed5b795d97a5dafedd3cc2042cfc810f02051d7d3e3cb',
+ 'io.grpc:grpc-stub:1532e291c0e9fd8230a6416c8ebbd902d99c7e2760241ae638ea761aa3dd5f43',
+ 'io.opencensus:opencensus-api:8e2cb0f6391d8eb0a1bcd01e7748883f0033b1941754f4ed3f19d2c3e4276fc8',
+ 'io.opencensus:opencensus-contrib-grpc-metrics:29fc79401082301542cab89d7054d2f0825f184492654c950020553ef4ff0ef8',
+ 'io.perfmark:perfmark-api:b734ba2149712409a44eabdb799f64768578fee0defe1418bb108fe32ea43e1a',
'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'net.glxn:qrgen:c85d9d8512d91e8ad11fe56259a7825bd50ce0245447e236cf168d1b17591882',
'net.jcip:jcip-annotations:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
diff --git a/settings.gradle b/settings.gradle
index 500b2803c47..b12eb486d4d 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -2,6 +2,8 @@ include 'assets'
include 'common'
include 'p2p'
include 'core'
+include 'cli'
+include 'daemon'
include 'desktop'
include 'monitor'
include 'pricenode'